From 542d9542008a932a6de27babd21c02c9cf7cd48c Mon Sep 17 00:00:00 2001 From: Felix Schaumann Date: Thu, 11 May 2023 08:30:16 +0200 Subject: [PATCH 001/238] Fix missing model monitoring in XGB train script --- .../tensorflow/prediction/pipeline.py | 16 +++++---- .../training/assets/train_tf_model.py | 13 ++++--- .../pipelines/tensorflow/training/pipeline.py | 1 + .../pipelines/xgboost/prediction/pipeline.py | 16 +++++---- .../training/assets/train_xgb_model.py | 35 ++++++++++++++----- .../pipelines/xgboost/training/pipeline.py | 1 + 6 files changed, 57 insertions(+), 25 deletions(-) diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index 5121aea3..b011b0c1 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -108,12 +108,16 @@ def tensorflow_pipeline( ).set_display_name("Ingest data") # lookup champion model - champion_model = lookup_model( - model_name=model_name, - project_location=project_location, - project_id=project_id, - fail_on_model_not_found=True, - ).set_display_name("Look up champion model") + champion_model = ( + lookup_model( + model_name=model_name, + project_location=project_location, + project_id=project_id, + fail_on_model_not_found=True, + ) + .set_display_name("Look up champion model") + .set_caching_options(False) + ) # batch predict from BigQuery to BigQuery bigquery_source_input_uri = f"bq://{project_id}.{dataset_id}.{ingested_table}" diff --git a/pipelines/src/pipelines/tensorflow/training/assets/train_tf_model.py b/pipelines/src/pipelines/tensorflow/training/assets/train_tf_model.py index c89a7880..5d164ae7 100644 --- a/pipelines/src/pipelines/tensorflow/training/assets/train_tf_model.py +++ b/pipelines/src/pipelines/tensorflow/training/assets/train_tf_model.py @@ -219,6 +219,9 @@ def _get_temp_dir(dirpath, task_id): parser.add_argument("--hparams", default={}, type=json.loads) args = parser.parse_args() +if args.model.startswith("gs://"): + args.model = Path("/gcs/" + args.model[5:]) + # merge dictionaries by overwriting default_model_params if provided in model_params hparams = {**DEFAULT_HPARAMS, **args.hparams} logging.info(f"Using model hyper-parameters: {hparams}") @@ -261,9 +264,9 @@ def _get_temp_dir(dirpath, task_id): logging.info("not chief node, exiting now") sys.exit() -os.makedirs(args.model, exist_ok=True) logging.info(f"Save model to: {args.model}") -tf_model.save(args.model, save_format="tf") +args.model.mkdir(parents=True) +tf_model.save(str(args.model), save_format="tf") logging.info(f"Save metrics to: {args.metrics}") eval_metrics = dict(zip(tf_model.metrics_names, tf_model.evaluate(test_ds))) @@ -281,11 +284,13 @@ def _get_temp_dir(dirpath, task_id): json.dump(metrics, fp) # Persist URIs of training file(s) for model monitoring in batch predictions -path = Path(args.model) / TRAINING_DATASET_INFO +# See https://cloud.google.com/python/docs/reference/aiplatform/latest/google.cloud.aiplatform_v1beta1.types.ModelMonitoringObjectiveConfig.TrainingDataset # noqa: E501 +# for the expected schema. +path = args.model / TRAINING_DATASET_INFO training_dataset_for_monitoring = { "gcsSource": {"uris": [args.train_data]}, "dataFormat": "csv", - "targetField": hparams["label"], + "targetField": label, } logging.info(f"Save training dataset info for model monitoring: {path}") logging.info(f"Training dataset: {training_dataset_for_monitoring}") diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index cb99e091..2767d210 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -215,6 +215,7 @@ def tensorflow_pipeline( fail_on_model_not_found=False, ) .set_display_name("Lookup past model") + .set_caching_options(False) .outputs["model_resource_name"] ) diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index 0bd1ec91..ed474eb8 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -102,12 +102,16 @@ def xgboost_pipeline( ).set_display_name("Ingest data") # lookup champion model - champion_model = lookup_model( - model_name=model_name, - project_location=project_location, - project_id=project_id, - fail_on_model_not_found=True, - ).set_display_name("Look up champion model") + champion_model = ( + lookup_model( + model_name=model_name, + project_location=project_location, + project_id=project_id, + fail_on_model_not_found=True, + ) + .set_display_name("Look up champion model") + .set_caching_options(False) + ) # batch predict from BigQuery to BigQuery bigquery_source_input_uri = f"bq://{project_id}.{dataset_id}.{ingested_table}" diff --git a/pipelines/src/pipelines/xgboost/training/assets/train_xgb_model.py b/pipelines/src/pipelines/xgboost/training/assets/train_xgb_model.py index 31d95247..71cc65b5 100644 --- a/pipelines/src/pipelines/xgboost/training/assets/train_xgb_model.py +++ b/pipelines/src/pipelines/xgboost/training/assets/train_xgb_model.py @@ -1,4 +1,6 @@ import argparse +from pathlib import Path + import joblib import json import os @@ -14,7 +16,9 @@ logging.basicConfig(level=logging.DEBUG) - +# used for monitoring during prediction time +TRAINING_DATASET_INFO = "training_dataset.json" +# numeric/categorical features in Chicago trips dataset to be preprocessed NUM_COLS = ["dayofweek", "hourofday", "trip_distance", "trip_miles", "trip_seconds"] ORD_COLS = ["company"] OHE_COLS = ["payment_type"] @@ -39,6 +43,9 @@ def indices_in_list(elements: list, base_list: list) -> list: parser.add_argument("--hparams", default={}, type=json.loads) args = parser.parse_args() +if args.model.startswith("gs://"): + args.model = Path("/gcs/" + args.model[5:]) + logging.info("Read csv files into dataframes") df_train = pd.read_csv(args.train_data) df_valid = pd.read_csv(args.valid_data) @@ -111,15 +118,25 @@ def indices_in_list(elements: list, base_list: list) -> list: "rootMeanSquaredLogError": np.sqrt(metrics.mean_squared_log_error(y_test, y_pred)), } -try: - model_path = args.model.replace("gs://", "/gcs/") - logging.info(f"Save model to: {model_path}") - os.makedirs(model_path, exist_ok=True) - joblib.dump(pipeline, model_path + "model.joblib") -except Exception as e: - print(e) - raise e +logging.info(f"Save model to: {args.model}") +args.model.mkdir(parents=True) +joblib.dump(pipeline, str(args.model / "model.joblib")) logging.info(f"Metrics: {metrics}") with open(args.metrics, "w") as fp: json.dump(metrics, fp) + +# Persist URIs of training file(s) for model monitoring in batch predictions +# See https://cloud.google.com/python/docs/reference/aiplatform/latest/google.cloud.aiplatform_v1beta1.types.ModelMonitoringObjectiveConfig.TrainingDataset # noqa: E501 +# for the expected schema. +path = args.model / TRAINING_DATASET_INFO +training_dataset_for_monitoring = { + "gcsSource": {"uris": [args.train_data]}, + "dataFormat": "csv", + "targetField": label, +} +logging.info(f"Training dataset info: {training_dataset_for_monitoring}") + +with open(path, "w") as fp: + logging.info(f"Save training dataset info for model monitoring: {path}") + json.dump(training_dataset_for_monitoring, fp) diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 1db2023c..27cb47ed 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -212,6 +212,7 @@ def xgboost_pipeline( fail_on_model_not_found=False, ) .set_display_name("Lookup past model") + .set_caching_options(False) .outputs["model_resource_name"] ) From 22f6dcd3c863764356e2e801f97c0bc05876deff Mon Sep 17 00:00:00 2001 From: ariadnafer Date: Fri, 12 May 2023 14:52:56 +0200 Subject: [PATCH 002/238] feat: enable multiquery, update queries and pipelines --- .../bigquery_components/bq_query_to_table.py | 20 +-------- .../tensorflow/prediction/pipeline.py | 30 +++++++------ .../tensorflow/prediction/queries/ingest.sql | 13 +++++- .../pipelines/tensorflow/training/pipeline.py | 45 +++++++------------ .../tensorflow/training/queries/ingest.sql | 29 ++++++++---- .../tensorflow/training/queries/sample.sql | 24 +++------- .../pipelines/xgboost/prediction/pipeline.py | 30 ++++++++----- .../xgboost/prediction/queries/ingest.sql | 9 ++++ .../pipelines/xgboost/training/pipeline.py | 44 +++++++----------- .../xgboost/training/queries/ingest.sql | 27 +++++++---- .../xgboost/training/queries/sample.sql | 12 +++-- 11 files changed, 145 insertions(+), 138 deletions(-) diff --git a/components/bigquery-components/src/bigquery_components/bq_query_to_table.py b/components/bigquery-components/src/bigquery_components/bq_query_to_table.py index 9530bd92..4b6afc29 100644 --- a/components/bigquery-components/src/bigquery_components/bq_query_to_table.py +++ b/components/bigquery-components/src/bigquery_components/bq_query_to_table.py @@ -22,25 +22,15 @@ def bq_query_to_table( query: str, bq_client_project_id: str, - destination_project_id: str, - dataset_id: str = None, - table_id: str = None, dataset_location: str = "EU", - query_job_config: dict = None, ) -> None: """ Run query & create a new BigQuery table Args: query (str): SQL query to execute, results are saved in a BigQuery table bq_client_project_id (str): project id that will be used by the bq client - destination_project_id (str): project id where BQ table will be created - dataset_id (str): dataset id where BQ table will be created - table_id (str): table name (without project id and dataset id) dataset_location (str): bq dataset location - query_job_config (dict): dict containing optional parameters required by the bq query operation. No need to specify destination param - See available parameters here - https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.job.QueryJobConfig.html Returns: None """ @@ -50,13 +40,7 @@ def bq_query_to_table( logging.getLogger().setLevel(logging.INFO) - if (dataset_id is not None) and (table_id is not None): - dest_table_ref = f"{destination_project_id}.{dataset_id}.{table_id}" - else: - dest_table_ref = None - if query_job_config is None: - query_job_config = {} - job_config = bigquery.QueryJobConfig(destination=dest_table_ref, **query_job_config) + job_config = bigquery.QueryJobConfig() bq_client = bigquery.client.Client( project=bq_client_project_id, location=dataset_location @@ -65,7 +49,7 @@ def bq_query_to_table( try: result = query_job.result() - logging.info(f"BQ table {dest_table_ref} created") + logging.info(f"BQ Job finished") except GoogleCloudError as e: logging.error(e) logging.error(query_job.error_result) diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index b011b0c1..3a9fdb02 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import os import pathlib @@ -29,9 +28,10 @@ def tensorflow_pipeline( project_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_project_id: str = os.environ.get("VERTEX_PROJECT_ID"), model_name: str = "simple_tensorflow", - dataset_id: str = "preprocessing", + preprocessing_dataset_id: str = "preprocessing", dataset_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_dataset_id: str = "chicago_taxi_trips", + prediction_dataset_id: str = "prediction", timestamp: str = "2022-12-01 00:00:00", batch_prediction_machine_type: str = "n1-standard-4", batch_prediction_min_replicas: int = 3, @@ -49,13 +49,14 @@ def tensorflow_pipeline( Args: project_id (str): project id of the Google Cloud project project_location (str): location of the Google Cloud project - pipeline_files_gcs_path (str): GCS path where the pipeline files are located ingestion_project_id (str): project id containing the source bigquery data for ingestion. This can be the same as `project_id` if the source data is in the same project where the ML pipeline is executed. model_name (str): name of model - model_label (str): label of model - dataset_id (str): id of BQ dataset used to store all staging data & predictions + preprocessing_dataset_id (str): id of BQ dataset used to + store all staging data . + prediction_dataset_id (str): id of BQ dataset used to + store all predictions. dataset_location (str): location of dataset ingestion_dataset_id (str): dataset id of ingestion data timestamp (str): Optional. Empty or a specific timestamp in ISO 8601 format @@ -91,6 +92,10 @@ def tensorflow_pipeline( queries_folder / "ingest.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, + prediction_dataset=f"{ingestion_project_id}.{prediction_dataset_id}", + preprocessing_dataset=f"{ingestion_project_id}.{preprocessing_dataset_id}", + ingested_table=ingested_table, + dataset_region=project_location, filter_column=time_column, filter_start_value=timestamp, ) @@ -98,14 +103,11 @@ def tensorflow_pipeline( # data ingestion and preprocessing operations kwargs = dict( bq_client_project_id=project_id, - destination_project_id=project_id, - dataset_id=dataset_id, dataset_location=dataset_location, - query_job_config=json.dumps(dict(write_disposition="WRITE_TRUNCATE")), ) - ingest = bq_query_to_table( - query=ingest_query, table_id=ingested_table, **kwargs - ).set_display_name("Ingest data") + ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( + "Ingest data" + ) # lookup champion model champion_model = ( @@ -120,8 +122,10 @@ def tensorflow_pipeline( ) # batch predict from BigQuery to BigQuery - bigquery_source_input_uri = f"bq://{project_id}.{dataset_id}.{ingested_table}" - bigquery_destination_output_uri = f"bq://{project_id}.{dataset_id}" + bigquery_source_input_uri = ( + f"bq://{project_id}.{preprocessing_dataset_id}.{ingested_table}" + ) + bigquery_destination_output_uri = f"bq://{project_id}.{prediction_dataset_id}" instance_config = {"instanceType": "object"} # predict data diff --git a/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql b/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql index e01a7c57..5a44d671 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql +++ b/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql @@ -14,8 +14,16 @@ -- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill +CREATE SCHEMA IF NOT EXISTS `{{ prediction_dataset }}` + OPTIONS ( + description = 'Prediction Dataset', + location = "{{ dataset_region }}"); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( with filter_start_values as ( - SELECT + SELECT IF("{{ filter_start_value }}" = '', CURRENT_DATETIME(), CAST("{{ filter_start_value }}" AS DATETIME)) as filter_start_value ) -- Ingest data between 2 and 3 months ago @@ -43,7 +51,7 @@ SELECT trip_miles, CAST( CASE WHEN trip_seconds is NULL then m.avg_trip_seconds WHEN trip_seconds <= 0 then m.avg_trip_seconds - ELSE trip_seconds + ELSE trip_seconds END AS FLOAT64) AS trip_seconds, payment_type, company, @@ -54,3 +62,4 @@ WHERE "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} AND `{{ field }}` IS NOT NULL {% endfor %} + ); diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 2767d210..56aea64c 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import os import pathlib @@ -101,6 +100,9 @@ def tensorflow_pipeline( queries_folder / "ingest.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + ingested_table=ingested_table, + dataset_region=project_location, filter_column=time_column, target_column=label_column_name, filter_start_value=timestamp, @@ -109,6 +111,8 @@ def tensorflow_pipeline( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=train_table, num_lots=10, lots=tuple(range(8)), ) @@ -116,6 +120,8 @@ def tensorflow_pipeline( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=valid_table, num_lots=10, lots="(8)", ) @@ -123,51 +129,34 @@ def tensorflow_pipeline( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=test_table, num_lots=10, lots="(9)", ) - data_cleaning_query = generate_query( - queries_folder / "engineer_features.sql", - source_dataset=dataset_id, - source_table=train_table, - ) # data ingestion and preprocessing operations - kwargs = dict( - bq_client_project_id=project_id, - destination_project_id=project_id, - dataset_id=dataset_id, - dataset_location=dataset_location, - query_job_config=json.dumps(dict(write_disposition="WRITE_TRUNCATE")), + kwargs = dict(bq_client_project_id=project_id, dataset_location=dataset_location) + ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( + "Ingest data" ) - ingest = bq_query_to_table( - query=ingest_query, table_id=ingested_table, **kwargs - ).set_display_name("Ingest data") - # exporting data to GCS from BQ split_train_data = ( - bq_query_to_table(query=split_train_query, table_id=train_table, **kwargs) + bq_query_to_table(query=split_train_query, **kwargs) .after(ingest) .set_display_name("Split train data") ) split_valid_data = ( - bq_query_to_table(query=split_valid_query, table_id=valid_table, **kwargs) + bq_query_to_table(query=split_valid_query, **kwargs) .after(ingest) .set_display_name("Split validation data") ) split_test_data = ( - bq_query_to_table(query=split_test_query, table_id=test_table, **kwargs) + bq_query_to_table(query=split_test_query, **kwargs) .after(ingest) .set_display_name("Split test data") ) - data_cleaning = ( - bq_query_to_table( - query=data_cleaning_query, table_id=preprocessed_table, **kwargs - ) - .after(split_train_data) - .set_display_name("Clean data") - ) # data extraction to gcs @@ -176,10 +165,10 @@ def tensorflow_pipeline( bq_client_project_id=project_id, source_project_id=project_id, dataset_id=dataset_id, - table_name=preprocessed_table, + table_name=train_table, dataset_location=dataset_location, ) - .after(data_cleaning) + .after(split_train_data) .set_display_name("Extract train data to storage") ).outputs["dataset"] valid_dataset = ( diff --git a/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql b/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql index de9459fc..7f9f3b0b 100644 --- a/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql +++ b/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql @@ -14,26 +14,36 @@ -- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill -with filter_start_values as ( - SELECT - IF("{{ filter_start_value }}" = '', CURRENT_DATETIME(), CAST("{{ filter_start_value }}" AS DATETIME)) as filter_start_value + +CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` + OPTIONS ( + description = 'Preprocessing Dataset', + location = "{{ dataset_region }}"); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( +WITH filter_start_values AS ( +SELECT + IF("{{ filter_start_value }}" = '', + CURRENT_DATETIME(), + CAST("{{ filter_start_value }}" AS DATETIME)) AS filter_start_value ) -- Ingest data between 2 and 3 months ago -,filtered_data as ( +,filtered_data AS ( SELECT * FROM `{{ source_dataset }}.{{ source_table }}`, filter_start_values WHERE DATE({{ filter_column }}) BETWEEN - DATE_SUB(DATE(CAST(filter_start_values.filter_start_value as DATETIME)), INTERVAL 3 MONTH) AND + DATE_SUB(DATE(CAST(filter_start_values.filter_start_value AS DATETIME)), INTERVAL 3 MONTH) AND DATE_SUB(DATE(filter_start_value), INTERVAL 2 MONTH) ) -- Use the average trip_seconds as a replacement for NULL or 0 values -,mean_time as ( +,mean_time AS ( SELECT CAST(avg(trip_seconds) AS INT64) as avg_trip_seconds FROM filtered_data ) - SELECT CAST(EXTRACT(DAYOFWEEK FROM trip_start_timestamp) AS FLOAT64) AS dayofweek, CAST(EXTRACT(HOUR FROM trip_start_timestamp) AS FLOAT64) AS hourofday, @@ -43,15 +53,16 @@ SELECT trip_miles, CAST( CASE WHEN trip_seconds is NULL then m.avg_trip_seconds WHEN trip_seconds <= 0 then m.avg_trip_seconds - ELSE trip_seconds + ELSE trip_seconds END AS FLOAT64) AS trip_seconds, payment_type, company, (fare + tips + tolls + extras) AS `{{ target_column }}`, -FROM filtered_data as t, mean_time as m +FROM filtered_data AS t, mean_time AS m WHERE trip_miles > 0 AND fare > 0 AND fare < 1500 {% for field in ["fare", "trip_start_timestamp", "pickup_longitude", "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} AND `{{ field }}` IS NOT NULL {% endfor %} +); diff --git a/pipelines/src/pipelines/tensorflow/training/queries/sample.sql b/pipelines/src/pipelines/tensorflow/training/queries/sample.sql index bf47bbf6..be8458be 100644 --- a/pipelines/src/pipelines/tensorflow/training/queries/sample.sql +++ b/pipelines/src/pipelines/tensorflow/training/queries/sample.sql @@ -1,20 +1,10 @@ --- Copyright 2022 Google LLC +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ target_table }}`; --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at - --- https://www.apache.org/licenses/LICENSE-2.0 - --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - -SELECT * +CREATE TABLE `{{ preprocessing_dataset }}.{{ target_table }}` AS ( +SELECT + * FROM - `{{ source_dataset }}.{{ source_table }}` AS t + `{{ source_dataset }}.{{ source_table }}` AS t WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - {{ num_lots }}) IN {{ lots }} + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + {{ num_lots }}) IN {{ lots }}); diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index ed474eb8..0c4504ec 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import os import pathlib @@ -29,9 +28,10 @@ def xgboost_pipeline( project_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_project_id: str = os.environ.get("VERTEX_PROJECT_ID"), model_name: str = "simple_xgboost", - dataset_id: str = "preprocessing", + preprocessing_dataset_id: str = "preprocessing", dataset_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_dataset_id: str = "chicago_taxi_trips", + prediction_dataset_id: str = "prediction", timestamp: str = "2022-12-01 00:00:00", batch_prediction_machine_type: str = "n1-standard-4", batch_prediction_min_replicas: int = 3, @@ -50,14 +50,17 @@ def xgboost_pipeline( for ingestion. This can be the same as `project_id` if the source data is in the same project where the ML pipeline is executed. model_name (str): name of model - dataset_id (str): id of BQ dataset used to store all staging data & predictions + preprocessing_dataset_id (str): id of BQ dataset used to + store all staging data . + prediction_dataset_id (str): id of BQ dataset used to + store all predictions. dataset_location (str): location of dataset ingestion_dataset_id (str): dataset id of ingestion data timestamp (str): Optional. Empty or a specific timestamp in ISO 8601 format (YYYY-MM-DDThh:mm:ss.sss±hh:mm or YYYY-MM-DDThh:mm:ss). If any time part is missing, it will be regarded as zero. batch_prediction_machine_type (str): Machine type to be used for Vertex Batch - Prediction. Example machine_types - n1-standard-4, n1-standard-16 etc + Prediction. Example machine_types - n1-standard-4, n1-standard-16 etc. batch_prediction_min_replicas (int): Minimum no of machines to distribute the Vertex Batch Prediction job for horizontal scalability batch_prediction_max_replicas (int): Maximum no of machines to distribute the @@ -85,6 +88,10 @@ def xgboost_pipeline( queries_folder / "ingest.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, + prediction_dataset=f"{ingestion_project_id}.{prediction_dataset_id}", + preprocessing_dataset=f"{ingestion_project_id}.{preprocessing_dataset_id}", + ingested_table=ingested_table, + dataset_region=project_location, filter_column=time_column, filter_start_value=timestamp, ) @@ -92,14 +99,11 @@ def xgboost_pipeline( # data ingestion and preprocessing operations kwargs = dict( bq_client_project_id=project_id, - destination_project_id=project_id, - dataset_id=dataset_id, dataset_location=dataset_location, - query_job_config=json.dumps(dict(write_disposition="WRITE_TRUNCATE")), ) - ingest = bq_query_to_table( - query=ingest_query, table_id=ingested_table, **kwargs - ).set_display_name("Ingest data") + ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( + "Ingest data" + ) # lookup champion model champion_model = ( @@ -114,8 +118,10 @@ def xgboost_pipeline( ) # batch predict from BigQuery to BigQuery - bigquery_source_input_uri = f"bq://{project_id}.{dataset_id}.{ingested_table}" - bigquery_destination_output_uri = f"bq://{project_id}.{dataset_id}" + bigquery_source_input_uri = ( + f"bq://{project_id}.{preprocessing_dataset_id}.{ingested_table}" + ) + bigquery_destination_output_uri = f"bq://{project_id}.{prediction_dataset_id}" batch_prediction = ( model_batch_predict( diff --git a/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql b/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql index e01a7c57..bf8fe57a 100644 --- a/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql +++ b/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql @@ -14,6 +14,14 @@ -- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill +CREATE SCHEMA IF NOT EXISTS `{{ prediction_dataset }}` + OPTIONS ( + description = 'Prediction Dataset', + location = "{{ dataset_region }}"); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( with filter_start_values as ( SELECT IF("{{ filter_start_value }}" = '', CURRENT_DATETIME(), CAST("{{ filter_start_value }}" AS DATETIME)) as filter_start_value @@ -54,3 +62,4 @@ WHERE "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} AND `{{ field }}` IS NOT NULL {% endfor %} + ); diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 27cb47ed..6c9ad9d9 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import os import pathlib @@ -99,6 +98,9 @@ def xgboost_pipeline( queries_folder / "ingest.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + ingested_table=ingested_table, + dataset_region=project_location, filter_column=time_column, target_column=label_column_name, filter_start_value=timestamp, @@ -107,6 +109,8 @@ def xgboost_pipeline( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=train_table, num_lots=10, lots=tuple(range(8)), ) @@ -114,57 +118,43 @@ def xgboost_pipeline( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=valid_table, num_lots=10, lots="(8)", ) - data_cleaning_query = generate_query( - queries_folder / "engineer_features.sql", - source_dataset=dataset_id, - source_table=train_table, - ) split_test_query = generate_query( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=test_table, num_lots=10, lots="(9)", ) # data ingestion and preprocessing operations - kwargs = dict( - bq_client_project_id=project_id, - destination_project_id=project_id, - dataset_id=dataset_id, - dataset_location=dataset_location, - query_job_config=json.dumps(dict(write_disposition="WRITE_TRUNCATE")), + kwargs = dict(bq_client_project_id=project_id, dataset_location=dataset_location) + ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( + "Ingest data" ) - ingest = bq_query_to_table( - query=ingest_query, table_id=ingested_table, **kwargs - ).set_display_name("Ingest data") split_train_data = ( - bq_query_to_table(query=split_train_query, table_id=train_table, **kwargs) + bq_query_to_table(query=split_train_query, **kwargs) .after(ingest) .set_display_name("Split train data") ) split_valid_data = ( - bq_query_to_table(query=split_valid_query, table_id=valid_table, **kwargs) + bq_query_to_table(query=split_valid_query, **kwargs) .after(ingest) .set_display_name("Split validation data") ) split_test_data = ( - bq_query_to_table(query=split_test_query, table_id=test_table, **kwargs) + bq_query_to_table(query=split_test_query, **kwargs) .after(ingest) .set_display_name("Split test data") ) - data_cleaning = ( - bq_query_to_table( - query=data_cleaning_query, table_id=preprocessed_table, **kwargs - ) - .after(split_train_data) - .set_display_name("Clean data") - ) # data extraction to gcs @@ -173,10 +163,10 @@ def xgboost_pipeline( bq_client_project_id=project_id, source_project_id=project_id, dataset_id=dataset_id, - table_name=preprocessed_table, + table_name=train_table, dataset_location=dataset_location, ) - .after(data_cleaning) + .after(split_train_data) .set_display_name("Extract train data to storage") ).outputs["dataset"] valid_dataset = ( diff --git a/pipelines/src/pipelines/xgboost/training/queries/ingest.sql b/pipelines/src/pipelines/xgboost/training/queries/ingest.sql index de9459fc..fbb6df32 100644 --- a/pipelines/src/pipelines/xgboost/training/queries/ingest.sql +++ b/pipelines/src/pipelines/xgboost/training/queries/ingest.sql @@ -14,26 +14,36 @@ -- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill -with filter_start_values as ( - SELECT - IF("{{ filter_start_value }}" = '', CURRENT_DATETIME(), CAST("{{ filter_start_value }}" AS DATETIME)) as filter_start_value + +CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` + OPTIONS ( + description = 'Preprocessing Dataset', + location = "{{ dataset_region }}"); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( +WITH filter_start_values AS ( +SELECT + IF("{{ filter_start_value }}" = '', + CURRENT_DATETIME(), + CAST("{{ filter_start_value }}" AS DATETIME)) AS filter_start_value ) -- Ingest data between 2 and 3 months ago -,filtered_data as ( +,filtered_data AS ( SELECT * FROM `{{ source_dataset }}.{{ source_table }}`, filter_start_values WHERE DATE({{ filter_column }}) BETWEEN - DATE_SUB(DATE(CAST(filter_start_values.filter_start_value as DATETIME)), INTERVAL 3 MONTH) AND + DATE_SUB(DATE(CAST(filter_start_values.filter_start_value AS DATETIME)), INTERVAL 3 MONTH) AND DATE_SUB(DATE(filter_start_value), INTERVAL 2 MONTH) ) -- Use the average trip_seconds as a replacement for NULL or 0 values -,mean_time as ( +,mean_time AS ( SELECT CAST(avg(trip_seconds) AS INT64) as avg_trip_seconds FROM filtered_data ) - SELECT CAST(EXTRACT(DAYOFWEEK FROM trip_start_timestamp) AS FLOAT64) AS dayofweek, CAST(EXTRACT(HOUR FROM trip_start_timestamp) AS FLOAT64) AS hourofday, @@ -48,10 +58,11 @@ SELECT payment_type, company, (fare + tips + tolls + extras) AS `{{ target_column }}`, -FROM filtered_data as t, mean_time as m +FROM filtered_data AS t, mean_time AS m WHERE trip_miles > 0 AND fare > 0 AND fare < 1500 {% for field in ["fare", "trip_start_timestamp", "pickup_longitude", "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} AND `{{ field }}` IS NOT NULL {% endfor %} +); diff --git a/pipelines/src/pipelines/xgboost/training/queries/sample.sql b/pipelines/src/pipelines/xgboost/training/queries/sample.sql index bb2dc9ce..be8458be 100644 --- a/pipelines/src/pipelines/xgboost/training/queries/sample.sql +++ b/pipelines/src/pipelines/xgboost/training/queries/sample.sql @@ -1,6 +1,10 @@ -SELECT * +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ target_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ target_table }}` AS ( +SELECT + * FROM - `{{ source_dataset }}.{{ source_table }}` AS t + `{{ source_dataset }}.{{ source_table }}` AS t WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - {{ num_lots }}) IN {{ lots }} + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + {{ num_lots }}) IN {{ lots }}); From 531f125fe2cf97cbbae86238cc8978793c0d098e Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 16 May 2023 11:49:51 +0100 Subject: [PATCH 003/238] feat: combine assets into a single folder --- Makefile | 6 +++--- .../{pipelines/tensorflow/prediction => }/assets/.gitkeep | 0 .../tensorflow/training => }/assets/train_tf_model.py | 0 .../xgboost/training => }/assets/train_xgb_model.py | 0 pipelines/pipelines/tensorflow/training/pipeline.py | 2 +- pipelines/pipelines/xgboost/prediction/assets/.gitkeep | 0 pipelines/pipelines/xgboost/training/assets/.gitkeep | 0 pipelines/pipelines/xgboost/training/pipeline.py | 2 +- 8 files changed, 5 insertions(+), 5 deletions(-) rename pipelines/{pipelines/tensorflow/prediction => }/assets/.gitkeep (100%) rename pipelines/{pipelines/tensorflow/training => }/assets/train_tf_model.py (100%) rename pipelines/{pipelines/xgboost/training => }/assets/train_xgb_model.py (100%) delete mode 100644 pipelines/pipelines/xgboost/prediction/assets/.gitkeep delete mode 100644 pipelines/pipelines/xgboost/training/assets/.gitkeep diff --git a/Makefile b/Makefile index 74718267..87426406 100644 --- a/Makefile +++ b/Makefile @@ -62,9 +62,9 @@ test-all-components: ## Run unit tests for all pipeline components $(MAKE) test-components GROUP=$$(basename $$component_group) ; \ done -sync-assets: ## Sync assets folder to GCS. Must specify pipeline= - if [ -d "./pipelines/pipelines/${PIPELINE_TEMPLATE}/$(pipeline)/assets/" ] ; then \ - gsutil -m rsync -r -d ./pipelines/pipelines/${PIPELINE_TEMPLATE}/$(pipeline)/assets ${PIPELINE_FILES_GCS_PATH}/$(pipeline)/assets ; \ +sync-assets: ## Sync assets folder to GCS. + if [ -d "./pipelines/assets" ] ; then \ + gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}/assets ; \ fi ; run: ## Compile pipeline, copy assets to GCS, and run pipeline in sandbox environment. Must specify pipeline=. Optionally specify enable_pipeline_caching= (defaults to default Vertex caching behaviour) diff --git a/pipelines/pipelines/tensorflow/prediction/assets/.gitkeep b/pipelines/assets/.gitkeep similarity index 100% rename from pipelines/pipelines/tensorflow/prediction/assets/.gitkeep rename to pipelines/assets/.gitkeep diff --git a/pipelines/pipelines/tensorflow/training/assets/train_tf_model.py b/pipelines/assets/train_tf_model.py similarity index 100% rename from pipelines/pipelines/tensorflow/training/assets/train_tf_model.py rename to pipelines/assets/train_tf_model.py diff --git a/pipelines/pipelines/xgboost/training/assets/train_xgb_model.py b/pipelines/assets/train_xgb_model.py similarity index 100% rename from pipelines/pipelines/xgboost/training/assets/train_xgb_model.py rename to pipelines/assets/train_xgb_model.py diff --git a/pipelines/pipelines/tensorflow/training/pipeline.py b/pipelines/pipelines/tensorflow/training/pipeline.py index b2ab814a..838c082f 100644 --- a/pipelines/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/pipelines/tensorflow/training/pipeline.py @@ -81,7 +81,7 @@ def tensorflow_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/training/assets/train_tf_model.py" + train_script_uri = f"{pipeline_files_gcs_path}/assets/train_tf_model.py" hparams = dict( batch_size=100, epochs=5, diff --git a/pipelines/pipelines/xgboost/prediction/assets/.gitkeep b/pipelines/pipelines/xgboost/prediction/assets/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/pipelines/pipelines/xgboost/training/assets/.gitkeep b/pipelines/pipelines/xgboost/training/assets/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/pipelines/pipelines/xgboost/training/pipeline.py b/pipelines/pipelines/xgboost/training/pipeline.py index 5fe34056..20c83dfb 100644 --- a/pipelines/pipelines/xgboost/training/pipeline.py +++ b/pipelines/pipelines/xgboost/training/pipeline.py @@ -79,7 +79,7 @@ def xgboost_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/training/assets/train_xgb_model.py" + train_script_uri = f"{pipeline_files_gcs_path}/assets/train_xgb_model.py" hparams = dict( n_estimators=200, early_stopping_rounds=10, From 0b8b77f8758d4d64e4b5e1fb4754acc495598c42 Mon Sep 17 00:00:00 2001 From: ariadnafer Date: Tue, 16 May 2023 13:53:18 +0200 Subject: [PATCH 004/238] refactor: replace custom BQ component with BigqueryQueryJobOp --- .../src/bigquery_components/__init__.py | 2 - .../bigquery_components/bq_query_to_table.py | 57 ---------------- pipelines/src/pipelines/__init__.py | 8 ++- .../tensorflow/prediction/pipeline.py | 12 ++-- .../pipelines/tensorflow/training/pipeline.py | 66 ++++--------------- .../training/queries/engineer_features.sql | 18 ----- .../tensorflow/training/queries/ingest.sql | 50 +++++++++----- .../tensorflow/training/queries/sample.sql | 10 --- .../pipelines/xgboost/prediction/pipeline.py | 13 ++-- .../pipelines/xgboost/training/pipeline.py | 66 ++++--------------- .../training/queries/engineer_features.sql | 4 -- .../xgboost/training/queries/ingest.sql | 50 +++++++++----- .../xgboost/training/queries/sample.sql | 10 --- 13 files changed, 107 insertions(+), 259 deletions(-) delete mode 100644 components/bigquery-components/src/bigquery_components/bq_query_to_table.py delete mode 100644 pipelines/src/pipelines/tensorflow/training/queries/engineer_features.sql delete mode 100644 pipelines/src/pipelines/tensorflow/training/queries/sample.sql delete mode 100644 pipelines/src/pipelines/xgboost/training/queries/engineer_features.sql delete mode 100644 pipelines/src/pipelines/xgboost/training/queries/sample.sql diff --git a/components/bigquery-components/src/bigquery_components/__init__.py b/components/bigquery-components/src/bigquery_components/__init__.py index 2e3870ac..e979abab 100644 --- a/components/bigquery-components/src/bigquery_components/__init__.py +++ b/components/bigquery-components/src/bigquery_components/__init__.py @@ -1,9 +1,7 @@ -from .bq_query_to_table import bq_query_to_table from .extract_bq_to_dataset import extract_bq_to_dataset __version__ = "0.0.1" __all__ = [ - "bq_query_to_table", "extract_bq_to_dataset", ] diff --git a/components/bigquery-components/src/bigquery_components/bq_query_to_table.py b/components/bigquery-components/src/bigquery_components/bq_query_to_table.py deleted file mode 100644 index 4b6afc29..00000000 --- a/components/bigquery-components/src/bigquery_components/bq_query_to_table.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2022 Google LLC - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# https://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from kfp.v2.dsl import component - - -@component( - base_image="python:3.7", - packages_to_install=["google-cloud-bigquery==2.30.0"], -) -def bq_query_to_table( - query: str, - bq_client_project_id: str, - dataset_location: str = "EU", -) -> None: - """ - Run query & create a new BigQuery table - Args: - query (str): SQL query to execute, results are saved in a BigQuery table - bq_client_project_id (str): project id that will be used by the bq client - dataset_location (str): bq dataset location - required by the bq query operation. No need to specify destination param - Returns: - None - """ - from google.cloud.exceptions import GoogleCloudError - from google.cloud import bigquery - import logging - - logging.getLogger().setLevel(logging.INFO) - - job_config = bigquery.QueryJobConfig() - - bq_client = bigquery.client.Client( - project=bq_client_project_id, location=dataset_location - ) - query_job = bq_client.query(query, job_config=job_config) - - try: - result = query_job.result() - logging.info(f"BQ Job finished") - except GoogleCloudError as e: - logging.error(e) - logging.error(query_job.error_result) - logging.error(query_job.errors) - raise e diff --git a/pipelines/src/pipelines/__init__.py b/pipelines/src/pipelines/__init__.py index 113da917..badd535e 100644 --- a/pipelines/src/pipelines/__init__.py +++ b/pipelines/src/pipelines/__init__.py @@ -30,4 +30,10 @@ def generate_query(input_file: Path, **replacements) -> str: with open(input_file, "r") as f: query_template = f.read() - return Template(query_template).render(**replacements) + # Render the template with the provided replacements + query = Template(query_template).render(**replacements) + + # Escape double quotes, newline and tab characters + query = query.replace('"', '\\"').replace("\n", "\\n").replace("\t", "\\t") + + return query diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index 3a9fdb02..60ad3b47 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -15,10 +15,10 @@ import os import pathlib +from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp from kfp.v2 import compiler, dsl from pipelines import generate_query -from bigquery_components import bq_query_to_table from vertex_components import lookup_model, model_batch_predict @@ -101,13 +101,9 @@ def tensorflow_pipeline( ) # data ingestion and preprocessing operations - kwargs = dict( - bq_client_project_id=project_id, - dataset_location=dataset_location, - ) - ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( - "Ingest data" - ) + ingest = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=ingest_query + ).set_display_name("Ingest data") # lookup champion model champion_model = ( diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 56aea64c..ba98bea1 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -15,9 +15,10 @@ import os import pathlib +from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp from kfp.v2 import compiler, dsl from pipelines import generate_query -from bigquery_components import bq_query_to_table, extract_bq_to_dataset +from bigquery_components import extract_bq_to_dataset from vertex_components import ( lookup_model, custom_train_job, @@ -106,57 +107,14 @@ def tensorflow_pipeline( filter_column=time_column, target_column=label_column_name, filter_start_value=timestamp, + train_table=train_table, + validation_table=valid_table, + test_table=test_table, ) - split_train_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=train_table, - num_lots=10, - lots=tuple(range(8)), - ) - split_valid_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=valid_table, - num_lots=10, - lots="(8)", - ) - split_test_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=test_table, - num_lots=10, - lots="(9)", - ) - - # data ingestion and preprocessing operations - kwargs = dict(bq_client_project_id=project_id, dataset_location=dataset_location) - ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( - "Ingest data" - ) - - split_train_data = ( - bq_query_to_table(query=split_train_query, **kwargs) - .after(ingest) - .set_display_name("Split train data") - ) - split_valid_data = ( - bq_query_to_table(query=split_valid_query, **kwargs) - .after(ingest) - .set_display_name("Split validation data") - ) - split_test_data = ( - bq_query_to_table(query=split_test_query, **kwargs) - .after(ingest) - .set_display_name("Split test data") - ) + ingest = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=ingest_query + ).set_display_name("Ingest data") # data extraction to gcs @@ -168,8 +126,9 @@ def tensorflow_pipeline( table_name=train_table, dataset_location=dataset_location, ) - .after(split_train_data) + .after(ingest) .set_display_name("Extract train data to storage") + .set_caching_options(False) ).outputs["dataset"] valid_dataset = ( extract_bq_to_dataset( @@ -179,8 +138,9 @@ def tensorflow_pipeline( table_name=valid_table, dataset_location=dataset_location, ) - .after(split_valid_data) + .after(ingest) .set_display_name("Extract validation data to storage") + .set_caching_options(False) ).outputs["dataset"] test_dataset = ( extract_bq_to_dataset( @@ -191,7 +151,7 @@ def tensorflow_pipeline( dataset_location=dataset_location, destination_gcs_uri=test_dataset_uri, ) - .after(split_test_data) + .after(ingest) .set_display_name("Extract test data to storage") .set_caching_options(False) ).outputs["dataset"] diff --git a/pipelines/src/pipelines/tensorflow/training/queries/engineer_features.sql b/pipelines/src/pipelines/tensorflow/training/queries/engineer_features.sql deleted file mode 100644 index b8094a35..00000000 --- a/pipelines/src/pipelines/tensorflow/training/queries/engineer_features.sql +++ /dev/null @@ -1,18 +0,0 @@ --- Copyright 2022 Google LLC - --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at - --- https://www.apache.org/licenses/LICENSE-2.0 - --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - -/* The Purpose of this query is to clean the data before the training*/ -SELECT - * -FROM `{{ source_dataset }}.{{ source_table }}` diff --git a/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql b/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql index 7f9f3b0b..b3a31606 100644 --- a/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql +++ b/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql @@ -1,20 +1,3 @@ --- Copyright 2022 Google LLC - --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at - --- https://www.apache.org/licenses/LICENSE-2.0 - --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - --- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead --- This allows us to set the filter_start_value to a specific time for testing or for backfill - CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` OPTIONS ( description = 'Preprocessing Dataset', @@ -66,3 +49,36 @@ WHERE AND `{{ field }}` IS NOT NULL {% endfor %} ); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ train_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ train_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (0, 1, 2, 3, 4, 5, 6, 7)); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ validation_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ validation_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (8)); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ test_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ test_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (9)); diff --git a/pipelines/src/pipelines/tensorflow/training/queries/sample.sql b/pipelines/src/pipelines/tensorflow/training/queries/sample.sql deleted file mode 100644 index be8458be..00000000 --- a/pipelines/src/pipelines/tensorflow/training/queries/sample.sql +++ /dev/null @@ -1,10 +0,0 @@ -DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ target_table }}`; - -CREATE TABLE `{{ preprocessing_dataset }}.{{ target_table }}` AS ( -SELECT - * -FROM - `{{ source_dataset }}.{{ source_table }}` AS t -WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - {{ num_lots }}) IN {{ lots }}); diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index 0c4504ec..7fdea71c 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -15,10 +15,10 @@ import os import pathlib +from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp from kfp.v2 import compiler, dsl from pipelines import generate_query -from bigquery_components import bq_query_to_table from vertex_components import lookup_model, model_batch_predict @@ -96,14 +96,9 @@ def xgboost_pipeline( filter_start_value=timestamp, ) - # data ingestion and preprocessing operations - kwargs = dict( - bq_client_project_id=project_id, - dataset_location=dataset_location, - ) - ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( - "Ingest data" - ) + ingest = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=ingest_query + ).set_display_name("Ingest data") # lookup champion model champion_model = ( diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 6c9ad9d9..385e41ab 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -15,9 +15,10 @@ import os import pathlib +from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp from kfp.v2 import compiler, dsl from pipelines import generate_query -from bigquery_components import bq_query_to_table, extract_bq_to_dataset +from bigquery_components import extract_bq_to_dataset from vertex_components import ( lookup_model, custom_train_job, @@ -104,57 +105,14 @@ def xgboost_pipeline( filter_column=time_column, target_column=label_column_name, filter_start_value=timestamp, + train_table=train_table, + validation_table=valid_table, + test_table=test_table, ) - split_train_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=train_table, - num_lots=10, - lots=tuple(range(8)), - ) - split_valid_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=valid_table, - num_lots=10, - lots="(8)", - ) - split_test_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=test_table, - num_lots=10, - lots="(9)", - ) - - # data ingestion and preprocessing operations - kwargs = dict(bq_client_project_id=project_id, dataset_location=dataset_location) - ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( - "Ingest data" - ) - - split_train_data = ( - bq_query_to_table(query=split_train_query, **kwargs) - .after(ingest) - .set_display_name("Split train data") - ) - split_valid_data = ( - bq_query_to_table(query=split_valid_query, **kwargs) - .after(ingest) - .set_display_name("Split validation data") - ) - split_test_data = ( - bq_query_to_table(query=split_test_query, **kwargs) - .after(ingest) - .set_display_name("Split test data") - ) + ingest = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=ingest_query + ).set_display_name("Ingest data") # data extraction to gcs @@ -166,8 +124,9 @@ def xgboost_pipeline( table_name=train_table, dataset_location=dataset_location, ) - .after(split_train_data) + .after(ingest) .set_display_name("Extract train data to storage") + .set_caching_options(False) ).outputs["dataset"] valid_dataset = ( extract_bq_to_dataset( @@ -177,8 +136,9 @@ def xgboost_pipeline( table_name=valid_table, dataset_location=dataset_location, ) - .after(split_valid_data) + .after(ingest) .set_display_name("Extract validation data to storage") + .set_caching_options(False) ).outputs["dataset"] test_dataset = ( extract_bq_to_dataset( @@ -189,7 +149,7 @@ def xgboost_pipeline( dataset_location=dataset_location, destination_gcs_uri=test_dataset_uri, ) - .after(split_test_data) + .after(ingest) .set_display_name("Extract test data to storage") .set_caching_options(False) ).outputs["dataset"] diff --git a/pipelines/src/pipelines/xgboost/training/queries/engineer_features.sql b/pipelines/src/pipelines/xgboost/training/queries/engineer_features.sql deleted file mode 100644 index 148d9ce7..00000000 --- a/pipelines/src/pipelines/xgboost/training/queries/engineer_features.sql +++ /dev/null @@ -1,4 +0,0 @@ -/* The Purpose of this query is to clean the data before the training*/ -SELECT - * -FROM `{{ source_dataset }}.{{ source_table }}` diff --git a/pipelines/src/pipelines/xgboost/training/queries/ingest.sql b/pipelines/src/pipelines/xgboost/training/queries/ingest.sql index fbb6df32..10ecf037 100644 --- a/pipelines/src/pipelines/xgboost/training/queries/ingest.sql +++ b/pipelines/src/pipelines/xgboost/training/queries/ingest.sql @@ -1,20 +1,3 @@ --- Copyright 2022 Google LLC - --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at - --- https://www.apache.org/licenses/LICENSE-2.0 - --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - --- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead --- This allows us to set the filter_start_value to a specific time for testing or for backfill - CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` OPTIONS ( description = 'Preprocessing Dataset', @@ -66,3 +49,36 @@ WHERE AND `{{ field }}` IS NOT NULL {% endfor %} ); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ train_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ train_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (0, 1, 2, 3, 4, 5, 6, 7)); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ validation_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ validation_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (8)); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ test_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ test_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (9)); diff --git a/pipelines/src/pipelines/xgboost/training/queries/sample.sql b/pipelines/src/pipelines/xgboost/training/queries/sample.sql deleted file mode 100644 index be8458be..00000000 --- a/pipelines/src/pipelines/xgboost/training/queries/sample.sql +++ /dev/null @@ -1,10 +0,0 @@ -DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ target_table }}`; - -CREATE TABLE `{{ preprocessing_dataset }}.{{ target_table }}` AS ( -SELECT - * -FROM - `{{ source_dataset }}.{{ source_table }}` AS t -WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - {{ num_lots }}) IN {{ lots }}); From e784ab1f2226b3a709114bc037ec6ab7f305a8cc Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 16 May 2023 13:07:29 +0100 Subject: [PATCH 005/238] docs: update related to assets --- README.md | 2 +- cloudbuild/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f83622a7..98051006 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ When triggering ad hoc runs in your dev/sandbox environment, or when running the ### Assets -In each pipeline folder, there is an `assets` directory (`pipelines/pipelines///assets/`). This can be used for any additional files that may be needed during execution of the pipelines. +Pipeline folder, contains `assets` directory (`pipelines/assets/`). This can be used for any additional files that may be needed during execution of the pipelines. This directory is rsync'd to Google Cloud Storage when running a pipeline in the sandbox environment or as part of the CD pipeline (see [CI/CD setup](cloudbuild/README.md)). ## Testing diff --git a/cloudbuild/README.md b/cloudbuild/README.md index 97eb2a4a..d541b034 100644 --- a/cloudbuild/README.md +++ b/cloudbuild/README.md @@ -21,8 +21,8 @@ limitations under the License. There are five CI/CD pipelines 1. `pr-checks.yaml` - runs pre-commit checks and unit tests on the custom KFP components, and checks that the ML pipelines (training and prediction) can compile. -1. `e2e-test.yaml` - copies the "assets" folders to the chosen GCS destination (versioned by git commit hash) and runs end-to-end tests of the training and prediction pipeline. -1. `release.yaml` - compiles training and prediction pipelines, then copies the compiled pipelines and their respective "assets" folders to the chosen GCS destination (versioned by git tag). +1. `e2e-test.yaml` - copies the "assets" folder to the chosen GCS destination (versioned by git commit hash) and runs end-to-end tests of the training and prediction pipeline. +1. `release.yaml` - compiles training and prediction pipelines, then copies the compiled pipelines and "assets" folder to the chosen GCS destination (versioned by git tag). 1. `terraform-plan.yaml` - Checks the Terraform configuration under `terraform/envs/` (e.g. `terraform/envs/test`), and produces a summary of any proposed changes that will be applied on merge to the main branch. 1. `terraform-apply.yaml` - Applies the Terraform configuration under `terraform/envs/` (e.g. `terraform/envs/test`). From 15bf42508fa977dd731ba1f4f1acbcbcb2b82f06 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 16 May 2023 14:08:13 +0100 Subject: [PATCH 006/238] ci: ci changes after combined assets --- cloudbuild/e2e-test.yaml | 6 ++---- cloudbuild/release.yaml | 10 ++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 1194415e..c8f86917 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -22,10 +22,8 @@ steps: args: - -c - | - mkdir -p ${COMMIT_SHA}/training/assets && \ - mkdir -p ${COMMIT_SHA}/prediction/assets && \ - cp -r pipelines/pipelines/${_PIPELINE_TEMPLATE}/training/assets ${COMMIT_SHA}/training/ && \ - cp -r pipelines/pipelines/${_PIPELINE_TEMPLATE}/prediction/assets ${COMMIT_SHA}/prediction/ && \ + mkdir -p ${COMMIT_SHA}/assets && \ + cp -r pipelines/assets && \ gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} # Install Python deps diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index 63e60115..adf2a018 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -35,12 +35,10 @@ steps: args: - -c - | - mkdir -p ${TAG_NAME}/training/assets && \ - mkdir -p ${TAG_NAME}/prediction/assets && \ - cp pipelines/training.json ${TAG_NAME}/training/training.json && \ - cp pipelines/prediction.json ${TAG_NAME}/prediction/prediction.json && \ - cp -r pipelines/pipelines/${_PIPELINE_TEMPLATE}/training/assets ${TAG_NAME}/training/ && \ - cp -r pipelines/pipelines/${_PIPELINE_TEMPLATE}/prediction/assets ${TAG_NAME}/prediction/ && \ + mkdir -p ${TAG_NAME}/assets && \ + cp pipelines/training.json ${TAG_NAME}/training.json && \ + cp pipelines/prediction.json ${TAG_NAME}/prediction.json && \ + cp -r pipelines/assets ${TAG_NAME} && \ for dest in ${_PIPELINE_PUBLISH_GCS_PATHS} ; do \ gsutil cp -r ${TAG_NAME} $$dest ; \ done From 4bc0eff627bb217891c4f1556b8bb74331b621ae Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 16 May 2023 14:18:16 +0100 Subject: [PATCH 007/238] ci: cloudbuild assets fix --- cloudbuild/e2e-test.yaml | 6 ++++-- cloudbuild/release.yaml | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index c8f86917..f503f5cf 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -22,8 +22,10 @@ steps: args: - -c - | - mkdir -p ${COMMIT_SHA}/assets && \ - cp -r pipelines/assets && \ + mkdir -p ${COMMIT_SHA}/training/assets && \ + mkdir -p ${COMMIT_SHA}/prediction/assets && \ + cp -r pipelines/assets ${COMMIT_SHA}/training/ && \ + cp -r pipelines/assets ${COMMIT_SHA}/prediction/ && \ gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} # Install Python deps diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index adf2a018..0804f09f 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -35,10 +35,12 @@ steps: args: - -c - | - mkdir -p ${TAG_NAME}/assets && \ - cp pipelines/training.json ${TAG_NAME}/training.json && \ - cp pipelines/prediction.json ${TAG_NAME}/prediction.json && \ - cp -r pipelines/assets ${TAG_NAME} && \ + mkdir -p ${TAG_NAME}/training/assets && \ + mkdir -p ${TAG_NAME}/prediction/assets && \ + cp pipelines/training.json ${TAG_NAME}/training/training.json && \ + cp pipelines/prediction.json ${TAG_NAME}/prediction/prediction.json && \ + cp -r pipelines/assets ${TAG_NAME}/training/ && \ + cp -r pipelines/assets ${TAG_NAME}/prediction/ && \ for dest in ${_PIPELINE_PUBLISH_GCS_PATHS} ; do \ gsutil cp -r ${TAG_NAME} $$dest ; \ done From bb4bfa1712893c5d486c5c71d31c15ccaea10463 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 16 May 2023 15:12:04 +0100 Subject: [PATCH 008/238] ci: ci changes --- cloudbuild/e2e-test.yaml | 6 ++---- cloudbuild/release.yaml | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index f503f5cf..7e2edcef 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -22,10 +22,8 @@ steps: args: - -c - | - mkdir -p ${COMMIT_SHA}/training/assets && \ - mkdir -p ${COMMIT_SHA}/prediction/assets && \ - cp -r pipelines/assets ${COMMIT_SHA}/training/ && \ - cp -r pipelines/assets ${COMMIT_SHA}/prediction/ && \ + mkdir -p ${COMMIT_SHA}/assets && \ + cp -r pipelines/assets ${COMMIT_SHA} && \ gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} # Install Python deps diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index 0804f09f..63e60115 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -39,8 +39,8 @@ steps: mkdir -p ${TAG_NAME}/prediction/assets && \ cp pipelines/training.json ${TAG_NAME}/training/training.json && \ cp pipelines/prediction.json ${TAG_NAME}/prediction/prediction.json && \ - cp -r pipelines/assets ${TAG_NAME}/training/ && \ - cp -r pipelines/assets ${TAG_NAME}/prediction/ && \ + cp -r pipelines/pipelines/${_PIPELINE_TEMPLATE}/training/assets ${TAG_NAME}/training/ && \ + cp -r pipelines/pipelines/${_PIPELINE_TEMPLATE}/prediction/assets ${TAG_NAME}/prediction/ && \ for dest in ${_PIPELINE_PUBLISH_GCS_PATHS} ; do \ gsutil cp -r ${TAG_NAME} $$dest ; \ done From 2580bd4b1d417f87bf12bd6d32e511818aefe494 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Mon, 15 May 2023 23:14:26 +0100 Subject: [PATCH 009/238] build(.python-version): change python version from 3.7 to 3.9 --- .python-version | 2 +- components/bigquery-components/.python-version | 2 +- components/vertex-components/.python-version | 2 +- pipelines/.python-version | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.python-version b/.python-version index f7e5aa84..9f3d4c17 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.7.12 +3.9.16 diff --git a/components/bigquery-components/.python-version b/components/bigquery-components/.python-version index f7e5aa84..9f3d4c17 100644 --- a/components/bigquery-components/.python-version +++ b/components/bigquery-components/.python-version @@ -1 +1 @@ -3.7.12 +3.9.16 diff --git a/components/vertex-components/.python-version b/components/vertex-components/.python-version index f7e5aa84..9f3d4c17 100644 --- a/components/vertex-components/.python-version +++ b/components/vertex-components/.python-version @@ -1 +1 @@ -3.7.12 +3.9.16 diff --git a/pipelines/.python-version b/pipelines/.python-version index f7e5aa84..9f3d4c17 100644 --- a/pipelines/.python-version +++ b/pipelines/.python-version @@ -1 +1 @@ -3.7.12 +3.9.16 From 453bb9fc29551962589dda950b79cf16de4c2d51 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Mon, 15 May 2023 23:17:48 +0100 Subject: [PATCH 010/238] build(pyproject.toml): migrate from pipenv to poetry all pipfile.lock files have been deleted and replaced with poetry.lock files --- components/bigquery-components/Pipfile | 14 - components/bigquery-components/Pipfile.lock | 1220 ----------- components/bigquery-components/poetry.lock | 1772 ++++++++++++++++ components/bigquery-components/pyproject.toml | 36 +- components/vertex-components/Pipfile | 16 - components/vertex-components/Pipfile.lock | 1792 ---------------- components/vertex-components/poetry.lock | 1695 +++++++++++++++ components/vertex-components/pyproject.toml | 37 +- pipelines/Pipfile | 19 - pipelines/Pipfile.lock | 1193 ----------- pipelines/poetry.lock | 1845 +++++++++++++++++ pipelines/pyproject.toml | 35 +- poetry.lock | 1845 +++++++++++++++++ pyproject.toml | 26 + 14 files changed, 7235 insertions(+), 4310 deletions(-) delete mode 100644 components/bigquery-components/Pipfile delete mode 100644 components/bigquery-components/Pipfile.lock create mode 100644 components/bigquery-components/poetry.lock delete mode 100644 components/vertex-components/Pipfile delete mode 100644 components/vertex-components/Pipfile.lock create mode 100644 components/vertex-components/poetry.lock delete mode 100644 pipelines/Pipfile delete mode 100644 pipelines/Pipfile.lock create mode 100644 pipelines/poetry.lock create mode 100644 poetry.lock create mode 100644 pyproject.toml diff --git a/components/bigquery-components/Pipfile b/components/bigquery-components/Pipfile deleted file mode 100644 index cfe725a8..00000000 --- a/components/bigquery-components/Pipfile +++ /dev/null @@ -1,14 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -kfp = "==1.8.21" - -[dev-packages] -google-cloud-bigquery = "==2.30.0" -pytest = ">=7.3.1,<8.0.0" - -[requires] -python_version = "3.7" diff --git a/components/bigquery-components/Pipfile.lock b/components/bigquery-components/Pipfile.lock deleted file mode 100644 index 602d5cba..00000000 --- a/components/bigquery-components/Pipfile.lock +++ /dev/null @@ -1,1220 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "5d62fbd9c31800d2f58a5c338a68119081148c391a4437bdc919329e2d2b3029" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.7" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "absl-py": { - "hashes": [ - "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47", - "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d" - ], - "markers": "python_version >= '3.6'", - "version": "==1.4.0" - }, - "attrs": { - "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" - }, - "cachetools": { - "hashes": [ - "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14", - "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4" - ], - "markers": "python_version ~= '3.7'", - "version": "==5.3.0" - }, - "certifi": { - "hashes": [ - "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", - "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.5.7" - }, - "charset-normalizer": { - "hashes": [ - "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", - "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", - "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", - "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", - "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", - "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", - "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", - "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", - "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", - "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", - "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", - "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", - "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", - "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", - "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", - "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", - "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", - "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", - "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", - "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", - "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", - "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", - "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", - "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", - "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", - "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", - "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", - "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", - "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", - "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", - "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", - "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", - "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", - "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", - "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", - "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", - "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", - "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", - "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", - "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", - "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", - "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", - "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", - "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", - "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", - "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", - "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", - "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", - "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", - "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", - "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", - "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", - "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", - "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", - "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", - "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", - "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", - "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", - "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", - "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", - "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", - "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", - "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", - "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", - "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", - "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", - "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", - "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", - "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", - "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", - "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", - "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", - "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", - "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", - "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.1.0" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "cloudpickle": { - "hashes": [ - "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f", - "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5" - ], - "markers": "python_version >= '3.6'", - "version": "==2.2.1" - }, - "deprecated": { - "hashes": [ - "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d", - "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.13" - }, - "docstring-parser": { - "hashes": [ - "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682", - "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9" - ], - "markers": "python_version >= '3.6' and python_version < '4.0'", - "version": "==0.15" - }, - "fire": { - "hashes": [ - "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6" - ], - "version": "==0.5.0" - }, - "google-api-core": { - "hashes": [ - "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22", - "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.11.0" - }, - "google-api-python-client": { - "hashes": [ - "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9", - "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.12.11" - }, - "google-auth": { - "hashes": [ - "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc", - "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.17.3" - }, - "google-auth-httplib2": { - "hashes": [ - "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10", - "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac" - ], - "version": "==0.1.0" - }, - "google-cloud-core": { - "hashes": [ - "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe", - "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.2" - }, - "google-cloud-storage": { - "hashes": [ - "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94", - "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3" - ], - "markers": "python_version >= '3.7'", - "version": "==2.9.0" - }, - "google-crc32c": { - "hashes": [ - "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", - "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", - "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", - "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", - "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", - "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", - "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", - "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", - "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", - "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", - "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", - "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", - "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", - "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", - "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", - "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", - "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", - "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", - "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", - "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", - "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", - "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", - "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", - "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", - "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", - "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", - "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", - "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", - "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", - "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", - "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", - "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", - "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", - "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", - "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", - "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", - "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", - "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", - "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", - "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", - "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", - "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", - "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", - "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", - "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", - "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", - "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", - "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", - "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", - "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", - "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", - "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", - "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", - "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", - "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", - "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", - "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", - "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", - "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", - "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", - "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", - "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", - "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", - "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", - "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", - "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", - "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", - "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.0" - }, - "google-resumable-media": { - "hashes": [ - "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93", - "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.0" - }, - "googleapis-common-protos": { - "hashes": [ - "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44", - "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f" - ], - "markers": "python_version >= '3.7'", - "version": "==1.59.0" - }, - "httplib2": { - "hashes": [ - "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", - "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.22.0" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "importlib-metadata": { - "hashes": [ - "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed", - "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705" - ], - "markers": "python_version < '3.8'", - "version": "==6.6.0" - }, - "importlib-resources": { - "hashes": [ - "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6", - "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a" - ], - "markers": "python_version < '3.9'", - "version": "==5.12.0" - }, - "jsonschema": { - "hashes": [ - "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6" - ], - "markers": "python_version >= '3.7'", - "version": "==4.17.3" - }, - "kfp": { - "hashes": [ - "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c" - ], - "index": "pypi", - "version": "==1.8.21" - }, - "kfp-pipeline-spec": { - "hashes": [ - "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==0.1.16" - }, - "kfp-server-api": { - "hashes": [ - "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de" - ], - "version": "==1.8.5" - }, - "kubernetes": { - "hashes": [ - "sha256:213befbb4e5aed95f94950c7eed0c2322fc5a2f8f40932e58d28fdd42d90836c", - "sha256:eb42333dad0bb5caf4e66460c6a4a1a36f0f057a040f35018f6c05a699baed86" - ], - "markers": "python_version >= '3.6'", - "version": "==25.3.0" - }, - "oauthlib": { - "hashes": [ - "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", - "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918" - ], - "markers": "python_version >= '3.6'", - "version": "==3.2.2" - }, - "pkgutil-resolve-name": { - "hashes": [ - "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174", - "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e" - ], - "markers": "python_version < '3.9'", - "version": "==1.3.10" - }, - "protobuf": { - "hashes": [ - "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7", - "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c", - "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2", - "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b", - "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050", - "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9", - "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7", - "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454", - "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480", - "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469", - "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c", - "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e", - "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db", - "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905", - "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b", - "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86", - "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4", - "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402", - "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7", - "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4", - "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99", - "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee" - ], - "markers": "python_version >= '3.7'", - "version": "==3.20.3" - }, - "pyasn1": { - "hashes": [ - "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57", - "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.5.0" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", - "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.3.0" - }, - "pydantic": { - "hashes": [ - "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e", - "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6", - "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd", - "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca", - "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b", - "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a", - "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245", - "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d", - "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee", - "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1", - "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3", - "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d", - "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5", - "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914", - "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd", - "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1", - "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e", - "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e", - "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a", - "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd", - "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f", - "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209", - "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d", - "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a", - "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143", - "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918", - "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52", - "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e", - "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f", - "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e", - "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb", - "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe", - "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe", - "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d", - "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209", - "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af" - ], - "markers": "python_version >= '3.7'", - "version": "==1.10.7" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_version >= '3.1'", - "version": "==3.0.9" - }, - "pyrsistent": { - "hashes": [ - "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8", - "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440", - "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a", - "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c", - "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3", - "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393", - "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9", - "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da", - "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf", - "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64", - "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a", - "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3", - "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98", - "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2", - "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8", - "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf", - "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc", - "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7", - "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28", - "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2", - "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b", - "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a", - "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64", - "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19", - "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1", - "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9", - "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c" - ], - "markers": "python_version >= '3.7'", - "version": "==0.19.3" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pyyaml": { - "hashes": [ - "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", - "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", - "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", - "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", - "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", - "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", - "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", - "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", - "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", - "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", - "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", - "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", - "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", - "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", - "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", - "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", - "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", - "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", - "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", - "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", - "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", - "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", - "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", - "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", - "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", - "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", - "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", - "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", - "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.4.1" - }, - "requests": { - "hashes": [ - "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294", - "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4" - ], - "markers": "python_version >= '3.7'", - "version": "==2.30.0" - }, - "requests-oauthlib": { - "hashes": [ - "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", - "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.3.1" - }, - "requests-toolbelt": { - "hashes": [ - "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7", - "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.1" - }, - "rsa": { - "hashes": [ - "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", - "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" - ], - "markers": "python_version >= '3.6'", - "version": "==4.9" - }, - "setuptools": { - "hashes": [ - "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", - "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" - ], - "markers": "python_version >= '3.7'", - "version": "==67.7.2" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "strip-hints": { - "hashes": [ - "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f" - ], - "version": "==0.1.10" - }, - "tabulate": { - "hashes": [ - "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", - "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" - ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" - }, - "termcolor": { - "hashes": [ - "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475", - "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.0" - }, - "typer": { - "hashes": [ - "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2", - "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee" - ], - "markers": "python_version >= '3.6'", - "version": "==0.9.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", - "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" - ], - "markers": "python_version < '3.9'", - "version": "==4.5.0" - }, - "uritemplate": { - "hashes": [ - "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f", - "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.0.1" - }, - "urllib3": { - "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" - }, - "websocket-client": { - "hashes": [ - "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40", - "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.1" - }, - "wheel": { - "hashes": [ - "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873", - "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247" - ], - "markers": "python_version >= '3.7'", - "version": "==0.40.0" - }, - "wrapt": { - "hashes": [ - "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", - "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", - "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", - "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", - "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", - "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", - "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", - "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", - "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", - "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", - "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", - "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", - "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", - "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", - "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", - "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", - "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", - "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", - "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", - "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", - "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", - "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", - "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", - "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", - "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", - "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", - "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", - "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", - "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", - "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", - "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", - "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", - "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", - "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", - "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", - "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", - "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", - "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", - "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", - "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", - "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", - "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", - "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", - "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", - "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", - "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", - "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", - "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", - "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", - "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", - "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", - "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", - "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", - "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", - "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", - "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", - "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", - "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", - "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", - "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", - "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", - "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", - "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", - "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", - "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", - "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", - "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", - "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", - "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", - "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", - "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", - "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", - "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", - "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", - "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.15.0" - }, - "zipp": { - "hashes": [ - "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", - "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556" - ], - "markers": "python_version < '3.10'", - "version": "==3.15.0" - } - }, - "develop": { - "cachetools": { - "hashes": [ - "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14", - "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4" - ], - "markers": "python_version ~= '3.7'", - "version": "==5.3.0" - }, - "certifi": { - "hashes": [ - "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", - "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.5.7" - }, - "charset-normalizer": { - "hashes": [ - "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", - "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", - "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", - "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", - "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", - "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", - "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", - "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", - "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", - "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", - "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", - "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", - "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", - "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", - "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", - "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", - "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", - "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", - "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", - "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", - "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", - "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", - "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", - "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", - "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", - "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", - "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", - "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", - "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", - "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", - "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", - "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", - "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", - "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", - "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", - "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", - "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", - "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", - "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", - "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", - "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", - "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", - "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", - "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", - "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", - "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", - "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", - "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", - "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", - "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", - "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", - "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", - "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", - "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", - "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", - "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", - "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", - "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", - "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", - "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", - "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", - "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", - "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", - "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", - "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", - "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", - "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", - "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", - "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", - "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", - "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", - "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", - "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", - "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", - "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.1.0" - }, - "exceptiongroup": { - "hashes": [ - "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e", - "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785" - ], - "markers": "python_version < '3.11'", - "version": "==1.1.1" - }, - "google-api-core": { - "hashes": [ - "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22", - "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.11.0" - }, - "google-auth": { - "hashes": [ - "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc", - "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.17.3" - }, - "google-cloud-bigquery": { - "hashes": [ - "sha256:f6546ee96d57f45eaac8c4b24b52228a7f21a33669730efc9e69569bc88a04ad", - "sha256:fdb57aa5e8af7d692eb5835739d9339dc1b5e89836430fe88dd1ddc1b0047639" - ], - "index": "pypi", - "version": "==2.30.0" - }, - "google-cloud-core": { - "hashes": [ - "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe", - "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.2" - }, - "google-crc32c": { - "hashes": [ - "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", - "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", - "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", - "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", - "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", - "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", - "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", - "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", - "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", - "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", - "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", - "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", - "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", - "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", - "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", - "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", - "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", - "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", - "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", - "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", - "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", - "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", - "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", - "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", - "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", - "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", - "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", - "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", - "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", - "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", - "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", - "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", - "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", - "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", - "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", - "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", - "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", - "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", - "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", - "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", - "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", - "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", - "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", - "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", - "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", - "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", - "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", - "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", - "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", - "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", - "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", - "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", - "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", - "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", - "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", - "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", - "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", - "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", - "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", - "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", - "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", - "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", - "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", - "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", - "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", - "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", - "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", - "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.0" - }, - "google-resumable-media": { - "hashes": [ - "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93", - "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.0" - }, - "googleapis-common-protos": { - "hashes": [ - "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44", - "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f" - ], - "markers": "python_version >= '3.7'", - "version": "==1.59.0" - }, - "grpcio": { - "hashes": [ - "sha256:02000b005bc8b72ff50c477b6431e8886b29961159e8b8d03c00b3dd9139baed", - "sha256:031bbd26656e0739e4b2c81c172155fb26e274b8d0312d67aefc730bcba915b6", - "sha256:1209d6b002b26e939e4c8ea37a3d5b4028eb9555394ea69fb1adbd4b61a10bb8", - "sha256:125ed35aa3868efa82eabffece6264bf638cfdc9f0cd58ddb17936684aafd0f8", - "sha256:1382bc499af92901c2240c4d540c74eae8a671e4fe9839bfeefdfcc3a106b5e2", - "sha256:16bca8092dd994f2864fdab278ae052fad4913f36f35238b2dd11af2d55a87db", - "sha256:1c59d899ee7160638613a452f9a4931de22623e7ba17897d8e3e348c2e9d8d0b", - "sha256:1d109df30641d050e009105f9c9ca5a35d01e34d2ee2a4e9c0984d392fd6d704", - "sha256:1fa7d6ddd33abbd3c8b3d7d07c56c40ea3d1891ce3cd2aa9fa73105ed5331866", - "sha256:21c4a1aae861748d6393a3ff7867473996c139a77f90326d9f4104bebb22d8b8", - "sha256:224166f06ccdaf884bf35690bf4272997c1405de3035d61384ccb5b25a4c1ca8", - "sha256:2262bd3512ba9e9f0e91d287393df6f33c18999317de45629b7bd46c40f16ba9", - "sha256:2585b3c294631a39b33f9f967a59b0fad23b1a71a212eba6bc1e3ca6e6eec9ee", - "sha256:27fb030a4589d2536daec5ff5ba2a128f4f155149efab578fe2de2cb21596d3d", - "sha256:30fbbce11ffeb4f9f91c13fe04899aaf3e9a81708bedf267bf447596b95df26b", - "sha256:3930669c9e6f08a2eed824738c3d5699d11cd47a0ecc13b68ed11595710b1133", - "sha256:3b170e441e91e4f321e46d3cc95a01cb307a4596da54aca59eb78ab0fc03754d", - "sha256:3db71c6f1ab688d8dfc102271cedc9828beac335a3a4372ec54b8bf11b43fd29", - "sha256:48cb7af77238ba16c77879009003f6b22c23425e5ee59cb2c4c103ec040638a5", - "sha256:49eace8ea55fbc42c733defbda1e4feb6d3844ecd875b01bb8b923709e0f5ec8", - "sha256:533eaf5b2a79a3c6f35cbd6a095ae99cac7f4f9c0e08bdcf86c130efd3c32adf", - "sha256:5942a3e05630e1ef5b7b5752e5da6582460a2e4431dae603de89fc45f9ec5aa9", - "sha256:62117486460c83acd3b5d85c12edd5fe20a374630475388cfc89829831d3eb79", - "sha256:650f5f2c9ab1275b4006707411bb6d6bc927886874a287661c3c6f332d4c068b", - "sha256:6dc1e2c9ac292c9a484ef900c568ccb2d6b4dfe26dfa0163d5bc815bb836c78d", - "sha256:73c238ef6e4b64272df7eec976bb016c73d3ab5a6c7e9cd906ab700523d312f3", - "sha256:775a2f70501370e5ba54e1ee3464413bff9bd85bd9a0b25c989698c44a6fb52f", - "sha256:860fcd6db7dce80d0a673a1cc898ce6bc3d4783d195bbe0e911bf8a62c93ff3f", - "sha256:87f47bf9520bba4083d65ab911f8f4c0ac3efa8241993edd74c8dd08ae87552f", - "sha256:960b176e0bb2b4afeaa1cd2002db1e82ae54c9b6e27ea93570a42316524e77cf", - "sha256:a7caf553ccaf715ec05b28c9b2ab2ee3fdb4036626d779aa09cf7cbf54b71445", - "sha256:a947d5298a0bbdd4d15671024bf33e2b7da79a70de600ed29ba7e0fef0539ebb", - "sha256:a97b0d01ae595c997c1d9d8249e2d2da829c2d8a4bdc29bb8f76c11a94915c9a", - "sha256:b7655f809e3420f80ce3bf89737169a9dce73238af594049754a1128132c0da4", - "sha256:c33744d0d1a7322da445c0fe726ea6d4e3ef2dfb0539eadf23dce366f52f546c", - "sha256:c55a9cf5cba80fb88c850915c865b8ed78d5e46e1f2ec1b27692f3eaaf0dca7e", - "sha256:d2f62fb1c914a038921677cfa536d645cb80e3dd07dc4859a3c92d75407b90a5", - "sha256:d8ae6e0df3a608e99ee1acafaafd7db0830106394d54571c1ece57f650124ce9", - "sha256:e355ee9da9c1c03f174efea59292b17a95e0b7b4d7d2a389265f731a9887d5a9", - "sha256:e3e526062c690517b42bba66ffe38aaf8bc99a180a78212e7b22baa86902f690", - "sha256:eb0807323572642ab73fd86fe53d88d843ce617dd1ddf430351ad0759809a0ae", - "sha256:ebff0738be0499d7db74d20dca9f22a7b27deae31e1bf92ea44924fd69eb6251", - "sha256:ed36e854449ff6c2f8ee145f94851fe171298e1e793f44d4f672c4a0d78064e7", - "sha256:ed3d458ded32ff3a58f157b60cc140c88f7ac8c506a1c567b2a9ee8a2fd2ce54", - "sha256:f4a7dca8ccd8023d916b900aa3c626f1bd181bd5b70159479b142f957ff420e4" - ], - "markers": "python_version >= '3.7'", - "version": "==1.54.0" - }, - "grpcio-status": { - "hashes": [ - "sha256:96968314e0c8576b2b631be3917c665964c8018900cb980d58a736fbff828578", - "sha256:b50305d52c0df6169493cca5f2e39b9b4d773b3f30d4a7a6b6dd7c18cb89007c" - ], - "version": "==1.54.0" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "importlib-metadata": { - "hashes": [ - "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed", - "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705" - ], - "markers": "python_version < '3.8'", - "version": "==6.6.0" - }, - "iniconfig": { - "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" - }, - "packaging": { - "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1" - }, - "pluggy": { - "hashes": [ - "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", - "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" - }, - "proto-plus": { - "hashes": [ - "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165", - "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d" - ], - "markers": "python_version >= '3.6'", - "version": "==1.22.2" - }, - "protobuf": { - "hashes": [ - "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7", - "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c", - "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2", - "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b", - "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050", - "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9", - "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7", - "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454", - "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480", - "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469", - "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c", - "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e", - "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db", - "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905", - "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b", - "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86", - "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4", - "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402", - "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7", - "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4", - "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99", - "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee" - ], - "markers": "python_version >= '3.7'", - "version": "==3.20.3" - }, - "pyasn1": { - "hashes": [ - "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57", - "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.5.0" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", - "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.3.0" - }, - "pytest": { - "hashes": [ - "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362", - "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3" - ], - "index": "pypi", - "version": "==7.3.1" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "requests": { - "hashes": [ - "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294", - "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4" - ], - "markers": "python_version >= '3.7'", - "version": "==2.30.0" - }, - "rsa": { - "hashes": [ - "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", - "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" - ], - "markers": "python_version >= '3.6'", - "version": "==4.9" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11'", - "version": "==2.0.1" - }, - "typing-extensions": { - "hashes": [ - "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", - "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" - ], - "markers": "python_version < '3.9'", - "version": "==4.5.0" - }, - "urllib3": { - "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" - }, - "zipp": { - "hashes": [ - "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", - "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556" - ], - "markers": "python_version < '3.10'", - "version": "==3.15.0" - } - } -} diff --git a/components/bigquery-components/poetry.lock b/components/bigquery-components/poetry.lock new file mode 100644 index 00000000..9a846c08 --- /dev/null +++ b/components/bigquery-components/poetry.lock @@ -0,0 +1,1772 @@ +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. + +[[package]] +name = "absl-py" +version = "1.4.0" +description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "absl-py-1.4.0.tar.gz", hash = "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"}, + {file = "absl_py-1.4.0-py3-none-any.whl", hash = "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47"}, +] + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "cachetools" +version = "5.3.0" +description = "Extensible memoizing collections and decorators" +category = "main" +optional = false +python-versions = "~=3.7" +files = [ + {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, + {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, +] + +[[package]] +name = "certifi" +version = "2023.5.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, +] + +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "cloudpickle" +version = "2.2.1" +description = "Extended pickling support for Python objects" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cloudpickle-2.2.1-py3-none-any.whl", hash = "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f"}, + {file = "cloudpickle-2.2.1.tar.gz", hash = "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "deprecated" +version = "1.2.13" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, + {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] + +[[package]] +name = "docstring-parser" +version = "0.15" +description = "Parse Python docstrings in reST, Google and Numpydoc format" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "docstring_parser-0.15-py3-none-any.whl", hash = "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9"}, + {file = "docstring_parser-0.15.tar.gz", hash = "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.12.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, + {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "fire" +version = "0.5.0" +description = "A library for automatically generating command line interfaces." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "fire-0.5.0.tar.gz", hash = "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6"}, +] + +[package.dependencies] +six = "*" +termcolor = "*" + +[[package]] +name = "google-api-core" +version = "2.11.0" +description = "Google API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.11.0.tar.gz", hash = "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22"}, + {file = "google_api_core-2.11.0-py3-none-any.whl", hash = "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0dev" +googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)", "grpcio-status (>=1.49.1,<2.0dev)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] + +[[package]] +name = "google-api-python-client" +version = "1.12.11" +description = "Google API Client Library for Python" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +files = [ + {file = "google-api-python-client-1.12.11.tar.gz", hash = "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9"}, + {file = "google_api_python_client-1.12.11-py2.py3-none-any.whl", hash = "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.21.0,<3dev", markers = "python_version >= \"3\""} +google-auth = {version = ">=1.16.0,<3dev", markers = "python_version >= \"3\""} +google-auth-httplib2 = ">=0.0.3" +httplib2 = ">=0.15.0,<1dev" +six = ">=1.13.0,<2dev" +uritemplate = ">=3.0.0,<4dev" + +[[package]] +name = "google-auth" +version = "2.18.0" +description = "Google Authentication Library" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +files = [ + {file = "google-auth-2.18.0.tar.gz", hash = "sha256:c66b488a8b005b23ccb97b1198b6cece516c91869091ac5b7c267422db2733c7"}, + {file = "google_auth-2.18.0-py2.py3-none-any.whl", hash = "sha256:ef3f3a67fa54d421a1c155864570f9a8de9179cedc937bda496b7a8ca338e936"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} +six = ">=1.9.0" +urllib3 = "<2.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0dev)"] + +[[package]] +name = "google-auth-httplib2" +version = "0.1.0" +description = "Google Authentication Library: httplib2 transport" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, + {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, +] + +[package.dependencies] +google-auth = "*" +httplib2 = ">=0.15.0" +six = "*" + +[[package]] +name = "google-cloud-aiplatform" +version = "1.24.1" +description = "Vertex AI API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-aiplatform-1.24.1.tar.gz", hash = "sha256:0ce9e97bf5c977397e52b3b7c4dc78610c135fbde11a60a6c0b77a4fdf776400"}, + {file = "google_cloud_aiplatform-1.24.1-py2.py3-none-any.whl", hash = "sha256:942765a6bad97e46e262dd6599dc5f171663ce952130e0b0b2eb97e0b1b98bfd"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-bigquery = ">=1.15.0,<4.0.0dev" +google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" +google-cloud-storage = ">=1.32.0,<3.0.0dev" +packaging = ">=14.3" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +shapely = "<2.0.0" + +[package.extras] +autologging = ["mlflow (>=1.27.0,<=2.1.1)"] +cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] +datasets = ["pyarrow (>=3.0.0,<8.0dev)"] +endpoint = ["requests (>=2.28.1)"] +full = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] +lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"] +metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"] +pipelines = ["pyyaml (>=5.3,<6)"] +prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<0.76.0)", "starlette (>=0.17.1)", "uvicorn[standard] (>=0.16.0)"] +private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] +tensorboard = ["tensorflow (>=2.3.0,<3.0.0dev)"] +testing = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "grpcio-testing", "ipython", "kfp", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "scikit-learn", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] +vizier = ["google-vizier (==0.0.4)"] +xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] + +[[package]] +name = "google-cloud-bigquery" +version = "2.30.0" +description = "Google BigQuery API client library" +category = "main" +optional = false +python-versions = ">=3.6, <3.11" +files = [ + {file = "google-cloud-bigquery-2.30.0.tar.gz", hash = "sha256:fdb57aa5e8af7d692eb5835739d9339dc1b5e89836430fe88dd1ddc1b0047639"}, + {file = "google_cloud_bigquery-2.30.0-py2.py3-none-any.whl", hash = "sha256:f6546ee96d57f45eaac8c4b24b52228a7f21a33669730efc9e69569bc88a04ad"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.29.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-core = ">=1.4.1,<3.0.0dev" +google-resumable-media = ">=0.6.0,<3.0dev" +grpcio = ">=1.38.1,<2.0dev" +packaging = ">=14.3" +proto-plus = ">=1.10.0" +protobuf = ">=3.12.0" +python-dateutil = ">=2.7.2,<3.0dev" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +all = ["Shapely (>=1.6.0,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.0.0,<3.0.0dev)", "grpcio (>=1.38.1,<2.0dev)", "opentelemetry-api (>=0.11b0)", "opentelemetry-instrumentation (>=0.11b0)", "opentelemetry-sdk (>=0.11b0)", "pandas (>=0.24.2)", "pyarrow (>=3.0.0,<7.0dev)", "tqdm (>=4.7.4,<5.0.0dev)"] +bignumeric-type = ["pyarrow (>=3.0.0,<7.0dev)"] +bqstorage = ["google-cloud-bigquery-storage (>=2.0.0,<3.0.0dev)", "grpcio (>=1.38.1,<2.0dev)", "pyarrow (>=3.0.0,<7.0dev)"] +geopandas = ["Shapely (>=1.6.0,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)"] +opentelemetry = ["opentelemetry-api (>=0.11b0)", "opentelemetry-instrumentation (>=0.11b0)", "opentelemetry-sdk (>=0.11b0)"] +pandas = ["pandas (>=0.24.2)", "pyarrow (>=3.0.0,<7.0dev)"] +tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] + +[[package]] +name = "google-cloud-core" +version = "2.3.2" +description = "Google Cloud API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-core-2.3.2.tar.gz", hash = "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a"}, + {file = "google_cloud_core-2.3.2-py2.py3-none-any.whl", hash = "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe"}, +] + +[package.dependencies] +google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" + +[package.extras] +grpc = ["grpcio (>=1.38.0,<2.0dev)"] + +[[package]] +name = "google-cloud-notebooks" +version = "1.7.0" +description = "Google Cloud Notebooks API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-notebooks-1.7.0.tar.gz", hash = "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e"}, + {file = "google_cloud_notebooks-1.7.0-py2.py3-none-any.whl", hash = "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-pipeline-components" +version = "1.0.42" +description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "google_cloud_pipeline_components-1.0.42-py3-none-any.whl", hash = "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev" +google-cloud-aiplatform = ">=1.14.0,<2" +google-cloud-notebooks = ">=0.4.0" +google-cloud-storage = ">=2.2.1,<3" +googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio-status = "<=1.47.0" +kfp = ">=1.8.9,<2.0.0" +protobuf = ">=3.19.0,<4.0.0dev" + +[package.extras] +tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev)", "google-cloud-aiplatform (>=1.14.0,<2)", "google-cloud-notebooks (>=0.4.0)", "google-cloud-storage (>=2.2.1,<3)", "googleapis-common-protos (>=1.56.2,<2.0dev)", "grpcio-status (<=1.47.0)", "kfp (>=1.8.9,<2.0.0)", "mock (>=4.0.0)", "protobuf (>=3.19.0,<4.0.0dev)", "pytest (>=6.0.0)"] + +[[package]] +name = "google-cloud-resource-manager" +version = "1.10.0" +description = "Google Cloud Resource Manager API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-resource-manager-1.10.0.tar.gz", hash = "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287"}, + {file = "google_cloud_resource_manager-1.10.0-py2.py3-none-any.whl", hash = "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-storage" +version = "2.9.0" +description = "Google Cloud Storage API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-storage-2.9.0.tar.gz", hash = "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3"}, + {file = "google_cloud_storage-2.9.0-py2.py3-none-any.whl", hash = "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" +google-cloud-core = ">=2.3.0,<3.0dev" +google-resumable-media = ">=2.3.2" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +protobuf = ["protobuf (<5.0.0dev)"] + +[[package]] +name = "google-crc32c" +version = "1.5.0" +description = "A python wrapper of the C library 'Google CRC32C'" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win32.whl", hash = "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win32.whl", hash = "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win32.whl", hash = "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win32.whl", hash = "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win32.whl", hash = "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93"}, +] + +[package.extras] +testing = ["pytest"] + +[[package]] +name = "google-resumable-media" +version = "2.5.0" +description = "Utilities for Google Media Downloads and Resumable Uploads" +category = "main" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "google-resumable-media-2.5.0.tar.gz", hash = "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93"}, + {file = "google_resumable_media-2.5.0-py2.py3-none-any.whl", hash = "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec"}, +] + +[package.dependencies] +google-crc32c = ">=1.0,<2.0dev" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] +requests = ["requests (>=2.18.0,<3.0.0dev)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.59.0" +description = "Common protobufs used in Google APIs" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.59.0.tar.gz", hash = "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44"}, + {file = "googleapis_common_protos-1.59.0-py2.py3-none-any.whl", hash = "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.12.6" +description = "IAM API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.12.6.tar.gz", hash = "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f"}, + {file = "grpc_google_iam_v1-0.12.6-py2.py3-none-any.whl", hash = "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.54.2" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.54.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c"}, + {file = "grpcio-1.54.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c0e3155fc5335ec7b3b70f15230234e529ca3607b20a562b6c75fb1b1218874c"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bf88004fe086c786dc56ef8dd6cb49c026833fdd6f42cb853008bce3f907148"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be88c081e33f20630ac3343d8ad9f1125f32987968e9c8c75c051c9800896e8"}, + {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:33d40954199bddbb6a78f8f6f2b2082660f381cd2583ec860a6c2fa7c8400c08"}, + {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b52d00d1793d290c81ad6a27058f5224a7d5f527867e5b580742e1bd211afeee"}, + {file = "grpcio-1.54.2-cp310-cp310-win32.whl", hash = "sha256:881d058c5ccbea7cc2c92085a11947b572498a27ef37d3eef4887f499054dca8"}, + {file = "grpcio-1.54.2-cp310-cp310-win_amd64.whl", hash = "sha256:0212e2f7fdf7592e4b9d365087da30cb4d71e16a6f213120c89b4f8fb35a3ab3"}, + {file = "grpcio-1.54.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:1e623e0cf99a0ac114f091b3083a1848dbc64b0b99e181473b5a4a68d4f6f821"}, + {file = "grpcio-1.54.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:66233ccd2a9371158d96e05d082043d47dadb18cbb294dc5accfdafc2e6b02a7"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:4cb283f630624ebb16c834e5ac3d7880831b07cbe76cb08ab7a271eeaeb8943e"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a1e601ee31ef30a9e2c601d0867e236ac54c922d32ed9f727b70dd5d82600d5"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8da84bbc61a4e92af54dc96344f328e5822d574f767e9b08e1602bb5ddc254a"}, + {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5008964885e8d23313c8e5ea0d44433be9bfd7e24482574e8cc43c02c02fc796"}, + {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2f5a1f1080ccdc7cbaf1171b2cf384d852496fe81ddedeb882d42b85727f610"}, + {file = "grpcio-1.54.2-cp311-cp311-win32.whl", hash = "sha256:b74ae837368cfffeb3f6b498688a123e6b960951be4dec0e869de77e7fa0439e"}, + {file = "grpcio-1.54.2-cp311-cp311-win_amd64.whl", hash = "sha256:8cdbcbd687e576d48f7886157c95052825ca9948c0ed2afdc0134305067be88b"}, + {file = "grpcio-1.54.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:782f4f8662a2157c4190d0f99eaaebc602899e84fb1e562a944e5025929e351c"}, + {file = "grpcio-1.54.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:714242ad0afa63a2e6dabd522ae22e1d76e07060b5af2ddda5474ba4f14c2c94"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:f900ed4ad7a0f1f05d35f955e0943944d5a75f607a836958c6b8ab2a81730ef2"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96a41817d2c763b1d0b32675abeb9179aa2371c72aefdf74b2d2b99a1b92417b"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70fcac7b94f4c904152809a050164650ac81c08e62c27aa9f156ac518029ebbe"}, + {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fd6c6c29717724acf9fc1847c4515d57e4dc12762452457b9cb37461f30a81bb"}, + {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c2392f5b5d84b71d853918687d806c1aa4308109e5ca158a16e16a6be71041eb"}, + {file = "grpcio-1.54.2-cp37-cp37m-win_amd64.whl", hash = "sha256:51630c92591d6d3fe488a7c706bd30a61594d144bac7dee20c8e1ce78294f474"}, + {file = "grpcio-1.54.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:b04202453941a63b36876a7172b45366dc0cde10d5fd7855c0f4a4e673c0357a"}, + {file = "grpcio-1.54.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:89dde0ac72a858a44a2feb8e43dc68c0c66f7857a23f806e81e1b7cc7044c9cf"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:09d4bfd84686cd36fd11fd45a0732c7628308d094b14d28ea74a81db0bce2ed3"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fc2b4edb938c8faa4b3c3ea90ca0dd89b7565a049e8e4e11b77e60e4ed2cc05"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61f7203e2767800edee7a1e1040aaaf124a35ce0c7fe0883965c6b762defe598"}, + {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e416c8baf925b5a1aff31f7f5aecc0060b25d50cce3a5a7255dc5cf2f1d4e5eb"}, + {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dc80c9c6b608bf98066a038e0172013a49cfa9a08d53335aefefda2c64fc68f4"}, + {file = "grpcio-1.54.2-cp38-cp38-win32.whl", hash = "sha256:8d6192c37a30a115f4663592861f50e130caed33efc4eec24d92ec881c92d771"}, + {file = "grpcio-1.54.2-cp38-cp38-win_amd64.whl", hash = "sha256:46a057329938b08e5f0e12ea3d7aed3ecb20a0c34c4a324ef34e00cecdb88a12"}, + {file = "grpcio-1.54.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:2296356b5c9605b73ed6a52660b538787094dae13786ba53080595d52df13a98"}, + {file = "grpcio-1.54.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:c72956972e4b508dd39fdc7646637a791a9665b478e768ffa5f4fe42123d5de1"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:9bdbb7624d65dc0ed2ed8e954e79ab1724526f09b1efa88dcd9a1815bf28be5f"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c44e1a765b31e175c391f22e8fc73b2a2ece0e5e6ff042743d8109b5d2eff9f"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cc928cfe6c360c1df636cf7991ab96f059666ac7b40b75a769410cc6217df9c"}, + {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a08920fa1a97d4b8ee5db2f31195de4a9def1a91bc003544eb3c9e6b8977960a"}, + {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4864f99aac207e3e45c5e26c6cbb0ad82917869abc2f156283be86c05286485c"}, + {file = "grpcio-1.54.2-cp39-cp39-win32.whl", hash = "sha256:b38b3de8cff5bc70f8f9c615f51b48eff7313fc9aca354f09f81b73036e7ddfa"}, + {file = "grpcio-1.54.2-cp39-cp39-win_amd64.whl", hash = "sha256:be48496b0e00460717225e7680de57c38be1d8629dc09dadcd1b3389d70d942b"}, + {file = "grpcio-1.54.2.tar.gz", hash = "sha256:50a9f075eeda5097aa9a182bb3877fe1272875e45370368ac0ee16ab9e22d019"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.54.2)"] + +[[package]] +name = "grpcio-status" +version = "1.47.0" +description = "Status proto mapping for gRPC" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.47.0.tar.gz", hash = "sha256:c9ce3213e84c6fd8801c31aca3ea4a6b3453eaa40b93a6c0a23ea8999808fa00"}, + {file = "grpcio_status-1.47.0-py3-none-any.whl", hash = "sha256:2154fdb8aad20452488712be6879657b508115ca06139fde8897ea8e9bc79367"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.47.0" +protobuf = ">=3.12.0" + +[[package]] +name = "httplib2" +version = "0.22.0" +description = "A comprehensive HTTP client library." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, + {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, +] + +[package.dependencies] +pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} + +[[package]] +name = "identify" +version = "2.5.24" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, + {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "kfp" +version = "1.8.21" +description = "KubeFlow Pipelines SDK" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "kfp-1.8.21.tar.gz", hash = "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c"}, +] + +[package.dependencies] +absl-py = ">=0.9,<2" +click = ">=7.1.2,<9" +cloudpickle = ">=2.0.0,<3" +Deprecated = ">=1.2.7,<2" +docstring-parser = ">=0.7.3,<1" +fire = ">=0.3.1,<1" +google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-python-client = ">=1.7.8,<2" +google-auth = ">=1.6.1,<3" +google-cloud-storage = ">=1.20.0,<3" +jsonschema = ">=3.0.1,<5" +kfp-pipeline-spec = ">=0.1.16,<0.2.0" +kfp-server-api = ">=1.1.2,<2.0.0" +kubernetes = ">=8.0.0,<26" +protobuf = ">=3.13.0,<4" +pydantic = ">=1.8.2,<2" +PyYAML = ">=5.3,<6" +requests-toolbelt = ">=0.8.0,<1" +strip-hints = ">=0.1.8,<1" +tabulate = ">=0.8.6,<1" +typer = ">=0.3.2,<1.0" +uritemplate = ">=3.0.1,<4" +urllib3 = "<2" + +[package.extras] +all = ["docker"] + +[[package]] +name = "kfp-pipeline-spec" +version = "0.1.16" +description = "Kubeflow Pipelines pipeline spec" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "kfp_pipeline_spec-0.1.16-py3-none-any.whl", hash = "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25"}, +] + +[package.dependencies] +protobuf = ">=3.13.0,<4" + +[[package]] +name = "kfp-server-api" +version = "1.8.5" +description = "Kubeflow Pipelines API" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "kfp-server-api-1.8.5.tar.gz", hash = "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de"}, +] + +[package.dependencies] +certifi = "*" +python-dateutil = "*" +six = ">=1.10" +urllib3 = ">=1.15" + +[[package]] +name = "kubernetes" +version = "25.3.0" +description = "Kubernetes python client" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "kubernetes-25.3.0-py2.py3-none-any.whl", hash = "sha256:eb42333dad0bb5caf4e66460c6a4a1a36f0f057a040f35018f6c05a699baed86"}, + {file = "kubernetes-25.3.0.tar.gz", hash = "sha256:213befbb4e5aed95f94950c7eed0c2322fc5a2f8f40932e58d28fdd42d90836c"}, +] + +[package.dependencies] +certifi = ">=14.05.14" +google-auth = ">=1.0.1" +python-dateutil = ">=2.5.3" +pyyaml = ">=5.4.1" +requests = "*" +requests-oauthlib = "*" +setuptools = ">=21.0.0" +six = ">=1.9.0" +urllib3 = ">=1.24.2" +websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" + +[package.extras] +adal = ["adal (>=1.0.2)"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "platformdirs" +version = "3.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.21.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "proto-plus" +version = "1.22.2" +description = "Beautiful, Pythonic protocol buffers." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.22.2.tar.gz", hash = "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165"}, + {file = "proto_plus-1.22.2-py3-none-any.whl", hash = "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "3.20.3" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, + {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, + {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, + {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, + {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, + {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, + {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, + {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, + {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, + {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, + {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, + {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, + {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, + {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, + {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, + {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.0-py2.py3-none-any.whl", hash = "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57"}, + {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pydantic" +version = "1.10.7" +description = "Data validation and settings management using python type hints" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, + {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, + {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, + {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, + {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, + {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, + {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, + {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, + {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, + {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrsistent" +version = "0.19.3" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, + {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, + {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, + {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, + {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, + {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, +] + +[[package]] +name = "pytest" +version = "7.3.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "5.4.1" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, +] + +[[package]] +name = "requests" +version = "2.30.0" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.30.0-py3-none-any.whl", hash = "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294"}, + {file = "requests-2.30.0.tar.gz", hash = "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "requests-toolbelt" +version = "0.10.1" +description = "A utility belt for advanced users of python-requests" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"}, + {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +category = "main" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "setuptools" +version = "67.7.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, + {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "shapely" +version = "1.8.5.post1" +description = "Geometric objects, predicates, and operations" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d048f93e42ba578b82758c15d8ae037d08e69d91d9872bca5a1895b118f4e2b0"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99ab0ddc05e44acabdbe657c599fdb9b2d82e86c5493bdae216c0c4018a82dee"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a2f0da0109e81e0c101a2b4cd8412f73f5f299e7b5b2deaf64cd2a100ac118"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6fe855e7d45685926b6ba00aaeb5eba5862611f7465775dacd527e081a8ced6d"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec14ceca36f67cb48b34d02d7f65a9acae15cd72b48e303531893ba4a960f3ea"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a2b2a65fa7f97115c1cd989fe9d6f39281ca2a8a014f1d4904c1a6e34d7f25"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-win32.whl", hash = "sha256:21776184516a16bf82a0c3d6d6a312b3cd15a4cabafc61ee01cf2714a82e8396"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-win_amd64.whl", hash = "sha256:a354199219c8d836f280b88f2c5102c81bb044ccea45bd361dc38a79f3873714"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:783bad5f48e2708a0e2f695a34ed382e4162c795cb2f0368b39528ac1d6db7ed"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a23ef3882d6aa203dd3623a3d55d698f59bfbd9f8a3bfed52c2da05a7f0f8640"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab38f7b5196ace05725e407cb8cab9ff66edb8e6f7bb36a398e8f73f52a7aaa2"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d086591f744be483b34628b391d741e46f2645fe37594319e0a673cc2c26bcf"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4728666fff8cccc65a07448cae72c75a8773fea061c3f4f139c44adc429b18c3"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-win32.whl", hash = "sha256:84010db15eb364a52b74ea8804ef92a6a930dfc1981d17a369444b6ddec66efd"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-win_amd64.whl", hash = "sha256:48dcfffb9e225c0481120f4bdf622131c8c95f342b00b158cdbe220edbbe20b6"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2fd15397638df291c427a53d641d3e6fd60458128029c8c4f487190473a69a91"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a74631e511153366c6dbe3229fa93f877e3c87ea8369cd00f1d38c76b0ed9ace"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:66bdac74fbd1d3458fa787191a90fa0ae610f09e2a5ec398c36f968cc0ed743f"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-win32.whl", hash = "sha256:6d388c0c1bd878ed1af4583695690aa52234b02ed35f93a1c8486ff52a555838"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:be9423d5a3577ac2e92c7e758bd8a2b205f5e51a012177a590bc46fc51eb4834"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5d7f85c2d35d39ff53c9216bc76b7641c52326f7e09aaad1789a3611a0f812f2"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:adcf8a11b98af9375e32bff91de184f33a68dc48b9cb9becad4f132fa25cfa3c"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:753ed0e21ab108bd4282405b9b659f2e985e8502b1a72b978eaa51d3496dee19"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-win32.whl", hash = "sha256:65b21243d8f6bcd421210daf1fabb9de84de2c04353c5b026173b88d17c1a581"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:370b574c78dc5af3a198a6da5d9b3d7c04654bd2ef7e80e80a3a0992dfb2d9cd"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:532a55ee2a6c52d23d6f7d1567c8f0473635f3b270262c44e1b0c88096827e22"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3480657460e939f45a7d359ef0e172a081f249312557fe9aa78c4fd3a362d993"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b65f5d530ba91e49ffc7c589255e878d2506a8b96ffce69d3b7c4500a9a9eaf8"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:147066da0be41b147a61f8eb805dea3b13709dbc873a431ccd7306e24d712bc0"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2822111ddc5bcfb116e6c663e403579d0fe3f147d2a97426011a191c43a7458"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b47bb6f9369e8bf3e6dbd33e6a25a47ee02b2874792a529fe04a49bf8bc0df6"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-win32.whl", hash = "sha256:2e0a8c2e55f1be1312b51c92b06462ea89e6bb703fab4b114e7a846d941cfc40"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-win_amd64.whl", hash = "sha256:0d885cb0cf670c1c834df3f371de8726efdf711f18e2a75da5cfa82843a7ab65"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0b4ee3132ee90f07d63db3aea316c4c065ed7a26231458dda0874414a09d6ba3"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:02dd5d7dc6e46515d88874134dc8fcdc65826bca93c3eecee59d1910c42c1b17"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6a9a4a31cd6e86d0fbe8473ceed83d4fe760b19d949fb557ef668defafea0f6"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:38f0fbbcb8ca20c16451c966c1f527cc43968e121c8a048af19ed3e339a921cd"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78fb9d929b8ee15cfd424b6c10879ce1907f24e05fb83310fc47d2cd27088e40"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89164e7a9776a19e29f01369a98529321994e2e4d852b92b7e01d4d9804c55bf"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-win32.whl", hash = "sha256:8e59817b0fe63d34baedaabba8c393c0090f061917d18fc0bcc2f621937a8f73"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-win_amd64.whl", hash = "sha256:e9c30b311de2513555ab02464ebb76115d242842b29c412f5a9aa0cac57be9f6"}, + {file = "Shapely-1.8.5.post1.tar.gz", hash = "sha256:ef3be705c3eac282a28058e6c6e5503419b250f482320df2172abcbea642c831"}, +] + +[package.extras] +all = ["numpy", "pytest", "pytest-cov"] +test = ["pytest", "pytest-cov"] +vectorized = ["numpy"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "strip-hints" +version = "0.1.10" +description = "Function and command-line program to strip Python type hints." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "strip-hints-0.1.10.tar.gz", hash = "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f"}, +] + +[package.dependencies] +wheel = "*" + +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "termcolor" +version = "2.3.0" +description = "ANSI color formatting for output in terminal" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, + {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typer" +version = "0.9.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.5.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, +] + +[[package]] +name = "uritemplate" +version = "3.0.1" +description = "URI templates" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, + {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, +] + +[[package]] +name = "urllib3" +version = "1.26.15" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, + {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.23.0" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, + {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, +] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.11,<4" +platformdirs = ">=3.2,<4" + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] + +[[package]] +name = "websocket-client" +version = "1.5.1" +description = "WebSocket client for Python with low level API options" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websocket-client-1.5.1.tar.gz", hash = "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40"}, + {file = "websocket_client-1.5.1-py3-none-any.whl", hash = "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"}, +] + +[package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "wheel" +version = "0.40.0" +description = "A built-package format for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, + {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, +] + +[package.extras] +test = ["pytest (>=6.0.0)"] + +[[package]] +name = "wrapt" +version = "1.15.0" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, + {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, + {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, + {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, + {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, + {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, + {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, + {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, + {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, + {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, + {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, + {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, + {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, + {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, + {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, + {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, + {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, + {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, + {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.9,<3.11" +content-hash = "1f75fb78ca2fd965417d30dd7715e4c282abcd1b53703654e5c022f4d2e79328" diff --git a/components/bigquery-components/pyproject.toml b/components/bigquery-components/pyproject.toml index 0ea3c6dc..083448b2 100644 --- a/components/bigquery-components/pyproject.toml +++ b/components/bigquery-components/pyproject.toml @@ -1,30 +1,30 @@ -[project] +[tool.poetry] name = "bigquery-components" -authors = [ - {name = "Example User", email = "user@example.com"}, -] -description = "BigQuery components" +version = "0.1.0" +authors = ["roberta nwokonko "] +description = "BigQuery components created by Datatonic" readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.7", -] -requires-python = ">=3.7" -dynamic = ["version"] -dependencies = [ - "kfp == 1.8.21", + "Programming Language :: Python :: 3.9", ] -[project.optional-dependencies] -tests = [ - "google-cloud-bigquery == 2.30.0", - "pytest >= 7.3.1,<8.0.0", -] +[tool.poetry.dependencies] +python = ">=3.9,<3.11" +Jinja2 = ">=3.0.1,<4.0.0" +google-cloud-aiplatform = "1.24.1" +google-cloud-pipeline-components = "1.0.42" +google-cloud-bigquery = "2.30.0" +kfp = "==1.8.21" + +[tool.poetry.group.dev.dependencies] +pytest = ">=7.3.1,<8.0.0" +pre-commit = ">=2.14.1,<3.0.0" [build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta:__legacy__" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" [tool.flake8] max-line-length = 88 diff --git a/components/vertex-components/Pipfile b/components/vertex-components/Pipfile deleted file mode 100644 index 6de5c5ef..00000000 --- a/components/vertex-components/Pipfile +++ /dev/null @@ -1,16 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -kfp = "==1.8.21" - -[dev-packages] -google-cloud-aiplatform = "==1.24.1" -google-cloud-pipeline-components = "==1.0.42" -pytest = ">=7.3.1,<8.0.0" -pre-commit = ">=2.14.1,<3.0.0" - -[requires] -python_version = "3.7" diff --git a/components/vertex-components/Pipfile.lock b/components/vertex-components/Pipfile.lock deleted file mode 100644 index e275cc15..00000000 --- a/components/vertex-components/Pipfile.lock +++ /dev/null @@ -1,1792 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "42000dc26266b6808ea2b6a7261983f236b59a8966b57835907a9b134fbef804" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.7" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "absl-py": { - "hashes": [ - "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47", - "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d" - ], - "markers": "python_version >= '3.6'", - "version": "==1.4.0" - }, - "attrs": { - "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" - }, - "cachetools": { - "hashes": [ - "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14", - "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4" - ], - "markers": "python_version ~= '3.7'", - "version": "==5.3.0" - }, - "certifi": { - "hashes": [ - "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", - "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.5.7" - }, - "charset-normalizer": { - "hashes": [ - "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", - "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", - "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", - "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", - "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", - "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", - "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", - "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", - "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", - "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", - "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", - "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", - "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", - "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", - "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", - "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", - "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", - "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", - "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", - "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", - "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", - "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", - "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", - "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", - "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", - "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", - "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", - "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", - "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", - "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", - "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", - "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", - "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", - "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", - "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", - "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", - "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", - "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", - "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", - "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", - "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", - "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", - "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", - "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", - "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", - "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", - "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", - "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", - "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", - "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", - "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", - "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", - "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", - "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", - "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", - "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", - "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", - "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", - "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", - "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", - "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", - "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", - "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", - "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", - "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", - "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", - "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", - "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", - "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", - "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", - "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", - "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", - "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", - "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", - "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.1.0" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "cloudpickle": { - "hashes": [ - "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f", - "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5" - ], - "markers": "python_version >= '3.6'", - "version": "==2.2.1" - }, - "deprecated": { - "hashes": [ - "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d", - "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.13" - }, - "docstring-parser": { - "hashes": [ - "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682", - "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9" - ], - "markers": "python_version >= '3.6' and python_version < '4.0'", - "version": "==0.15" - }, - "fire": { - "hashes": [ - "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6" - ], - "version": "==0.5.0" - }, - "google-api-core": { - "hashes": [ - "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22", - "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.11.0" - }, - "google-api-python-client": { - "hashes": [ - "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9", - "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.12.11" - }, - "google-auth": { - "hashes": [ - "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc", - "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.17.3" - }, - "google-auth-httplib2": { - "hashes": [ - "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10", - "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac" - ], - "version": "==0.1.0" - }, - "google-cloud-core": { - "hashes": [ - "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe", - "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.2" - }, - "google-cloud-storage": { - "hashes": [ - "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94", - "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3" - ], - "markers": "python_version >= '3.7'", - "version": "==2.9.0" - }, - "google-crc32c": { - "hashes": [ - "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", - "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", - "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", - "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", - "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", - "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", - "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", - "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", - "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", - "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", - "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", - "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", - "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", - "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", - "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", - "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", - "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", - "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", - "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", - "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", - "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", - "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", - "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", - "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", - "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", - "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", - "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", - "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", - "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", - "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", - "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", - "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", - "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", - "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", - "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", - "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", - "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", - "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", - "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", - "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", - "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", - "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", - "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", - "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", - "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", - "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", - "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", - "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", - "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", - "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", - "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", - "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", - "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", - "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", - "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", - "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", - "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", - "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", - "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", - "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", - "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", - "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", - "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", - "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", - "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", - "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", - "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", - "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.0" - }, - "google-resumable-media": { - "hashes": [ - "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93", - "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.0" - }, - "googleapis-common-protos": { - "hashes": [ - "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44", - "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f" - ], - "markers": "python_version >= '3.7'", - "version": "==1.59.0" - }, - "httplib2": { - "hashes": [ - "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", - "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.22.0" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "importlib-metadata": { - "hashes": [ - "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed", - "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705" - ], - "markers": "python_version < '3.8'", - "version": "==6.6.0" - }, - "importlib-resources": { - "hashes": [ - "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6", - "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a" - ], - "markers": "python_version < '3.9'", - "version": "==5.12.0" - }, - "jsonschema": { - "hashes": [ - "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6" - ], - "markers": "python_version >= '3.7'", - "version": "==4.17.3" - }, - "kfp": { - "hashes": [ - "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c" - ], - "index": "pypi", - "version": "==1.8.21" - }, - "kfp-pipeline-spec": { - "hashes": [ - "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==0.1.16" - }, - "kfp-server-api": { - "hashes": [ - "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de" - ], - "version": "==1.8.5" - }, - "kubernetes": { - "hashes": [ - "sha256:213befbb4e5aed95f94950c7eed0c2322fc5a2f8f40932e58d28fdd42d90836c", - "sha256:eb42333dad0bb5caf4e66460c6a4a1a36f0f057a040f35018f6c05a699baed86" - ], - "markers": "python_version >= '3.6'", - "version": "==25.3.0" - }, - "oauthlib": { - "hashes": [ - "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", - "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918" - ], - "markers": "python_version >= '3.6'", - "version": "==3.2.2" - }, - "pkgutil-resolve-name": { - "hashes": [ - "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174", - "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e" - ], - "markers": "python_version < '3.9'", - "version": "==1.3.10" - }, - "protobuf": { - "hashes": [ - "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7", - "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c", - "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2", - "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b", - "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050", - "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9", - "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7", - "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454", - "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480", - "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469", - "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c", - "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e", - "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db", - "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905", - "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b", - "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86", - "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4", - "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402", - "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7", - "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4", - "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99", - "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee" - ], - "markers": "python_version >= '3.7'", - "version": "==3.20.3" - }, - "pyasn1": { - "hashes": [ - "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57", - "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.5.0" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", - "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.3.0" - }, - "pydantic": { - "hashes": [ - "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e", - "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6", - "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd", - "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca", - "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b", - "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a", - "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245", - "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d", - "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee", - "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1", - "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3", - "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d", - "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5", - "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914", - "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd", - "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1", - "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e", - "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e", - "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a", - "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd", - "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f", - "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209", - "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d", - "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a", - "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143", - "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918", - "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52", - "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e", - "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f", - "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e", - "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb", - "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe", - "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe", - "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d", - "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209", - "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af" - ], - "markers": "python_version >= '3.7'", - "version": "==1.10.7" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_version >= '3.1'", - "version": "==3.0.9" - }, - "pyrsistent": { - "hashes": [ - "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8", - "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440", - "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a", - "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c", - "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3", - "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393", - "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9", - "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da", - "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf", - "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64", - "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a", - "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3", - "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98", - "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2", - "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8", - "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf", - "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc", - "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7", - "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28", - "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2", - "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b", - "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a", - "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64", - "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19", - "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1", - "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9", - "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c" - ], - "markers": "python_version >= '3.7'", - "version": "==0.19.3" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pyyaml": { - "hashes": [ - "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", - "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", - "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", - "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", - "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", - "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", - "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", - "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", - "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", - "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", - "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", - "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", - "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", - "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", - "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", - "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", - "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", - "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", - "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", - "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", - "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", - "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", - "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", - "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", - "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", - "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", - "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", - "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", - "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.4.1" - }, - "requests": { - "hashes": [ - "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294", - "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4" - ], - "markers": "python_version >= '3.7'", - "version": "==2.30.0" - }, - "requests-oauthlib": { - "hashes": [ - "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", - "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.3.1" - }, - "requests-toolbelt": { - "hashes": [ - "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7", - "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.1" - }, - "rsa": { - "hashes": [ - "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", - "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" - ], - "markers": "python_version >= '3.6'", - "version": "==4.9" - }, - "setuptools": { - "hashes": [ - "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", - "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" - ], - "markers": "python_version >= '3.7'", - "version": "==67.7.2" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "strip-hints": { - "hashes": [ - "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f" - ], - "version": "==0.1.10" - }, - "tabulate": { - "hashes": [ - "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", - "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" - ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" - }, - "termcolor": { - "hashes": [ - "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475", - "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.0" - }, - "typer": { - "hashes": [ - "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2", - "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee" - ], - "markers": "python_version >= '3.6'", - "version": "==0.9.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", - "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" - ], - "markers": "python_version < '3.9'", - "version": "==4.5.0" - }, - "uritemplate": { - "hashes": [ - "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f", - "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.0.1" - }, - "urllib3": { - "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" - }, - "websocket-client": { - "hashes": [ - "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40", - "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.1" - }, - "wheel": { - "hashes": [ - "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873", - "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247" - ], - "markers": "python_version >= '3.7'", - "version": "==0.40.0" - }, - "wrapt": { - "hashes": [ - "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", - "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", - "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", - "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", - "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", - "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", - "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", - "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", - "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", - "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", - "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", - "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", - "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", - "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", - "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", - "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", - "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", - "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", - "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", - "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", - "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", - "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", - "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", - "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", - "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", - "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", - "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", - "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", - "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", - "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", - "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", - "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", - "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", - "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", - "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", - "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", - "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", - "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", - "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", - "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", - "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", - "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", - "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", - "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", - "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", - "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", - "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", - "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", - "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", - "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", - "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", - "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", - "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", - "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", - "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", - "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", - "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", - "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", - "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", - "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", - "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", - "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", - "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", - "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", - "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", - "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", - "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", - "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", - "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", - "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", - "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", - "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", - "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", - "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", - "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.15.0" - }, - "zipp": { - "hashes": [ - "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", - "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556" - ], - "markers": "python_version < '3.10'", - "version": "==3.15.0" - } - }, - "develop": { - "absl-py": { - "hashes": [ - "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47", - "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d" - ], - "markers": "python_version >= '3.6'", - "version": "==1.4.0" - }, - "attrs": { - "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" - }, - "cachetools": { - "hashes": [ - "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14", - "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4" - ], - "markers": "python_version ~= '3.7'", - "version": "==5.3.0" - }, - "certifi": { - "hashes": [ - "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", - "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.5.7" - }, - "cfgv": { - "hashes": [ - "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426", - "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.3.1" - }, - "charset-normalizer": { - "hashes": [ - "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", - "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", - "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", - "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", - "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", - "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", - "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", - "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", - "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", - "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", - "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", - "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", - "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", - "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", - "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", - "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", - "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", - "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", - "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", - "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", - "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", - "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", - "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", - "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", - "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", - "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", - "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", - "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", - "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", - "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", - "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", - "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", - "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", - "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", - "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", - "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", - "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", - "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", - "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", - "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", - "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", - "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", - "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", - "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", - "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", - "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", - "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", - "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", - "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", - "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", - "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", - "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", - "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", - "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", - "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", - "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", - "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", - "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", - "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", - "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", - "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", - "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", - "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", - "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", - "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", - "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", - "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", - "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", - "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", - "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", - "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", - "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", - "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", - "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", - "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.1.0" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "cloudpickle": { - "hashes": [ - "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f", - "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5" - ], - "markers": "python_version >= '3.6'", - "version": "==2.2.1" - }, - "deprecated": { - "hashes": [ - "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d", - "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.13" - }, - "distlib": { - "hashes": [ - "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46", - "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e" - ], - "version": "==0.3.6" - }, - "docstring-parser": { - "hashes": [ - "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682", - "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9" - ], - "markers": "python_version >= '3.6' and python_version < '4.0'", - "version": "==0.15" - }, - "exceptiongroup": { - "hashes": [ - "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e", - "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785" - ], - "markers": "python_version < '3.11'", - "version": "==1.1.1" - }, - "filelock": { - "hashes": [ - "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9", - "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718" - ], - "markers": "python_version >= '3.7'", - "version": "==3.12.0" - }, - "fire": { - "hashes": [ - "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6" - ], - "version": "==0.5.0" - }, - "google-api-core": { - "hashes": [ - "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22", - "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.11.0" - }, - "google-api-python-client": { - "hashes": [ - "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9", - "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.12.11" - }, - "google-auth": { - "hashes": [ - "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc", - "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.17.3" - }, - "google-auth-httplib2": { - "hashes": [ - "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10", - "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac" - ], - "version": "==0.1.0" - }, - "google-cloud-aiplatform": { - "hashes": [ - "sha256:0ce9e97bf5c977397e52b3b7c4dc78610c135fbde11a60a6c0b77a4fdf776400", - "sha256:942765a6bad97e46e262dd6599dc5f171663ce952130e0b0b2eb97e0b1b98bfd" - ], - "index": "pypi", - "version": "==1.24.1" - }, - "google-cloud-bigquery": { - "hashes": [ - "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df", - "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a" - ], - "markers": "python_version >= '3.7'", - "version": "==3.10.0" - }, - "google-cloud-core": { - "hashes": [ - "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe", - "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.2" - }, - "google-cloud-notebooks": { - "hashes": [ - "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab", - "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e" - ], - "markers": "python_version >= '3.7'", - "version": "==1.7.0" - }, - "google-cloud-pipeline-components": { - "hashes": [ - "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623" - ], - "index": "pypi", - "version": "==1.0.42" - }, - "google-cloud-resource-manager": { - "hashes": [ - "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a", - "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287" - ], - "markers": "python_version >= '3.7'", - "version": "==1.10.0" - }, - "google-cloud-storage": { - "hashes": [ - "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94", - "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3" - ], - "markers": "python_version >= '3.7'", - "version": "==2.9.0" - }, - "google-crc32c": { - "hashes": [ - "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", - "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", - "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", - "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", - "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", - "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", - "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", - "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", - "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", - "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", - "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", - "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", - "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", - "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", - "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", - "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", - "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", - "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", - "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", - "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", - "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", - "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", - "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", - "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", - "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", - "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", - "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", - "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", - "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", - "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", - "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", - "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", - "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", - "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", - "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", - "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", - "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", - "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", - "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", - "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", - "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", - "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", - "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", - "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", - "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", - "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", - "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", - "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", - "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", - "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", - "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", - "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", - "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", - "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", - "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", - "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", - "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", - "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", - "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", - "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", - "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", - "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", - "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", - "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", - "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", - "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", - "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", - "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.0" - }, - "google-resumable-media": { - "hashes": [ - "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93", - "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.0" - }, - "googleapis-common-protos": { - "hashes": [ - "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44", - "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f" - ], - "markers": "python_version >= '3.7'", - "version": "==1.59.0" - }, - "grpc-google-iam-v1": { - "hashes": [ - "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f", - "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c" - ], - "markers": "python_version >= '3.7'", - "version": "==0.12.6" - }, - "grpcio": { - "hashes": [ - "sha256:02000b005bc8b72ff50c477b6431e8886b29961159e8b8d03c00b3dd9139baed", - "sha256:031bbd26656e0739e4b2c81c172155fb26e274b8d0312d67aefc730bcba915b6", - "sha256:1209d6b002b26e939e4c8ea37a3d5b4028eb9555394ea69fb1adbd4b61a10bb8", - "sha256:125ed35aa3868efa82eabffece6264bf638cfdc9f0cd58ddb17936684aafd0f8", - "sha256:1382bc499af92901c2240c4d540c74eae8a671e4fe9839bfeefdfcc3a106b5e2", - "sha256:16bca8092dd994f2864fdab278ae052fad4913f36f35238b2dd11af2d55a87db", - "sha256:1c59d899ee7160638613a452f9a4931de22623e7ba17897d8e3e348c2e9d8d0b", - "sha256:1d109df30641d050e009105f9c9ca5a35d01e34d2ee2a4e9c0984d392fd6d704", - "sha256:1fa7d6ddd33abbd3c8b3d7d07c56c40ea3d1891ce3cd2aa9fa73105ed5331866", - "sha256:21c4a1aae861748d6393a3ff7867473996c139a77f90326d9f4104bebb22d8b8", - "sha256:224166f06ccdaf884bf35690bf4272997c1405de3035d61384ccb5b25a4c1ca8", - "sha256:2262bd3512ba9e9f0e91d287393df6f33c18999317de45629b7bd46c40f16ba9", - "sha256:2585b3c294631a39b33f9f967a59b0fad23b1a71a212eba6bc1e3ca6e6eec9ee", - "sha256:27fb030a4589d2536daec5ff5ba2a128f4f155149efab578fe2de2cb21596d3d", - "sha256:30fbbce11ffeb4f9f91c13fe04899aaf3e9a81708bedf267bf447596b95df26b", - "sha256:3930669c9e6f08a2eed824738c3d5699d11cd47a0ecc13b68ed11595710b1133", - "sha256:3b170e441e91e4f321e46d3cc95a01cb307a4596da54aca59eb78ab0fc03754d", - "sha256:3db71c6f1ab688d8dfc102271cedc9828beac335a3a4372ec54b8bf11b43fd29", - "sha256:48cb7af77238ba16c77879009003f6b22c23425e5ee59cb2c4c103ec040638a5", - "sha256:49eace8ea55fbc42c733defbda1e4feb6d3844ecd875b01bb8b923709e0f5ec8", - "sha256:533eaf5b2a79a3c6f35cbd6a095ae99cac7f4f9c0e08bdcf86c130efd3c32adf", - "sha256:5942a3e05630e1ef5b7b5752e5da6582460a2e4431dae603de89fc45f9ec5aa9", - "sha256:62117486460c83acd3b5d85c12edd5fe20a374630475388cfc89829831d3eb79", - "sha256:650f5f2c9ab1275b4006707411bb6d6bc927886874a287661c3c6f332d4c068b", - "sha256:6dc1e2c9ac292c9a484ef900c568ccb2d6b4dfe26dfa0163d5bc815bb836c78d", - "sha256:73c238ef6e4b64272df7eec976bb016c73d3ab5a6c7e9cd906ab700523d312f3", - "sha256:775a2f70501370e5ba54e1ee3464413bff9bd85bd9a0b25c989698c44a6fb52f", - "sha256:860fcd6db7dce80d0a673a1cc898ce6bc3d4783d195bbe0e911bf8a62c93ff3f", - "sha256:87f47bf9520bba4083d65ab911f8f4c0ac3efa8241993edd74c8dd08ae87552f", - "sha256:960b176e0bb2b4afeaa1cd2002db1e82ae54c9b6e27ea93570a42316524e77cf", - "sha256:a7caf553ccaf715ec05b28c9b2ab2ee3fdb4036626d779aa09cf7cbf54b71445", - "sha256:a947d5298a0bbdd4d15671024bf33e2b7da79a70de600ed29ba7e0fef0539ebb", - "sha256:a97b0d01ae595c997c1d9d8249e2d2da829c2d8a4bdc29bb8f76c11a94915c9a", - "sha256:b7655f809e3420f80ce3bf89737169a9dce73238af594049754a1128132c0da4", - "sha256:c33744d0d1a7322da445c0fe726ea6d4e3ef2dfb0539eadf23dce366f52f546c", - "sha256:c55a9cf5cba80fb88c850915c865b8ed78d5e46e1f2ec1b27692f3eaaf0dca7e", - "sha256:d2f62fb1c914a038921677cfa536d645cb80e3dd07dc4859a3c92d75407b90a5", - "sha256:d8ae6e0df3a608e99ee1acafaafd7db0830106394d54571c1ece57f650124ce9", - "sha256:e355ee9da9c1c03f174efea59292b17a95e0b7b4d7d2a389265f731a9887d5a9", - "sha256:e3e526062c690517b42bba66ffe38aaf8bc99a180a78212e7b22baa86902f690", - "sha256:eb0807323572642ab73fd86fe53d88d843ce617dd1ddf430351ad0759809a0ae", - "sha256:ebff0738be0499d7db74d20dca9f22a7b27deae31e1bf92ea44924fd69eb6251", - "sha256:ed36e854449ff6c2f8ee145f94851fe171298e1e793f44d4f672c4a0d78064e7", - "sha256:ed3d458ded32ff3a58f157b60cc140c88f7ac8c506a1c567b2a9ee8a2fd2ce54", - "sha256:f4a7dca8ccd8023d916b900aa3c626f1bd181bd5b70159479b142f957ff420e4" - ], - "version": "==1.54.0" - }, - "grpcio-status": { - "hashes": [ - "sha256:2154fdb8aad20452488712be6879657b508115ca06139fde8897ea8e9bc79367", - "sha256:c9ce3213e84c6fd8801c31aca3ea4a6b3453eaa40b93a6c0a23ea8999808fa00" - ], - "markers": "python_version >= '3.6'", - "version": "==1.47.0" - }, - "httplib2": { - "hashes": [ - "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", - "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.22.0" - }, - "identify": { - "hashes": [ - "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4", - "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.24" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "importlib-metadata": { - "hashes": [ - "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed", - "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705" - ], - "markers": "python_version < '3.8'", - "version": "==6.6.0" - }, - "importlib-resources": { - "hashes": [ - "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6", - "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a" - ], - "markers": "python_version < '3.9'", - "version": "==5.12.0" - }, - "iniconfig": { - "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" - }, - "jsonschema": { - "hashes": [ - "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6" - ], - "markers": "python_version >= '3.7'", - "version": "==4.17.3" - }, - "kfp": { - "hashes": [ - "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c" - ], - "index": "pypi", - "version": "==1.8.21" - }, - "kfp-pipeline-spec": { - "hashes": [ - "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==0.1.16" - }, - "kfp-server-api": { - "hashes": [ - "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de" - ], - "version": "==1.8.5" - }, - "kubernetes": { - "hashes": [ - "sha256:213befbb4e5aed95f94950c7eed0c2322fc5a2f8f40932e58d28fdd42d90836c", - "sha256:eb42333dad0bb5caf4e66460c6a4a1a36f0f057a040f35018f6c05a699baed86" - ], - "markers": "python_version >= '3.6'", - "version": "==25.3.0" - }, - "nodeenv": { - "hashes": [ - "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e", - "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==1.7.0" - }, - "oauthlib": { - "hashes": [ - "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", - "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918" - ], - "markers": "python_version >= '3.6'", - "version": "==3.2.2" - }, - "packaging": { - "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1" - }, - "pkgutil-resolve-name": { - "hashes": [ - "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174", - "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e" - ], - "markers": "python_version < '3.9'", - "version": "==1.3.10" - }, - "platformdirs": { - "hashes": [ - "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4", - "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335" - ], - "markers": "python_version >= '3.7'", - "version": "==3.5.0" - }, - "pluggy": { - "hashes": [ - "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", - "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" - }, - "pre-commit": { - "hashes": [ - "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658", - "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad" - ], - "index": "pypi", - "version": "==2.21.0" - }, - "proto-plus": { - "hashes": [ - "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165", - "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d" - ], - "markers": "python_version >= '3.6'", - "version": "==1.22.2" - }, - "protobuf": { - "hashes": [ - "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7", - "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c", - "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2", - "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b", - "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050", - "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9", - "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7", - "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454", - "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480", - "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469", - "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c", - "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e", - "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db", - "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905", - "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b", - "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86", - "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4", - "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402", - "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7", - "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4", - "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99", - "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee" - ], - "markers": "python_version >= '3.7'", - "version": "==3.20.3" - }, - "pyasn1": { - "hashes": [ - "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57", - "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.5.0" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", - "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.3.0" - }, - "pydantic": { - "hashes": [ - "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e", - "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6", - "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd", - "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca", - "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b", - "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a", - "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245", - "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d", - "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee", - "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1", - "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3", - "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d", - "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5", - "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914", - "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd", - "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1", - "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e", - "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e", - "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a", - "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd", - "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f", - "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209", - "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d", - "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a", - "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143", - "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918", - "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52", - "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e", - "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f", - "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e", - "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb", - "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe", - "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe", - "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d", - "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209", - "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af" - ], - "markers": "python_version >= '3.7'", - "version": "==1.10.7" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_version >= '3.1'", - "version": "==3.0.9" - }, - "pyrsistent": { - "hashes": [ - "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8", - "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440", - "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a", - "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c", - "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3", - "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393", - "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9", - "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da", - "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf", - "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64", - "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a", - "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3", - "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98", - "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2", - "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8", - "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf", - "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc", - "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7", - "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28", - "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2", - "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b", - "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a", - "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64", - "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19", - "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1", - "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9", - "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c" - ], - "markers": "python_version >= '3.7'", - "version": "==0.19.3" - }, - "pytest": { - "hashes": [ - "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362", - "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3" - ], - "index": "pypi", - "version": "==7.3.1" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pyyaml": { - "hashes": [ - "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", - "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", - "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", - "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", - "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", - "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", - "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", - "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", - "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", - "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", - "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", - "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", - "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", - "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", - "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", - "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", - "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", - "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", - "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", - "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", - "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", - "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", - "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", - "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", - "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", - "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", - "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", - "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", - "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.4.1" - }, - "requests": { - "hashes": [ - "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294", - "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4" - ], - "markers": "python_version >= '3.7'", - "version": "==2.30.0" - }, - "requests-oauthlib": { - "hashes": [ - "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", - "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.3.1" - }, - "requests-toolbelt": { - "hashes": [ - "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7", - "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.1" - }, - "rsa": { - "hashes": [ - "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", - "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" - ], - "markers": "python_version >= '3.6'", - "version": "==4.9" - }, - "setuptools": { - "hashes": [ - "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", - "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" - ], - "markers": "python_version >= '3.7'", - "version": "==67.7.2" - }, - "shapely": { - "hashes": [ - "sha256:02dd5d7dc6e46515d88874134dc8fcdc65826bca93c3eecee59d1910c42c1b17", - "sha256:0b4ee3132ee90f07d63db3aea316c4c065ed7a26231458dda0874414a09d6ba3", - "sha256:0d885cb0cf670c1c834df3f371de8726efdf711f18e2a75da5cfa82843a7ab65", - "sha256:147066da0be41b147a61f8eb805dea3b13709dbc873a431ccd7306e24d712bc0", - "sha256:21776184516a16bf82a0c3d6d6a312b3cd15a4cabafc61ee01cf2714a82e8396", - "sha256:2e0a8c2e55f1be1312b51c92b06462ea89e6bb703fab4b114e7a846d941cfc40", - "sha256:2fd15397638df291c427a53d641d3e6fd60458128029c8c4f487190473a69a91", - "sha256:3480657460e939f45a7d359ef0e172a081f249312557fe9aa78c4fd3a362d993", - "sha256:370b574c78dc5af3a198a6da5d9b3d7c04654bd2ef7e80e80a3a0992dfb2d9cd", - "sha256:38f0fbbcb8ca20c16451c966c1f527cc43968e121c8a048af19ed3e339a921cd", - "sha256:4728666fff8cccc65a07448cae72c75a8773fea061c3f4f139c44adc429b18c3", - "sha256:48dcfffb9e225c0481120f4bdf622131c8c95f342b00b158cdbe220edbbe20b6", - "sha256:4b47bb6f9369e8bf3e6dbd33e6a25a47ee02b2874792a529fe04a49bf8bc0df6", - "sha256:532a55ee2a6c52d23d6f7d1567c8f0473635f3b270262c44e1b0c88096827e22", - "sha256:5d7f85c2d35d39ff53c9216bc76b7641c52326f7e09aaad1789a3611a0f812f2", - "sha256:65b21243d8f6bcd421210daf1fabb9de84de2c04353c5b026173b88d17c1a581", - "sha256:66bdac74fbd1d3458fa787191a90fa0ae610f09e2a5ec398c36f968cc0ed743f", - "sha256:6d388c0c1bd878ed1af4583695690aa52234b02ed35f93a1c8486ff52a555838", - "sha256:6fe855e7d45685926b6ba00aaeb5eba5862611f7465775dacd527e081a8ced6d", - "sha256:753ed0e21ab108bd4282405b9b659f2e985e8502b1a72b978eaa51d3496dee19", - "sha256:783bad5f48e2708a0e2f695a34ed382e4162c795cb2f0368b39528ac1d6db7ed", - "sha256:78fb9d929b8ee15cfd424b6c10879ce1907f24e05fb83310fc47d2cd27088e40", - "sha256:84010db15eb364a52b74ea8804ef92a6a930dfc1981d17a369444b6ddec66efd", - "sha256:89164e7a9776a19e29f01369a98529321994e2e4d852b92b7e01d4d9804c55bf", - "sha256:8d086591f744be483b34628b391d741e46f2645fe37594319e0a673cc2c26bcf", - "sha256:8e59817b0fe63d34baedaabba8c393c0090f061917d18fc0bcc2f621937a8f73", - "sha256:99a2f0da0109e81e0c101a2b4cd8412f73f5f299e7b5b2deaf64cd2a100ac118", - "sha256:99ab0ddc05e44acabdbe657c599fdb9b2d82e86c5493bdae216c0c4018a82dee", - "sha256:a23ef3882d6aa203dd3623a3d55d698f59bfbd9f8a3bfed52c2da05a7f0f8640", - "sha256:a354199219c8d836f280b88f2c5102c81bb044ccea45bd361dc38a79f3873714", - "sha256:a74631e511153366c6dbe3229fa93f877e3c87ea8369cd00f1d38c76b0ed9ace", - "sha256:ab38f7b5196ace05725e407cb8cab9ff66edb8e6f7bb36a398e8f73f52a7aaa2", - "sha256:adcf8a11b98af9375e32bff91de184f33a68dc48b9cb9becad4f132fa25cfa3c", - "sha256:b65f5d530ba91e49ffc7c589255e878d2506a8b96ffce69d3b7c4500a9a9eaf8", - "sha256:be9423d5a3577ac2e92c7e758bd8a2b205f5e51a012177a590bc46fc51eb4834", - "sha256:c2822111ddc5bcfb116e6c663e403579d0fe3f147d2a97426011a191c43a7458", - "sha256:c6a9a4a31cd6e86d0fbe8473ceed83d4fe760b19d949fb557ef668defafea0f6", - "sha256:d048f93e42ba578b82758c15d8ae037d08e69d91d9872bca5a1895b118f4e2b0", - "sha256:d8a2b2a65fa7f97115c1cd989fe9d6f39281ca2a8a014f1d4904c1a6e34d7f25", - "sha256:e9c30b311de2513555ab02464ebb76115d242842b29c412f5a9aa0cac57be9f6", - "sha256:ec14ceca36f67cb48b34d02d7f65a9acae15cd72b48e303531893ba4a960f3ea", - "sha256:ef3be705c3eac282a28058e6c6e5503419b250f482320df2172abcbea642c831" - ], - "markers": "python_version >= '3.6'", - "version": "==1.8.5.post1" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "strip-hints": { - "hashes": [ - "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f" - ], - "version": "==0.1.10" - }, - "tabulate": { - "hashes": [ - "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", - "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" - ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" - }, - "termcolor": { - "hashes": [ - "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475", - "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.0" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11'", - "version": "==2.0.1" - }, - "typer": { - "hashes": [ - "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2", - "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee" - ], - "markers": "python_version >= '3.6'", - "version": "==0.9.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", - "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" - ], - "markers": "python_version < '3.9'", - "version": "==4.5.0" - }, - "uritemplate": { - "hashes": [ - "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f", - "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.0.1" - }, - "urllib3": { - "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" - }, - "virtualenv": { - "hashes": [ - "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e", - "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924" - ], - "markers": "python_version >= '3.7'", - "version": "==20.23.0" - }, - "websocket-client": { - "hashes": [ - "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40", - "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.1" - }, - "wheel": { - "hashes": [ - "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873", - "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247" - ], - "markers": "python_version >= '3.7'", - "version": "==0.40.0" - }, - "wrapt": { - "hashes": [ - "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", - "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", - "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", - "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", - "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", - "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", - "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", - "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", - "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", - "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", - "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", - "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", - "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", - "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", - "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", - "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", - "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", - "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", - "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", - "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", - "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", - "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", - "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", - "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", - "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", - "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", - "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", - "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", - "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", - "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", - "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", - "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", - "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", - "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", - "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", - "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", - "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", - "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", - "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", - "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", - "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", - "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", - "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", - "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", - "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", - "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", - "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", - "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", - "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", - "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", - "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", - "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", - "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", - "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", - "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", - "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", - "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", - "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", - "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", - "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", - "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", - "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", - "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", - "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", - "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", - "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", - "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", - "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", - "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", - "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", - "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", - "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", - "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", - "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", - "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.15.0" - }, - "zipp": { - "hashes": [ - "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", - "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556" - ], - "markers": "python_version < '3.10'", - "version": "==3.15.0" - } - } -} diff --git a/components/vertex-components/poetry.lock b/components/vertex-components/poetry.lock new file mode 100644 index 00000000..45fcd24d --- /dev/null +++ b/components/vertex-components/poetry.lock @@ -0,0 +1,1695 @@ +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. + +[[package]] +name = "absl-py" +version = "1.4.0" +description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "absl-py-1.4.0.tar.gz", hash = "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"}, + {file = "absl_py-1.4.0-py3-none-any.whl", hash = "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47"}, +] + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "cachetools" +version = "5.3.0" +description = "Extensible memoizing collections and decorators" +category = "main" +optional = false +python-versions = "~=3.7" +files = [ + {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, + {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, +] + +[[package]] +name = "certifi" +version = "2023.5.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, +] + +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "cloudpickle" +version = "2.2.1" +description = "Extended pickling support for Python objects" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cloudpickle-2.2.1-py3-none-any.whl", hash = "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f"}, + {file = "cloudpickle-2.2.1.tar.gz", hash = "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "deprecated" +version = "1.2.13" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, + {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] + +[[package]] +name = "docstring-parser" +version = "0.15" +description = "Parse Python docstrings in reST, Google and Numpydoc format" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "docstring_parser-0.15-py3-none-any.whl", hash = "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9"}, + {file = "docstring_parser-0.15.tar.gz", hash = "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.12.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, + {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "fire" +version = "0.5.0" +description = "A library for automatically generating command line interfaces." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "fire-0.5.0.tar.gz", hash = "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6"}, +] + +[package.dependencies] +six = "*" +termcolor = "*" + +[[package]] +name = "google-api-core" +version = "2.11.0" +description = "Google API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.11.0.tar.gz", hash = "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22"}, + {file = "google_api_core-2.11.0-py3-none-any.whl", hash = "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0dev" +googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)", "grpcio-status (>=1.49.1,<2.0dev)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] + +[[package]] +name = "google-api-python-client" +version = "1.12.11" +description = "Google API Client Library for Python" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +files = [ + {file = "google-api-python-client-1.12.11.tar.gz", hash = "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9"}, + {file = "google_api_python_client-1.12.11-py2.py3-none-any.whl", hash = "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.21.0,<3dev", markers = "python_version >= \"3\""} +google-auth = {version = ">=1.16.0,<3dev", markers = "python_version >= \"3\""} +google-auth-httplib2 = ">=0.0.3" +httplib2 = ">=0.15.0,<1dev" +six = ">=1.13.0,<2dev" +uritemplate = ">=3.0.0,<4dev" + +[[package]] +name = "google-auth" +version = "2.18.0" +description = "Google Authentication Library" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +files = [ + {file = "google-auth-2.18.0.tar.gz", hash = "sha256:c66b488a8b005b23ccb97b1198b6cece516c91869091ac5b7c267422db2733c7"}, + {file = "google_auth-2.18.0-py2.py3-none-any.whl", hash = "sha256:ef3f3a67fa54d421a1c155864570f9a8de9179cedc937bda496b7a8ca338e936"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} +six = ">=1.9.0" +urllib3 = "<2.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0dev)"] + +[[package]] +name = "google-auth-httplib2" +version = "0.1.0" +description = "Google Authentication Library: httplib2 transport" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, + {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, +] + +[package.dependencies] +google-auth = "*" +httplib2 = ">=0.15.0" +six = "*" + +[[package]] +name = "google-cloud-aiplatform" +version = "1.24.1" +description = "Vertex AI API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-aiplatform-1.24.1.tar.gz", hash = "sha256:0ce9e97bf5c977397e52b3b7c4dc78610c135fbde11a60a6c0b77a4fdf776400"}, + {file = "google_cloud_aiplatform-1.24.1-py2.py3-none-any.whl", hash = "sha256:942765a6bad97e46e262dd6599dc5f171663ce952130e0b0b2eb97e0b1b98bfd"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-bigquery = ">=1.15.0,<4.0.0dev" +google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" +google-cloud-storage = ">=1.32.0,<3.0.0dev" +packaging = ">=14.3" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +shapely = "<2.0.0" + +[package.extras] +autologging = ["mlflow (>=1.27.0,<=2.1.1)"] +cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] +datasets = ["pyarrow (>=3.0.0,<8.0dev)"] +endpoint = ["requests (>=2.28.1)"] +full = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] +lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"] +metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"] +pipelines = ["pyyaml (>=5.3,<6)"] +prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<0.76.0)", "starlette (>=0.17.1)", "uvicorn[standard] (>=0.16.0)"] +private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] +tensorboard = ["tensorflow (>=2.3.0,<3.0.0dev)"] +testing = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "grpcio-testing", "ipython", "kfp", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "scikit-learn", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] +vizier = ["google-vizier (==0.0.4)"] +xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] + +[[package]] +name = "google-cloud-bigquery" +version = "3.10.0" +description = "Google BigQuery API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-bigquery-3.10.0.tar.gz", hash = "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df"}, + {file = "google_cloud_bigquery-3.10.0-py2.py3-none-any.whl", hash = "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-core = ">=1.6.0,<3.0.0dev" +google-resumable-media = ">=0.6.0,<3.0dev" +grpcio = ">=1.47.0,<2.0dev" +packaging = ">=20.0.0" +proto-plus = ">=1.15.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +python-dateutil = ">=2.7.2,<3.0dev" +requests = ">=2.21.0,<3.0.0dev" + +[package.extras] +all = ["Shapely (>=1.8.4,<2.0dev)", "db-dtypes (>=0.3.0,<2.0.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)", "ipywidgets (>=7.7.0)", "opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)", "tqdm (>=4.7.4,<5.0.0dev)"] +bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "pyarrow (>=3.0.0)"] +geopandas = ["Shapely (>=1.8.4,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)"] +ipython = ["ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)"] +ipywidgets = ["ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)"] +opentelemetry = ["opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)"] +pandas = ["db-dtypes (>=0.3.0,<2.0.0dev)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)"] +tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] + +[[package]] +name = "google-cloud-core" +version = "2.3.2" +description = "Google Cloud API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-core-2.3.2.tar.gz", hash = "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a"}, + {file = "google_cloud_core-2.3.2-py2.py3-none-any.whl", hash = "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe"}, +] + +[package.dependencies] +google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" + +[package.extras] +grpc = ["grpcio (>=1.38.0,<2.0dev)"] + +[[package]] +name = "google-cloud-notebooks" +version = "1.7.0" +description = "Google Cloud Notebooks API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-notebooks-1.7.0.tar.gz", hash = "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e"}, + {file = "google_cloud_notebooks-1.7.0-py2.py3-none-any.whl", hash = "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-pipeline-components" +version = "1.0.42" +description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "google_cloud_pipeline_components-1.0.42-py3-none-any.whl", hash = "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev" +google-cloud-aiplatform = ">=1.14.0,<2" +google-cloud-notebooks = ">=0.4.0" +google-cloud-storage = ">=2.2.1,<3" +googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio-status = "<=1.47.0" +kfp = ">=1.8.9,<2.0.0" +protobuf = ">=3.19.0,<4.0.0dev" + +[package.extras] +tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev)", "google-cloud-aiplatform (>=1.14.0,<2)", "google-cloud-notebooks (>=0.4.0)", "google-cloud-storage (>=2.2.1,<3)", "googleapis-common-protos (>=1.56.2,<2.0dev)", "grpcio-status (<=1.47.0)", "kfp (>=1.8.9,<2.0.0)", "mock (>=4.0.0)", "protobuf (>=3.19.0,<4.0.0dev)", "pytest (>=6.0.0)"] + +[[package]] +name = "google-cloud-resource-manager" +version = "1.10.0" +description = "Google Cloud Resource Manager API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-resource-manager-1.10.0.tar.gz", hash = "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287"}, + {file = "google_cloud_resource_manager-1.10.0-py2.py3-none-any.whl", hash = "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-storage" +version = "2.9.0" +description = "Google Cloud Storage API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-storage-2.9.0.tar.gz", hash = "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3"}, + {file = "google_cloud_storage-2.9.0-py2.py3-none-any.whl", hash = "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" +google-cloud-core = ">=2.3.0,<3.0dev" +google-resumable-media = ">=2.3.2" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +protobuf = ["protobuf (<5.0.0dev)"] + +[[package]] +name = "google-crc32c" +version = "1.5.0" +description = "A python wrapper of the C library 'Google CRC32C'" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win32.whl", hash = "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win32.whl", hash = "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win32.whl", hash = "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win32.whl", hash = "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win32.whl", hash = "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93"}, +] + +[package.extras] +testing = ["pytest"] + +[[package]] +name = "google-resumable-media" +version = "2.5.0" +description = "Utilities for Google Media Downloads and Resumable Uploads" +category = "main" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "google-resumable-media-2.5.0.tar.gz", hash = "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93"}, + {file = "google_resumable_media-2.5.0-py2.py3-none-any.whl", hash = "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec"}, +] + +[package.dependencies] +google-crc32c = ">=1.0,<2.0dev" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] +requests = ["requests (>=2.18.0,<3.0.0dev)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.59.0" +description = "Common protobufs used in Google APIs" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.59.0.tar.gz", hash = "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44"}, + {file = "googleapis_common_protos-1.59.0-py2.py3-none-any.whl", hash = "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.12.6" +description = "IAM API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.12.6.tar.gz", hash = "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f"}, + {file = "grpc_google_iam_v1-0.12.6-py2.py3-none-any.whl", hash = "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.54.2" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.54.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c"}, + {file = "grpcio-1.54.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c0e3155fc5335ec7b3b70f15230234e529ca3607b20a562b6c75fb1b1218874c"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bf88004fe086c786dc56ef8dd6cb49c026833fdd6f42cb853008bce3f907148"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be88c081e33f20630ac3343d8ad9f1125f32987968e9c8c75c051c9800896e8"}, + {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:33d40954199bddbb6a78f8f6f2b2082660f381cd2583ec860a6c2fa7c8400c08"}, + {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b52d00d1793d290c81ad6a27058f5224a7d5f527867e5b580742e1bd211afeee"}, + {file = "grpcio-1.54.2-cp310-cp310-win32.whl", hash = "sha256:881d058c5ccbea7cc2c92085a11947b572498a27ef37d3eef4887f499054dca8"}, + {file = "grpcio-1.54.2-cp310-cp310-win_amd64.whl", hash = "sha256:0212e2f7fdf7592e4b9d365087da30cb4d71e16a6f213120c89b4f8fb35a3ab3"}, + {file = "grpcio-1.54.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:1e623e0cf99a0ac114f091b3083a1848dbc64b0b99e181473b5a4a68d4f6f821"}, + {file = "grpcio-1.54.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:66233ccd2a9371158d96e05d082043d47dadb18cbb294dc5accfdafc2e6b02a7"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:4cb283f630624ebb16c834e5ac3d7880831b07cbe76cb08ab7a271eeaeb8943e"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a1e601ee31ef30a9e2c601d0867e236ac54c922d32ed9f727b70dd5d82600d5"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8da84bbc61a4e92af54dc96344f328e5822d574f767e9b08e1602bb5ddc254a"}, + {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5008964885e8d23313c8e5ea0d44433be9bfd7e24482574e8cc43c02c02fc796"}, + {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2f5a1f1080ccdc7cbaf1171b2cf384d852496fe81ddedeb882d42b85727f610"}, + {file = "grpcio-1.54.2-cp311-cp311-win32.whl", hash = "sha256:b74ae837368cfffeb3f6b498688a123e6b960951be4dec0e869de77e7fa0439e"}, + {file = "grpcio-1.54.2-cp311-cp311-win_amd64.whl", hash = "sha256:8cdbcbd687e576d48f7886157c95052825ca9948c0ed2afdc0134305067be88b"}, + {file = "grpcio-1.54.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:782f4f8662a2157c4190d0f99eaaebc602899e84fb1e562a944e5025929e351c"}, + {file = "grpcio-1.54.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:714242ad0afa63a2e6dabd522ae22e1d76e07060b5af2ddda5474ba4f14c2c94"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:f900ed4ad7a0f1f05d35f955e0943944d5a75f607a836958c6b8ab2a81730ef2"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96a41817d2c763b1d0b32675abeb9179aa2371c72aefdf74b2d2b99a1b92417b"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70fcac7b94f4c904152809a050164650ac81c08e62c27aa9f156ac518029ebbe"}, + {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fd6c6c29717724acf9fc1847c4515d57e4dc12762452457b9cb37461f30a81bb"}, + {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c2392f5b5d84b71d853918687d806c1aa4308109e5ca158a16e16a6be71041eb"}, + {file = "grpcio-1.54.2-cp37-cp37m-win_amd64.whl", hash = "sha256:51630c92591d6d3fe488a7c706bd30a61594d144bac7dee20c8e1ce78294f474"}, + {file = "grpcio-1.54.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:b04202453941a63b36876a7172b45366dc0cde10d5fd7855c0f4a4e673c0357a"}, + {file = "grpcio-1.54.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:89dde0ac72a858a44a2feb8e43dc68c0c66f7857a23f806e81e1b7cc7044c9cf"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:09d4bfd84686cd36fd11fd45a0732c7628308d094b14d28ea74a81db0bce2ed3"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fc2b4edb938c8faa4b3c3ea90ca0dd89b7565a049e8e4e11b77e60e4ed2cc05"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61f7203e2767800edee7a1e1040aaaf124a35ce0c7fe0883965c6b762defe598"}, + {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e416c8baf925b5a1aff31f7f5aecc0060b25d50cce3a5a7255dc5cf2f1d4e5eb"}, + {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dc80c9c6b608bf98066a038e0172013a49cfa9a08d53335aefefda2c64fc68f4"}, + {file = "grpcio-1.54.2-cp38-cp38-win32.whl", hash = "sha256:8d6192c37a30a115f4663592861f50e130caed33efc4eec24d92ec881c92d771"}, + {file = "grpcio-1.54.2-cp38-cp38-win_amd64.whl", hash = "sha256:46a057329938b08e5f0e12ea3d7aed3ecb20a0c34c4a324ef34e00cecdb88a12"}, + {file = "grpcio-1.54.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:2296356b5c9605b73ed6a52660b538787094dae13786ba53080595d52df13a98"}, + {file = "grpcio-1.54.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:c72956972e4b508dd39fdc7646637a791a9665b478e768ffa5f4fe42123d5de1"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:9bdbb7624d65dc0ed2ed8e954e79ab1724526f09b1efa88dcd9a1815bf28be5f"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c44e1a765b31e175c391f22e8fc73b2a2ece0e5e6ff042743d8109b5d2eff9f"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cc928cfe6c360c1df636cf7991ab96f059666ac7b40b75a769410cc6217df9c"}, + {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a08920fa1a97d4b8ee5db2f31195de4a9def1a91bc003544eb3c9e6b8977960a"}, + {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4864f99aac207e3e45c5e26c6cbb0ad82917869abc2f156283be86c05286485c"}, + {file = "grpcio-1.54.2-cp39-cp39-win32.whl", hash = "sha256:b38b3de8cff5bc70f8f9c615f51b48eff7313fc9aca354f09f81b73036e7ddfa"}, + {file = "grpcio-1.54.2-cp39-cp39-win_amd64.whl", hash = "sha256:be48496b0e00460717225e7680de57c38be1d8629dc09dadcd1b3389d70d942b"}, + {file = "grpcio-1.54.2.tar.gz", hash = "sha256:50a9f075eeda5097aa9a182bb3877fe1272875e45370368ac0ee16ab9e22d019"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.54.2)"] + +[[package]] +name = "grpcio-status" +version = "1.47.0" +description = "Status proto mapping for gRPC" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.47.0.tar.gz", hash = "sha256:c9ce3213e84c6fd8801c31aca3ea4a6b3453eaa40b93a6c0a23ea8999808fa00"}, + {file = "grpcio_status-1.47.0-py3-none-any.whl", hash = "sha256:2154fdb8aad20452488712be6879657b508115ca06139fde8897ea8e9bc79367"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.47.0" +protobuf = ">=3.12.0" + +[[package]] +name = "httplib2" +version = "0.22.0" +description = "A comprehensive HTTP client library." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, + {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, +] + +[package.dependencies] +pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} + +[[package]] +name = "identify" +version = "2.5.24" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, + {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "kfp" +version = "1.8.21" +description = "KubeFlow Pipelines SDK" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "kfp-1.8.21.tar.gz", hash = "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c"}, +] + +[package.dependencies] +absl-py = ">=0.9,<2" +click = ">=7.1.2,<9" +cloudpickle = ">=2.0.0,<3" +Deprecated = ">=1.2.7,<2" +docstring-parser = ">=0.7.3,<1" +fire = ">=0.3.1,<1" +google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-python-client = ">=1.7.8,<2" +google-auth = ">=1.6.1,<3" +google-cloud-storage = ">=1.20.0,<3" +jsonschema = ">=3.0.1,<5" +kfp-pipeline-spec = ">=0.1.16,<0.2.0" +kfp-server-api = ">=1.1.2,<2.0.0" +kubernetes = ">=8.0.0,<26" +protobuf = ">=3.13.0,<4" +pydantic = ">=1.8.2,<2" +PyYAML = ">=5.3,<6" +requests-toolbelt = ">=0.8.0,<1" +strip-hints = ">=0.1.8,<1" +tabulate = ">=0.8.6,<1" +typer = ">=0.3.2,<1.0" +uritemplate = ">=3.0.1,<4" +urllib3 = "<2" + +[package.extras] +all = ["docker"] + +[[package]] +name = "kfp-pipeline-spec" +version = "0.1.16" +description = "Kubeflow Pipelines pipeline spec" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "kfp_pipeline_spec-0.1.16-py3-none-any.whl", hash = "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25"}, +] + +[package.dependencies] +protobuf = ">=3.13.0,<4" + +[[package]] +name = "kfp-server-api" +version = "1.8.5" +description = "Kubeflow Pipelines API" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "kfp-server-api-1.8.5.tar.gz", hash = "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de"}, +] + +[package.dependencies] +certifi = "*" +python-dateutil = "*" +six = ">=1.10" +urllib3 = ">=1.15" + +[[package]] +name = "kubernetes" +version = "25.3.0" +description = "Kubernetes python client" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "kubernetes-25.3.0-py2.py3-none-any.whl", hash = "sha256:eb42333dad0bb5caf4e66460c6a4a1a36f0f057a040f35018f6c05a699baed86"}, + {file = "kubernetes-25.3.0.tar.gz", hash = "sha256:213befbb4e5aed95f94950c7eed0c2322fc5a2f8f40932e58d28fdd42d90836c"}, +] + +[package.dependencies] +certifi = ">=14.05.14" +google-auth = ">=1.0.1" +python-dateutil = ">=2.5.3" +pyyaml = ">=5.4.1" +requests = "*" +requests-oauthlib = "*" +setuptools = ">=21.0.0" +six = ">=1.9.0" +urllib3 = ">=1.24.2" +websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" + +[package.extras] +adal = ["adal (>=1.0.2)"] + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "platformdirs" +version = "3.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.21.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "proto-plus" +version = "1.22.2" +description = "Beautiful, Pythonic protocol buffers." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.22.2.tar.gz", hash = "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165"}, + {file = "proto_plus-1.22.2-py3-none-any.whl", hash = "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "3.20.3" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, + {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, + {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, + {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, + {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, + {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, + {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, + {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, + {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, + {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, + {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, + {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, + {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, + {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, + {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, + {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.0-py2.py3-none-any.whl", hash = "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57"}, + {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pydantic" +version = "1.10.7" +description = "Data validation and settings management using python type hints" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, + {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, + {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, + {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, + {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, + {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, + {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, + {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, + {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, + {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrsistent" +version = "0.19.3" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, + {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, + {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, + {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, + {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, + {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, +] + +[[package]] +name = "pytest" +version = "7.3.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "5.4.1" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, +] + +[[package]] +name = "requests" +version = "2.30.0" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.30.0-py3-none-any.whl", hash = "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294"}, + {file = "requests-2.30.0.tar.gz", hash = "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "requests-toolbelt" +version = "0.10.1" +description = "A utility belt for advanced users of python-requests" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"}, + {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +category = "main" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "setuptools" +version = "67.7.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, + {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "shapely" +version = "1.8.5.post1" +description = "Geometric objects, predicates, and operations" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d048f93e42ba578b82758c15d8ae037d08e69d91d9872bca5a1895b118f4e2b0"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99ab0ddc05e44acabdbe657c599fdb9b2d82e86c5493bdae216c0c4018a82dee"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a2f0da0109e81e0c101a2b4cd8412f73f5f299e7b5b2deaf64cd2a100ac118"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6fe855e7d45685926b6ba00aaeb5eba5862611f7465775dacd527e081a8ced6d"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec14ceca36f67cb48b34d02d7f65a9acae15cd72b48e303531893ba4a960f3ea"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a2b2a65fa7f97115c1cd989fe9d6f39281ca2a8a014f1d4904c1a6e34d7f25"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-win32.whl", hash = "sha256:21776184516a16bf82a0c3d6d6a312b3cd15a4cabafc61ee01cf2714a82e8396"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-win_amd64.whl", hash = "sha256:a354199219c8d836f280b88f2c5102c81bb044ccea45bd361dc38a79f3873714"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:783bad5f48e2708a0e2f695a34ed382e4162c795cb2f0368b39528ac1d6db7ed"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a23ef3882d6aa203dd3623a3d55d698f59bfbd9f8a3bfed52c2da05a7f0f8640"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab38f7b5196ace05725e407cb8cab9ff66edb8e6f7bb36a398e8f73f52a7aaa2"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d086591f744be483b34628b391d741e46f2645fe37594319e0a673cc2c26bcf"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4728666fff8cccc65a07448cae72c75a8773fea061c3f4f139c44adc429b18c3"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-win32.whl", hash = "sha256:84010db15eb364a52b74ea8804ef92a6a930dfc1981d17a369444b6ddec66efd"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-win_amd64.whl", hash = "sha256:48dcfffb9e225c0481120f4bdf622131c8c95f342b00b158cdbe220edbbe20b6"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2fd15397638df291c427a53d641d3e6fd60458128029c8c4f487190473a69a91"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a74631e511153366c6dbe3229fa93f877e3c87ea8369cd00f1d38c76b0ed9ace"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:66bdac74fbd1d3458fa787191a90fa0ae610f09e2a5ec398c36f968cc0ed743f"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-win32.whl", hash = "sha256:6d388c0c1bd878ed1af4583695690aa52234b02ed35f93a1c8486ff52a555838"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:be9423d5a3577ac2e92c7e758bd8a2b205f5e51a012177a590bc46fc51eb4834"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5d7f85c2d35d39ff53c9216bc76b7641c52326f7e09aaad1789a3611a0f812f2"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:adcf8a11b98af9375e32bff91de184f33a68dc48b9cb9becad4f132fa25cfa3c"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:753ed0e21ab108bd4282405b9b659f2e985e8502b1a72b978eaa51d3496dee19"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-win32.whl", hash = "sha256:65b21243d8f6bcd421210daf1fabb9de84de2c04353c5b026173b88d17c1a581"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:370b574c78dc5af3a198a6da5d9b3d7c04654bd2ef7e80e80a3a0992dfb2d9cd"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:532a55ee2a6c52d23d6f7d1567c8f0473635f3b270262c44e1b0c88096827e22"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3480657460e939f45a7d359ef0e172a081f249312557fe9aa78c4fd3a362d993"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b65f5d530ba91e49ffc7c589255e878d2506a8b96ffce69d3b7c4500a9a9eaf8"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:147066da0be41b147a61f8eb805dea3b13709dbc873a431ccd7306e24d712bc0"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2822111ddc5bcfb116e6c663e403579d0fe3f147d2a97426011a191c43a7458"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b47bb6f9369e8bf3e6dbd33e6a25a47ee02b2874792a529fe04a49bf8bc0df6"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-win32.whl", hash = "sha256:2e0a8c2e55f1be1312b51c92b06462ea89e6bb703fab4b114e7a846d941cfc40"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-win_amd64.whl", hash = "sha256:0d885cb0cf670c1c834df3f371de8726efdf711f18e2a75da5cfa82843a7ab65"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0b4ee3132ee90f07d63db3aea316c4c065ed7a26231458dda0874414a09d6ba3"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:02dd5d7dc6e46515d88874134dc8fcdc65826bca93c3eecee59d1910c42c1b17"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6a9a4a31cd6e86d0fbe8473ceed83d4fe760b19d949fb557ef668defafea0f6"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:38f0fbbcb8ca20c16451c966c1f527cc43968e121c8a048af19ed3e339a921cd"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78fb9d929b8ee15cfd424b6c10879ce1907f24e05fb83310fc47d2cd27088e40"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89164e7a9776a19e29f01369a98529321994e2e4d852b92b7e01d4d9804c55bf"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-win32.whl", hash = "sha256:8e59817b0fe63d34baedaabba8c393c0090f061917d18fc0bcc2f621937a8f73"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-win_amd64.whl", hash = "sha256:e9c30b311de2513555ab02464ebb76115d242842b29c412f5a9aa0cac57be9f6"}, + {file = "Shapely-1.8.5.post1.tar.gz", hash = "sha256:ef3be705c3eac282a28058e6c6e5503419b250f482320df2172abcbea642c831"}, +] + +[package.extras] +all = ["numpy", "pytest", "pytest-cov"] +test = ["pytest", "pytest-cov"] +vectorized = ["numpy"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "strip-hints" +version = "0.1.10" +description = "Function and command-line program to strip Python type hints." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "strip-hints-0.1.10.tar.gz", hash = "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f"}, +] + +[package.dependencies] +wheel = "*" + +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "termcolor" +version = "2.3.0" +description = "ANSI color formatting for output in terminal" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, + {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typer" +version = "0.9.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.5.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, +] + +[[package]] +name = "uritemplate" +version = "3.0.1" +description = "URI templates" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, + {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, +] + +[[package]] +name = "urllib3" +version = "1.26.15" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, + {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.23.0" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, + {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, +] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.11,<4" +platformdirs = ">=3.2,<4" + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] + +[[package]] +name = "websocket-client" +version = "1.5.1" +description = "WebSocket client for Python with low level API options" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websocket-client-1.5.1.tar.gz", hash = "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40"}, + {file = "websocket_client-1.5.1-py3-none-any.whl", hash = "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"}, +] + +[package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "wheel" +version = "0.40.0" +description = "A built-package format for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, + {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, +] + +[package.extras] +test = ["pytest (>=6.0.0)"] + +[[package]] +name = "wrapt" +version = "1.15.0" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, + {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, + {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, + {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, + {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, + {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, + {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, + {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, + {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, + {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, + {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, + {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, + {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, + {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, + {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, + {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, + {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, + {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, + {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.9,<3.11" +content-hash = "a33ea27bf21c7344b28dfc284c9da679f3dbd302759f7eb5e929058ebef11572" diff --git a/components/vertex-components/pyproject.toml b/components/vertex-components/pyproject.toml index bc16a237..9011c442 100644 --- a/components/vertex-components/pyproject.toml +++ b/components/vertex-components/pyproject.toml @@ -1,32 +1,28 @@ -[project] +[tool.poetry] name = "vertex-components" -authors = [ - {name = "Example User", email = "user@example.com"}, -] -description = "Vertex AI components" +version = "0.1.0" +authors = ["roberta nwokonko "] +description = "Vertex AI components made by Datatonic" readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.7", -] -requires-python = ">=3.7" -dynamic = ["version"] -dependencies = [ - "kfp == 1.8.21", + "Programming Language :: Python :: 3.9", ] -[project.optional-dependencies] -tests = [ - "google-cloud-aiplatform == 1.24.1", - "google-cloud-pipeline-components == 1.0.42", - "pytest >= 7.3.1,<8.0.0", - "pre-commit >= 2.14.1,<3.0.0", -] +[tool.poetry.dependencies] +python = ">=3.9,<3.11" +google-cloud-aiplatform = "1.24.1" +google-cloud-pipeline-components = "1.0.42" +kfp = "==1.8.21" + +[tool.poetry.group.dev.dependencies] +pytest = ">=7.3.1,<8.0.0" +pre-commit = ">=2.14.1,<3.0.0" [build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta:__legacy__" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" [tool.flake8] max-line-length = 88 @@ -45,3 +41,4 @@ pythonpath = [ ] testpaths = "tests" junit_family = "xunit2" + diff --git a/pipelines/Pipfile b/pipelines/Pipfile deleted file mode 100644 index 507b4e6d..00000000 --- a/pipelines/Pipfile +++ /dev/null @@ -1,19 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -kfp = "==1.8.21" -google-cloud-aiplatform = "==1.24.1" -google-cloud-pipeline-components = "==1.0.42" -Jinja2 = ">=3.0.1,<4.0.0" -vertex-components = {editable = true, path = "./../components/vertex-components"} -bigquery-components = {editable = true, path = "./../components/bigquery-components"} - -[dev-packages] -pytest = ">=7.3.1,<8.0.0" -pre-commit = ">=2.14.1,<3.0.0" - -[requires] -python_version = "3.7" diff --git a/pipelines/Pipfile.lock b/pipelines/Pipfile.lock deleted file mode 100644 index 92d8d064..00000000 --- a/pipelines/Pipfile.lock +++ /dev/null @@ -1,1193 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "c889b828537547fe3e76c0ca6f812f331bffdf13bfd8c72104145db94eb555e0" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.7" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "absl-py": { - "hashes": [ - "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47", - "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d" - ], - "markers": "python_version >= '3.6'", - "version": "==1.4.0" - }, - "attrs": { - "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" - }, - "bigquery-components": { - "editable": true, - "path": "./../components/bigquery-components" - }, - "cachetools": { - "hashes": [ - "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14", - "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4" - ], - "markers": "python_version ~= '3.7'", - "version": "==5.3.0" - }, - "certifi": { - "hashes": [ - "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", - "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.5.7" - }, - "charset-normalizer": { - "hashes": [ - "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", - "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", - "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", - "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", - "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", - "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", - "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", - "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", - "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", - "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", - "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", - "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", - "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", - "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", - "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", - "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", - "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", - "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", - "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", - "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", - "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", - "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", - "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", - "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", - "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", - "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", - "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", - "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", - "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", - "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", - "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", - "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", - "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", - "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", - "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", - "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", - "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", - "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", - "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", - "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", - "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", - "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", - "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", - "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", - "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", - "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", - "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", - "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", - "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", - "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", - "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", - "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", - "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", - "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", - "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", - "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", - "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", - "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", - "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", - "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", - "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", - "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", - "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", - "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", - "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", - "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", - "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", - "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", - "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", - "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", - "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", - "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", - "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", - "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", - "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.1.0" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "cloudpickle": { - "hashes": [ - "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f", - "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5" - ], - "markers": "python_version >= '3.6'", - "version": "==2.2.1" - }, - "deprecated": { - "hashes": [ - "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d", - "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.13" - }, - "docstring-parser": { - "hashes": [ - "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682", - "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9" - ], - "markers": "python_version >= '3.6' and python_version < '4.0'", - "version": "==0.15" - }, - "fire": { - "hashes": [ - "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6" - ], - "version": "==0.5.0" - }, - "google-api-core": { - "extras": [ - "grpc" - ], - "hashes": [ - "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22", - "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.11.0" - }, - "google-api-python-client": { - "hashes": [ - "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9", - "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.12.11" - }, - "google-auth": { - "hashes": [ - "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc", - "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.17.3" - }, - "google-auth-httplib2": { - "hashes": [ - "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10", - "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac" - ], - "version": "==0.1.0" - }, - "google-cloud-aiplatform": { - "hashes": [ - "sha256:0ce9e97bf5c977397e52b3b7c4dc78610c135fbde11a60a6c0b77a4fdf776400", - "sha256:942765a6bad97e46e262dd6599dc5f171663ce952130e0b0b2eb97e0b1b98bfd" - ], - "index": "pypi", - "version": "==1.24.1" - }, - "google-cloud-bigquery": { - "hashes": [ - "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df", - "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a" - ], - "markers": "python_version >= '3.7'", - "version": "==3.10.0" - }, - "google-cloud-core": { - "hashes": [ - "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe", - "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.2" - }, - "google-cloud-notebooks": { - "hashes": [ - "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab", - "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e" - ], - "markers": "python_version >= '3.7'", - "version": "==1.7.0" - }, - "google-cloud-pipeline-components": { - "hashes": [ - "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623" - ], - "index": "pypi", - "version": "==1.0.42" - }, - "google-cloud-resource-manager": { - "hashes": [ - "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a", - "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287" - ], - "markers": "python_version >= '3.7'", - "version": "==1.10.0" - }, - "google-cloud-storage": { - "hashes": [ - "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94", - "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3" - ], - "markers": "python_version >= '3.7'", - "version": "==2.9.0" - }, - "google-crc32c": { - "hashes": [ - "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", - "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", - "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", - "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", - "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", - "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", - "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", - "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", - "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", - "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", - "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", - "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", - "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", - "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", - "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", - "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", - "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", - "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", - "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", - "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", - "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", - "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", - "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", - "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", - "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", - "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", - "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", - "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", - "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", - "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", - "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", - "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", - "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", - "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", - "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", - "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", - "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", - "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", - "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", - "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", - "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", - "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", - "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", - "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", - "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", - "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", - "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", - "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", - "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", - "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", - "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", - "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", - "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", - "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", - "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", - "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", - "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", - "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", - "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", - "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", - "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", - "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", - "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", - "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", - "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", - "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", - "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", - "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.0" - }, - "google-resumable-media": { - "hashes": [ - "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93", - "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.0" - }, - "googleapis-common-protos": { - "hashes": [ - "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44", - "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f" - ], - "markers": "python_version >= '3.7'", - "version": "==1.59.0" - }, - "grpc-google-iam-v1": { - "hashes": [ - "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f", - "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c" - ], - "markers": "python_version >= '3.7'", - "version": "==0.12.6" - }, - "grpcio": { - "hashes": [ - "sha256:02000b005bc8b72ff50c477b6431e8886b29961159e8b8d03c00b3dd9139baed", - "sha256:031bbd26656e0739e4b2c81c172155fb26e274b8d0312d67aefc730bcba915b6", - "sha256:1209d6b002b26e939e4c8ea37a3d5b4028eb9555394ea69fb1adbd4b61a10bb8", - "sha256:125ed35aa3868efa82eabffece6264bf638cfdc9f0cd58ddb17936684aafd0f8", - "sha256:1382bc499af92901c2240c4d540c74eae8a671e4fe9839bfeefdfcc3a106b5e2", - "sha256:16bca8092dd994f2864fdab278ae052fad4913f36f35238b2dd11af2d55a87db", - "sha256:1c59d899ee7160638613a452f9a4931de22623e7ba17897d8e3e348c2e9d8d0b", - "sha256:1d109df30641d050e009105f9c9ca5a35d01e34d2ee2a4e9c0984d392fd6d704", - "sha256:1fa7d6ddd33abbd3c8b3d7d07c56c40ea3d1891ce3cd2aa9fa73105ed5331866", - "sha256:21c4a1aae861748d6393a3ff7867473996c139a77f90326d9f4104bebb22d8b8", - "sha256:224166f06ccdaf884bf35690bf4272997c1405de3035d61384ccb5b25a4c1ca8", - "sha256:2262bd3512ba9e9f0e91d287393df6f33c18999317de45629b7bd46c40f16ba9", - "sha256:2585b3c294631a39b33f9f967a59b0fad23b1a71a212eba6bc1e3ca6e6eec9ee", - "sha256:27fb030a4589d2536daec5ff5ba2a128f4f155149efab578fe2de2cb21596d3d", - "sha256:30fbbce11ffeb4f9f91c13fe04899aaf3e9a81708bedf267bf447596b95df26b", - "sha256:3930669c9e6f08a2eed824738c3d5699d11cd47a0ecc13b68ed11595710b1133", - "sha256:3b170e441e91e4f321e46d3cc95a01cb307a4596da54aca59eb78ab0fc03754d", - "sha256:3db71c6f1ab688d8dfc102271cedc9828beac335a3a4372ec54b8bf11b43fd29", - "sha256:48cb7af77238ba16c77879009003f6b22c23425e5ee59cb2c4c103ec040638a5", - "sha256:49eace8ea55fbc42c733defbda1e4feb6d3844ecd875b01bb8b923709e0f5ec8", - "sha256:533eaf5b2a79a3c6f35cbd6a095ae99cac7f4f9c0e08bdcf86c130efd3c32adf", - "sha256:5942a3e05630e1ef5b7b5752e5da6582460a2e4431dae603de89fc45f9ec5aa9", - "sha256:62117486460c83acd3b5d85c12edd5fe20a374630475388cfc89829831d3eb79", - "sha256:650f5f2c9ab1275b4006707411bb6d6bc927886874a287661c3c6f332d4c068b", - "sha256:6dc1e2c9ac292c9a484ef900c568ccb2d6b4dfe26dfa0163d5bc815bb836c78d", - "sha256:73c238ef6e4b64272df7eec976bb016c73d3ab5a6c7e9cd906ab700523d312f3", - "sha256:775a2f70501370e5ba54e1ee3464413bff9bd85bd9a0b25c989698c44a6fb52f", - "sha256:860fcd6db7dce80d0a673a1cc898ce6bc3d4783d195bbe0e911bf8a62c93ff3f", - "sha256:87f47bf9520bba4083d65ab911f8f4c0ac3efa8241993edd74c8dd08ae87552f", - "sha256:960b176e0bb2b4afeaa1cd2002db1e82ae54c9b6e27ea93570a42316524e77cf", - "sha256:a7caf553ccaf715ec05b28c9b2ab2ee3fdb4036626d779aa09cf7cbf54b71445", - "sha256:a947d5298a0bbdd4d15671024bf33e2b7da79a70de600ed29ba7e0fef0539ebb", - "sha256:a97b0d01ae595c997c1d9d8249e2d2da829c2d8a4bdc29bb8f76c11a94915c9a", - "sha256:b7655f809e3420f80ce3bf89737169a9dce73238af594049754a1128132c0da4", - "sha256:c33744d0d1a7322da445c0fe726ea6d4e3ef2dfb0539eadf23dce366f52f546c", - "sha256:c55a9cf5cba80fb88c850915c865b8ed78d5e46e1f2ec1b27692f3eaaf0dca7e", - "sha256:d2f62fb1c914a038921677cfa536d645cb80e3dd07dc4859a3c92d75407b90a5", - "sha256:d8ae6e0df3a608e99ee1acafaafd7db0830106394d54571c1ece57f650124ce9", - "sha256:e355ee9da9c1c03f174efea59292b17a95e0b7b4d7d2a389265f731a9887d5a9", - "sha256:e3e526062c690517b42bba66ffe38aaf8bc99a180a78212e7b22baa86902f690", - "sha256:eb0807323572642ab73fd86fe53d88d843ce617dd1ddf430351ad0759809a0ae", - "sha256:ebff0738be0499d7db74d20dca9f22a7b27deae31e1bf92ea44924fd69eb6251", - "sha256:ed36e854449ff6c2f8ee145f94851fe171298e1e793f44d4f672c4a0d78064e7", - "sha256:ed3d458ded32ff3a58f157b60cc140c88f7ac8c506a1c567b2a9ee8a2fd2ce54", - "sha256:f4a7dca8ccd8023d916b900aa3c626f1bd181bd5b70159479b142f957ff420e4" - ], - "version": "==1.54.0" - }, - "grpcio-status": { - "hashes": [ - "sha256:2154fdb8aad20452488712be6879657b508115ca06139fde8897ea8e9bc79367", - "sha256:c9ce3213e84c6fd8801c31aca3ea4a6b3453eaa40b93a6c0a23ea8999808fa00" - ], - "markers": "python_version >= '3.6'", - "version": "==1.47.0" - }, - "httplib2": { - "hashes": [ - "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", - "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.22.0" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "importlib-metadata": { - "hashes": [ - "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed", - "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705" - ], - "markers": "python_version < '3.8'", - "version": "==6.6.0" - }, - "importlib-resources": { - "hashes": [ - "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6", - "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a" - ], - "markers": "python_version < '3.9'", - "version": "==5.12.0" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "index": "pypi", - "version": "==3.1.2" - }, - "jsonschema": { - "hashes": [ - "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6" - ], - "markers": "python_version >= '3.7'", - "version": "==4.17.3" - }, - "kfp": { - "hashes": [ - "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c" - ], - "index": "pypi", - "version": "==1.8.21" - }, - "kfp-pipeline-spec": { - "hashes": [ - "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==0.1.16" - }, - "kfp-server-api": { - "hashes": [ - "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de" - ], - "version": "==1.8.5" - }, - "kubernetes": { - "hashes": [ - "sha256:213befbb4e5aed95f94950c7eed0c2322fc5a2f8f40932e58d28fdd42d90836c", - "sha256:eb42333dad0bb5caf4e66460c6a4a1a36f0f057a040f35018f6c05a699baed86" - ], - "markers": "python_version >= '3.6'", - "version": "==25.3.0" - }, - "markupsafe": { - "hashes": [ - "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", - "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", - "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", - "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", - "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", - "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", - "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", - "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", - "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", - "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", - "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", - "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", - "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", - "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", - "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", - "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", - "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", - "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", - "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", - "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", - "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", - "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", - "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", - "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", - "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", - "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", - "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", - "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", - "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", - "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", - "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", - "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", - "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", - "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", - "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", - "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", - "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", - "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", - "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", - "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", - "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", - "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", - "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", - "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", - "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", - "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", - "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", - "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", - "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", - "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.2" - }, - "oauthlib": { - "hashes": [ - "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", - "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918" - ], - "markers": "python_version >= '3.6'", - "version": "==3.2.2" - }, - "packaging": { - "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1" - }, - "pkgutil-resolve-name": { - "hashes": [ - "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174", - "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e" - ], - "markers": "python_version < '3.9'", - "version": "==1.3.10" - }, - "proto-plus": { - "hashes": [ - "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165", - "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d" - ], - "markers": "python_version >= '3.6'", - "version": "==1.22.2" - }, - "protobuf": { - "hashes": [ - "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7", - "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c", - "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2", - "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b", - "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050", - "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9", - "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7", - "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454", - "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480", - "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469", - "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c", - "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e", - "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db", - "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905", - "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b", - "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86", - "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4", - "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402", - "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7", - "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4", - "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99", - "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee" - ], - "markers": "python_version >= '3.7'", - "version": "==3.20.3" - }, - "pyasn1": { - "hashes": [ - "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57", - "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.5.0" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", - "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.3.0" - }, - "pydantic": { - "hashes": [ - "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e", - "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6", - "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd", - "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca", - "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b", - "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a", - "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245", - "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d", - "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee", - "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1", - "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3", - "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d", - "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5", - "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914", - "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd", - "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1", - "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e", - "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e", - "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a", - "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd", - "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f", - "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209", - "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d", - "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a", - "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143", - "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918", - "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52", - "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e", - "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f", - "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e", - "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb", - "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe", - "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe", - "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d", - "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209", - "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af" - ], - "markers": "python_version >= '3.7'", - "version": "==1.10.7" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_version >= '3.1'", - "version": "==3.0.9" - }, - "pyrsistent": { - "hashes": [ - "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8", - "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440", - "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a", - "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c", - "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3", - "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393", - "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9", - "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da", - "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf", - "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64", - "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a", - "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3", - "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98", - "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2", - "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8", - "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf", - "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc", - "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7", - "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28", - "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2", - "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b", - "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a", - "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64", - "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19", - "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1", - "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9", - "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c" - ], - "markers": "python_version >= '3.7'", - "version": "==0.19.3" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pyyaml": { - "hashes": [ - "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", - "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", - "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", - "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", - "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", - "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", - "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", - "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", - "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", - "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", - "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", - "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", - "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", - "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", - "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", - "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", - "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", - "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", - "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", - "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", - "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", - "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", - "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", - "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", - "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", - "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", - "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", - "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", - "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.4.1" - }, - "requests": { - "hashes": [ - "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294", - "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4" - ], - "markers": "python_version >= '3.7'", - "version": "==2.30.0" - }, - "requests-oauthlib": { - "hashes": [ - "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", - "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.3.1" - }, - "requests-toolbelt": { - "hashes": [ - "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7", - "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.1" - }, - "rsa": { - "hashes": [ - "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", - "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" - ], - "markers": "python_version >= '3.6'", - "version": "==4.9" - }, - "setuptools": { - "hashes": [ - "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", - "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" - ], - "markers": "python_version >= '3.7'", - "version": "==67.7.2" - }, - "shapely": { - "hashes": [ - "sha256:02dd5d7dc6e46515d88874134dc8fcdc65826bca93c3eecee59d1910c42c1b17", - "sha256:0b4ee3132ee90f07d63db3aea316c4c065ed7a26231458dda0874414a09d6ba3", - "sha256:0d885cb0cf670c1c834df3f371de8726efdf711f18e2a75da5cfa82843a7ab65", - "sha256:147066da0be41b147a61f8eb805dea3b13709dbc873a431ccd7306e24d712bc0", - "sha256:21776184516a16bf82a0c3d6d6a312b3cd15a4cabafc61ee01cf2714a82e8396", - "sha256:2e0a8c2e55f1be1312b51c92b06462ea89e6bb703fab4b114e7a846d941cfc40", - "sha256:2fd15397638df291c427a53d641d3e6fd60458128029c8c4f487190473a69a91", - "sha256:3480657460e939f45a7d359ef0e172a081f249312557fe9aa78c4fd3a362d993", - "sha256:370b574c78dc5af3a198a6da5d9b3d7c04654bd2ef7e80e80a3a0992dfb2d9cd", - "sha256:38f0fbbcb8ca20c16451c966c1f527cc43968e121c8a048af19ed3e339a921cd", - "sha256:4728666fff8cccc65a07448cae72c75a8773fea061c3f4f139c44adc429b18c3", - "sha256:48dcfffb9e225c0481120f4bdf622131c8c95f342b00b158cdbe220edbbe20b6", - "sha256:4b47bb6f9369e8bf3e6dbd33e6a25a47ee02b2874792a529fe04a49bf8bc0df6", - "sha256:532a55ee2a6c52d23d6f7d1567c8f0473635f3b270262c44e1b0c88096827e22", - "sha256:5d7f85c2d35d39ff53c9216bc76b7641c52326f7e09aaad1789a3611a0f812f2", - "sha256:65b21243d8f6bcd421210daf1fabb9de84de2c04353c5b026173b88d17c1a581", - "sha256:66bdac74fbd1d3458fa787191a90fa0ae610f09e2a5ec398c36f968cc0ed743f", - "sha256:6d388c0c1bd878ed1af4583695690aa52234b02ed35f93a1c8486ff52a555838", - "sha256:6fe855e7d45685926b6ba00aaeb5eba5862611f7465775dacd527e081a8ced6d", - "sha256:753ed0e21ab108bd4282405b9b659f2e985e8502b1a72b978eaa51d3496dee19", - "sha256:783bad5f48e2708a0e2f695a34ed382e4162c795cb2f0368b39528ac1d6db7ed", - "sha256:78fb9d929b8ee15cfd424b6c10879ce1907f24e05fb83310fc47d2cd27088e40", - "sha256:84010db15eb364a52b74ea8804ef92a6a930dfc1981d17a369444b6ddec66efd", - "sha256:89164e7a9776a19e29f01369a98529321994e2e4d852b92b7e01d4d9804c55bf", - "sha256:8d086591f744be483b34628b391d741e46f2645fe37594319e0a673cc2c26bcf", - "sha256:8e59817b0fe63d34baedaabba8c393c0090f061917d18fc0bcc2f621937a8f73", - "sha256:99a2f0da0109e81e0c101a2b4cd8412f73f5f299e7b5b2deaf64cd2a100ac118", - "sha256:99ab0ddc05e44acabdbe657c599fdb9b2d82e86c5493bdae216c0c4018a82dee", - "sha256:a23ef3882d6aa203dd3623a3d55d698f59bfbd9f8a3bfed52c2da05a7f0f8640", - "sha256:a354199219c8d836f280b88f2c5102c81bb044ccea45bd361dc38a79f3873714", - "sha256:a74631e511153366c6dbe3229fa93f877e3c87ea8369cd00f1d38c76b0ed9ace", - "sha256:ab38f7b5196ace05725e407cb8cab9ff66edb8e6f7bb36a398e8f73f52a7aaa2", - "sha256:adcf8a11b98af9375e32bff91de184f33a68dc48b9cb9becad4f132fa25cfa3c", - "sha256:b65f5d530ba91e49ffc7c589255e878d2506a8b96ffce69d3b7c4500a9a9eaf8", - "sha256:be9423d5a3577ac2e92c7e758bd8a2b205f5e51a012177a590bc46fc51eb4834", - "sha256:c2822111ddc5bcfb116e6c663e403579d0fe3f147d2a97426011a191c43a7458", - "sha256:c6a9a4a31cd6e86d0fbe8473ceed83d4fe760b19d949fb557ef668defafea0f6", - "sha256:d048f93e42ba578b82758c15d8ae037d08e69d91d9872bca5a1895b118f4e2b0", - "sha256:d8a2b2a65fa7f97115c1cd989fe9d6f39281ca2a8a014f1d4904c1a6e34d7f25", - "sha256:e9c30b311de2513555ab02464ebb76115d242842b29c412f5a9aa0cac57be9f6", - "sha256:ec14ceca36f67cb48b34d02d7f65a9acae15cd72b48e303531893ba4a960f3ea", - "sha256:ef3be705c3eac282a28058e6c6e5503419b250f482320df2172abcbea642c831" - ], - "markers": "python_version >= '3.6'", - "version": "==1.8.5.post1" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "strip-hints": { - "hashes": [ - "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f" - ], - "version": "==0.1.10" - }, - "tabulate": { - "hashes": [ - "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", - "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" - ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" - }, - "termcolor": { - "hashes": [ - "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475", - "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.0" - }, - "typer": { - "hashes": [ - "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2", - "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee" - ], - "markers": "python_version >= '3.6'", - "version": "==0.9.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", - "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" - ], - "markers": "python_version < '3.9'", - "version": "==4.5.0" - }, - "uritemplate": { - "hashes": [ - "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f", - "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.0.1" - }, - "urllib3": { - "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" - }, - "vertex-components": { - "editable": true, - "path": "./../components/vertex-components" - }, - "websocket-client": { - "hashes": [ - "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40", - "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.1" - }, - "wheel": { - "hashes": [ - "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873", - "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247" - ], - "markers": "python_version >= '3.7'", - "version": "==0.40.0" - }, - "wrapt": { - "hashes": [ - "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", - "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", - "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", - "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", - "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", - "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", - "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", - "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", - "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", - "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", - "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", - "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", - "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", - "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", - "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", - "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", - "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", - "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", - "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", - "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", - "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", - "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", - "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", - "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", - "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", - "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", - "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", - "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", - "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", - "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", - "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", - "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", - "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", - "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", - "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", - "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", - "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", - "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", - "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", - "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", - "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", - "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", - "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", - "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", - "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", - "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", - "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", - "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", - "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", - "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", - "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", - "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", - "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", - "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", - "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", - "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", - "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", - "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", - "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", - "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", - "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", - "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", - "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", - "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", - "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", - "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", - "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", - "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", - "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", - "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", - "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", - "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", - "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", - "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", - "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.15.0" - }, - "zipp": { - "hashes": [ - "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", - "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556" - ], - "markers": "python_version < '3.10'", - "version": "==3.15.0" - } - }, - "develop": { - "cfgv": { - "hashes": [ - "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426", - "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.3.1" - }, - "distlib": { - "hashes": [ - "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46", - "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e" - ], - "version": "==0.3.6" - }, - "exceptiongroup": { - "hashes": [ - "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e", - "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785" - ], - "markers": "python_version < '3.11'", - "version": "==1.1.1" - }, - "filelock": { - "hashes": [ - "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9", - "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718" - ], - "markers": "python_version >= '3.7'", - "version": "==3.12.0" - }, - "identify": { - "hashes": [ - "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4", - "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.24" - }, - "importlib-metadata": { - "hashes": [ - "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed", - "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705" - ], - "markers": "python_version < '3.8'", - "version": "==6.6.0" - }, - "iniconfig": { - "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" - }, - "nodeenv": { - "hashes": [ - "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e", - "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==1.7.0" - }, - "packaging": { - "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1" - }, - "platformdirs": { - "hashes": [ - "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4", - "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335" - ], - "markers": "python_version >= '3.7'", - "version": "==3.5.0" - }, - "pluggy": { - "hashes": [ - "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", - "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" - }, - "pre-commit": { - "hashes": [ - "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658", - "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad" - ], - "index": "pypi", - "version": "==2.21.0" - }, - "pytest": { - "hashes": [ - "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362", - "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3" - ], - "index": "pypi", - "version": "==7.3.1" - }, - "pyyaml": { - "hashes": [ - "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", - "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", - "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", - "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", - "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", - "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", - "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", - "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", - "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", - "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", - "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", - "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", - "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", - "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", - "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", - "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", - "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", - "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", - "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", - "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", - "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", - "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", - "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", - "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", - "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", - "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", - "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", - "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", - "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.4.1" - }, - "setuptools": { - "hashes": [ - "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", - "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" - ], - "markers": "python_version >= '3.7'", - "version": "==67.7.2" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11'", - "version": "==2.0.1" - }, - "typing-extensions": { - "hashes": [ - "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", - "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" - ], - "markers": "python_version < '3.9'", - "version": "==4.5.0" - }, - "virtualenv": { - "hashes": [ - "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e", - "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924" - ], - "markers": "python_version >= '3.7'", - "version": "==20.23.0" - }, - "zipp": { - "hashes": [ - "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", - "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556" - ], - "markers": "python_version < '3.10'", - "version": "==3.15.0" - } - } -} diff --git a/pipelines/poetry.lock b/pipelines/poetry.lock new file mode 100644 index 00000000..3c0c989a --- /dev/null +++ b/pipelines/poetry.lock @@ -0,0 +1,1845 @@ +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. + +[[package]] +name = "absl-py" +version = "1.4.0" +description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "absl-py-1.4.0.tar.gz", hash = "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"}, + {file = "absl_py-1.4.0-py3-none-any.whl", hash = "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47"}, +] + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "cachetools" +version = "5.3.0" +description = "Extensible memoizing collections and decorators" +category = "main" +optional = false +python-versions = "~=3.7" +files = [ + {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, + {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, +] + +[[package]] +name = "certifi" +version = "2023.5.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, +] + +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "cloudpickle" +version = "2.2.1" +description = "Extended pickling support for Python objects" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cloudpickle-2.2.1-py3-none-any.whl", hash = "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f"}, + {file = "cloudpickle-2.2.1.tar.gz", hash = "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "deprecated" +version = "1.2.13" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, + {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] + +[[package]] +name = "docstring-parser" +version = "0.15" +description = "Parse Python docstrings in reST, Google and Numpydoc format" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "docstring_parser-0.15-py3-none-any.whl", hash = "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9"}, + {file = "docstring_parser-0.15.tar.gz", hash = "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.12.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, + {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "fire" +version = "0.5.0" +description = "A library for automatically generating command line interfaces." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "fire-0.5.0.tar.gz", hash = "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6"}, +] + +[package.dependencies] +six = "*" +termcolor = "*" + +[[package]] +name = "google-api-core" +version = "2.10.2" +description = "Google API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.10.2.tar.gz", hash = "sha256:10c06f7739fe57781f87523375e8e1a3a4674bf6392cd6131a3222182b971320"}, + {file = "google_api_core-2.10.2-py3-none-any.whl", hash = "sha256:34f24bd1d5f72a8c4519773d99ca6bf080a6c4e041b4e9f024fe230191dda62e"}, +] + +[package.dependencies] +google-auth = ">=1.25.0,<3.0dev" +googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] + +[[package]] +name = "google-api-core" +version = "2.11.0" +description = "Google API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.11.0.tar.gz", hash = "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22"}, + {file = "google_api_core-2.11.0-py3-none-any.whl", hash = "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0dev" +googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio = [ + {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, +] +grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)", "grpcio-status (>=1.49.1,<2.0dev)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] + +[[package]] +name = "google-api-python-client" +version = "1.12.11" +description = "Google API Client Library for Python" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +files = [ + {file = "google-api-python-client-1.12.11.tar.gz", hash = "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9"}, + {file = "google_api_python_client-1.12.11-py2.py3-none-any.whl", hash = "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.21.0,<3dev", markers = "python_version >= \"3\""} +google-auth = {version = ">=1.16.0,<3dev", markers = "python_version >= \"3\""} +google-auth-httplib2 = ">=0.0.3" +httplib2 = ">=0.15.0,<1dev" +six = ">=1.13.0,<2dev" +uritemplate = ">=3.0.0,<4dev" + +[[package]] +name = "google-auth" +version = "2.18.0" +description = "Google Authentication Library" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +files = [ + {file = "google-auth-2.18.0.tar.gz", hash = "sha256:c66b488a8b005b23ccb97b1198b6cece516c91869091ac5b7c267422db2733c7"}, + {file = "google_auth-2.18.0-py2.py3-none-any.whl", hash = "sha256:ef3f3a67fa54d421a1c155864570f9a8de9179cedc937bda496b7a8ca338e936"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} +six = ">=1.9.0" +urllib3 = "<2.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0dev)"] + +[[package]] +name = "google-auth-httplib2" +version = "0.1.0" +description = "Google Authentication Library: httplib2 transport" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, + {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, +] + +[package.dependencies] +google-auth = "*" +httplib2 = ">=0.15.0" +six = "*" + +[[package]] +name = "google-cloud-aiplatform" +version = "1.24.1" +description = "Vertex AI API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-aiplatform-1.24.1.tar.gz", hash = "sha256:0ce9e97bf5c977397e52b3b7c4dc78610c135fbde11a60a6c0b77a4fdf776400"}, + {file = "google_cloud_aiplatform-1.24.1-py2.py3-none-any.whl", hash = "sha256:942765a6bad97e46e262dd6599dc5f171663ce952130e0b0b2eb97e0b1b98bfd"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-bigquery = ">=1.15.0,<4.0.0dev" +google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" +google-cloud-storage = ">=1.32.0,<3.0.0dev" +packaging = ">=14.3" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +shapely = "<2.0.0" + +[package.extras] +autologging = ["mlflow (>=1.27.0,<=2.1.1)"] +cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] +datasets = ["pyarrow (>=3.0.0,<8.0dev)"] +endpoint = ["requests (>=2.28.1)"] +full = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] +lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"] +metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"] +pipelines = ["pyyaml (>=5.3,<6)"] +prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<0.76.0)", "starlette (>=0.17.1)", "uvicorn[standard] (>=0.16.0)"] +private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] +tensorboard = ["tensorflow (>=2.3.0,<3.0.0dev)"] +testing = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "grpcio-testing", "ipython", "kfp", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "scikit-learn", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] +vizier = ["google-vizier (==0.0.4)"] +xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] + +[[package]] +name = "google-cloud-bigquery" +version = "3.10.0" +description = "Google BigQuery API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-bigquery-3.10.0.tar.gz", hash = "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df"}, + {file = "google_cloud_bigquery-3.10.0-py2.py3-none-any.whl", hash = "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-core = ">=1.6.0,<3.0.0dev" +google-resumable-media = ">=0.6.0,<3.0dev" +grpcio = [ + {version = ">=1.47.0,<2.0dev", markers = "python_version < \"3.11\""}, + {version = ">=1.49.1,<2.0dev", markers = "python_version >= \"3.11\""}, +] +packaging = ">=20.0.0" +proto-plus = ">=1.15.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +python-dateutil = ">=2.7.2,<3.0dev" +requests = ">=2.21.0,<3.0.0dev" + +[package.extras] +all = ["Shapely (>=1.8.4,<2.0dev)", "db-dtypes (>=0.3.0,<2.0.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)", "ipywidgets (>=7.7.0)", "opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)", "tqdm (>=4.7.4,<5.0.0dev)"] +bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "pyarrow (>=3.0.0)"] +geopandas = ["Shapely (>=1.8.4,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)"] +ipython = ["ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)"] +ipywidgets = ["ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)"] +opentelemetry = ["opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)"] +pandas = ["db-dtypes (>=0.3.0,<2.0.0dev)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)"] +tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] + +[[package]] +name = "google-cloud-core" +version = "2.3.2" +description = "Google Cloud API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-core-2.3.2.tar.gz", hash = "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a"}, + {file = "google_cloud_core-2.3.2-py2.py3-none-any.whl", hash = "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe"}, +] + +[package.dependencies] +google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" + +[package.extras] +grpc = ["grpcio (>=1.38.0,<2.0dev)"] + +[[package]] +name = "google-cloud-notebooks" +version = "1.4.4" +description = "" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-notebooks-1.4.4.tar.gz", hash = "sha256:76c1c21f2328c0777c4b0333d1a9adbeeea94cfc9af25fe589bef00a9dae41cd"}, + {file = "google_cloud_notebooks-1.4.4-py2.py3-none-any.whl", hash = "sha256:b0204d26663df0e460cd4158d6d0747e9e16d70c607e4da99572a77b51cf2e8d"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-notebooks" +version = "1.7.0" +description = "Google Cloud Notebooks API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-notebooks-1.7.0.tar.gz", hash = "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e"}, + {file = "google_cloud_notebooks-1.7.0-py2.py3-none-any.whl", hash = "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = [ + {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, + {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, +] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-pipeline-components" +version = "1.0.42" +description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "google_cloud_pipeline_components-1.0.42-py3-none-any.whl", hash = "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev" +google-cloud-aiplatform = ">=1.14.0,<2" +google-cloud-notebooks = ">=0.4.0" +google-cloud-storage = ">=2.2.1,<3" +googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio-status = "<=1.47.0" +kfp = ">=1.8.9,<2.0.0" +protobuf = ">=3.19.0,<4.0.0dev" + +[package.extras] +tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev)", "google-cloud-aiplatform (>=1.14.0,<2)", "google-cloud-notebooks (>=0.4.0)", "google-cloud-storage (>=2.2.1,<3)", "googleapis-common-protos (>=1.56.2,<2.0dev)", "grpcio-status (<=1.47.0)", "kfp (>=1.8.9,<2.0.0)", "mock (>=4.0.0)", "protobuf (>=3.19.0,<4.0.0dev)", "pytest (>=6.0.0)"] + +[[package]] +name = "google-cloud-resource-manager" +version = "1.6.3" +description = "Google Cloud Resource Manager API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-resource-manager-1.6.3.tar.gz", hash = "sha256:6cf8a9a74e65a03857896967c79307b75805c752ade3bc41224e6167774bc9c9"}, + {file = "google_cloud_resource_manager-1.6.3-py2.py3-none-any.whl", hash = "sha256:e330883c53c5e3e38a651da85ae0bce201a736b5cd5f9df10941160c6a66ce6e"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-resource-manager" +version = "1.10.0" +description = "Google Cloud Resource Manager API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-resource-manager-1.10.0.tar.gz", hash = "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287"}, + {file = "google_cloud_resource_manager-1.10.0-py2.py3-none-any.whl", hash = "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = [ + {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, + {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, +] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-storage" +version = "2.9.0" +description = "Google Cloud Storage API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-storage-2.9.0.tar.gz", hash = "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3"}, + {file = "google_cloud_storage-2.9.0-py2.py3-none-any.whl", hash = "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" +google-cloud-core = ">=2.3.0,<3.0dev" +google-resumable-media = ">=2.3.2" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +protobuf = ["protobuf (<5.0.0dev)"] + +[[package]] +name = "google-crc32c" +version = "1.5.0" +description = "A python wrapper of the C library 'Google CRC32C'" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win32.whl", hash = "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win32.whl", hash = "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win32.whl", hash = "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win32.whl", hash = "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win32.whl", hash = "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93"}, +] + +[package.extras] +testing = ["pytest"] + +[[package]] +name = "google-resumable-media" +version = "2.5.0" +description = "Utilities for Google Media Downloads and Resumable Uploads" +category = "main" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "google-resumable-media-2.5.0.tar.gz", hash = "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93"}, + {file = "google_resumable_media-2.5.0-py2.py3-none-any.whl", hash = "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec"}, +] + +[package.dependencies] +google-crc32c = ">=1.0,<2.0dev" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] +requests = ["requests (>=2.18.0,<3.0.0dev)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.59.0" +description = "Common protobufs used in Google APIs" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.59.0.tar.gz", hash = "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44"}, + {file = "googleapis_common_protos-1.59.0-py2.py3-none-any.whl", hash = "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.12.6" +description = "IAM API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.12.6.tar.gz", hash = "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f"}, + {file = "grpc_google_iam_v1-0.12.6-py2.py3-none-any.whl", hash = "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.54.2" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.54.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c"}, + {file = "grpcio-1.54.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c0e3155fc5335ec7b3b70f15230234e529ca3607b20a562b6c75fb1b1218874c"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bf88004fe086c786dc56ef8dd6cb49c026833fdd6f42cb853008bce3f907148"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be88c081e33f20630ac3343d8ad9f1125f32987968e9c8c75c051c9800896e8"}, + {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:33d40954199bddbb6a78f8f6f2b2082660f381cd2583ec860a6c2fa7c8400c08"}, + {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b52d00d1793d290c81ad6a27058f5224a7d5f527867e5b580742e1bd211afeee"}, + {file = "grpcio-1.54.2-cp310-cp310-win32.whl", hash = "sha256:881d058c5ccbea7cc2c92085a11947b572498a27ef37d3eef4887f499054dca8"}, + {file = "grpcio-1.54.2-cp310-cp310-win_amd64.whl", hash = "sha256:0212e2f7fdf7592e4b9d365087da30cb4d71e16a6f213120c89b4f8fb35a3ab3"}, + {file = "grpcio-1.54.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:1e623e0cf99a0ac114f091b3083a1848dbc64b0b99e181473b5a4a68d4f6f821"}, + {file = "grpcio-1.54.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:66233ccd2a9371158d96e05d082043d47dadb18cbb294dc5accfdafc2e6b02a7"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:4cb283f630624ebb16c834e5ac3d7880831b07cbe76cb08ab7a271eeaeb8943e"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a1e601ee31ef30a9e2c601d0867e236ac54c922d32ed9f727b70dd5d82600d5"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8da84bbc61a4e92af54dc96344f328e5822d574f767e9b08e1602bb5ddc254a"}, + {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5008964885e8d23313c8e5ea0d44433be9bfd7e24482574e8cc43c02c02fc796"}, + {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2f5a1f1080ccdc7cbaf1171b2cf384d852496fe81ddedeb882d42b85727f610"}, + {file = "grpcio-1.54.2-cp311-cp311-win32.whl", hash = "sha256:b74ae837368cfffeb3f6b498688a123e6b960951be4dec0e869de77e7fa0439e"}, + {file = "grpcio-1.54.2-cp311-cp311-win_amd64.whl", hash = "sha256:8cdbcbd687e576d48f7886157c95052825ca9948c0ed2afdc0134305067be88b"}, + {file = "grpcio-1.54.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:782f4f8662a2157c4190d0f99eaaebc602899e84fb1e562a944e5025929e351c"}, + {file = "grpcio-1.54.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:714242ad0afa63a2e6dabd522ae22e1d76e07060b5af2ddda5474ba4f14c2c94"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:f900ed4ad7a0f1f05d35f955e0943944d5a75f607a836958c6b8ab2a81730ef2"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96a41817d2c763b1d0b32675abeb9179aa2371c72aefdf74b2d2b99a1b92417b"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70fcac7b94f4c904152809a050164650ac81c08e62c27aa9f156ac518029ebbe"}, + {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fd6c6c29717724acf9fc1847c4515d57e4dc12762452457b9cb37461f30a81bb"}, + {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c2392f5b5d84b71d853918687d806c1aa4308109e5ca158a16e16a6be71041eb"}, + {file = "grpcio-1.54.2-cp37-cp37m-win_amd64.whl", hash = "sha256:51630c92591d6d3fe488a7c706bd30a61594d144bac7dee20c8e1ce78294f474"}, + {file = "grpcio-1.54.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:b04202453941a63b36876a7172b45366dc0cde10d5fd7855c0f4a4e673c0357a"}, + {file = "grpcio-1.54.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:89dde0ac72a858a44a2feb8e43dc68c0c66f7857a23f806e81e1b7cc7044c9cf"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:09d4bfd84686cd36fd11fd45a0732c7628308d094b14d28ea74a81db0bce2ed3"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fc2b4edb938c8faa4b3c3ea90ca0dd89b7565a049e8e4e11b77e60e4ed2cc05"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61f7203e2767800edee7a1e1040aaaf124a35ce0c7fe0883965c6b762defe598"}, + {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e416c8baf925b5a1aff31f7f5aecc0060b25d50cce3a5a7255dc5cf2f1d4e5eb"}, + {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dc80c9c6b608bf98066a038e0172013a49cfa9a08d53335aefefda2c64fc68f4"}, + {file = "grpcio-1.54.2-cp38-cp38-win32.whl", hash = "sha256:8d6192c37a30a115f4663592861f50e130caed33efc4eec24d92ec881c92d771"}, + {file = "grpcio-1.54.2-cp38-cp38-win_amd64.whl", hash = "sha256:46a057329938b08e5f0e12ea3d7aed3ecb20a0c34c4a324ef34e00cecdb88a12"}, + {file = "grpcio-1.54.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:2296356b5c9605b73ed6a52660b538787094dae13786ba53080595d52df13a98"}, + {file = "grpcio-1.54.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:c72956972e4b508dd39fdc7646637a791a9665b478e768ffa5f4fe42123d5de1"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:9bdbb7624d65dc0ed2ed8e954e79ab1724526f09b1efa88dcd9a1815bf28be5f"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c44e1a765b31e175c391f22e8fc73b2a2ece0e5e6ff042743d8109b5d2eff9f"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cc928cfe6c360c1df636cf7991ab96f059666ac7b40b75a769410cc6217df9c"}, + {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a08920fa1a97d4b8ee5db2f31195de4a9def1a91bc003544eb3c9e6b8977960a"}, + {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4864f99aac207e3e45c5e26c6cbb0ad82917869abc2f156283be86c05286485c"}, + {file = "grpcio-1.54.2-cp39-cp39-win32.whl", hash = "sha256:b38b3de8cff5bc70f8f9c615f51b48eff7313fc9aca354f09f81b73036e7ddfa"}, + {file = "grpcio-1.54.2-cp39-cp39-win_amd64.whl", hash = "sha256:be48496b0e00460717225e7680de57c38be1d8629dc09dadcd1b3389d70d942b"}, + {file = "grpcio-1.54.2.tar.gz", hash = "sha256:50a9f075eeda5097aa9a182bb3877fe1272875e45370368ac0ee16ab9e22d019"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.54.2)"] + +[[package]] +name = "grpcio-status" +version = "1.47.0" +description = "Status proto mapping for gRPC" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.47.0.tar.gz", hash = "sha256:c9ce3213e84c6fd8801c31aca3ea4a6b3453eaa40b93a6c0a23ea8999808fa00"}, + {file = "grpcio_status-1.47.0-py3-none-any.whl", hash = "sha256:2154fdb8aad20452488712be6879657b508115ca06139fde8897ea8e9bc79367"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.47.0" +protobuf = ">=3.12.0" + +[[package]] +name = "httplib2" +version = "0.22.0" +description = "A comprehensive HTTP client library." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, + {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, +] + +[package.dependencies] +pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} + +[[package]] +name = "identify" +version = "2.5.24" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, + {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "kfp" +version = "1.8.21" +description = "KubeFlow Pipelines SDK" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "kfp-1.8.21.tar.gz", hash = "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c"}, +] + +[package.dependencies] +absl-py = ">=0.9,<2" +click = ">=7.1.2,<9" +cloudpickle = ">=2.0.0,<3" +Deprecated = ">=1.2.7,<2" +docstring-parser = ">=0.7.3,<1" +fire = ">=0.3.1,<1" +google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-python-client = ">=1.7.8,<2" +google-auth = ">=1.6.1,<3" +google-cloud-storage = ">=1.20.0,<3" +jsonschema = ">=3.0.1,<5" +kfp-pipeline-spec = ">=0.1.16,<0.2.0" +kfp-server-api = ">=1.1.2,<2.0.0" +kubernetes = ">=8.0.0,<26" +protobuf = ">=3.13.0,<4" +pydantic = ">=1.8.2,<2" +PyYAML = ">=5.3,<6" +requests-toolbelt = ">=0.8.0,<1" +strip-hints = ">=0.1.8,<1" +tabulate = ">=0.8.6,<1" +typer = ">=0.3.2,<1.0" +uritemplate = ">=3.0.1,<4" +urllib3 = "<2" + +[package.extras] +all = ["docker"] + +[[package]] +name = "kfp-pipeline-spec" +version = "0.1.16" +description = "Kubeflow Pipelines pipeline spec" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "kfp_pipeline_spec-0.1.16-py3-none-any.whl", hash = "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25"}, +] + +[package.dependencies] +protobuf = ">=3.13.0,<4" + +[[package]] +name = "kfp-server-api" +version = "1.8.5" +description = "Kubeflow Pipelines API" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "kfp-server-api-1.8.5.tar.gz", hash = "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de"}, +] + +[package.dependencies] +certifi = "*" +python-dateutil = "*" +six = ">=1.10" +urllib3 = ">=1.15" + +[[package]] +name = "kubernetes" +version = "25.3.0" +description = "Kubernetes python client" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "kubernetes-25.3.0-py2.py3-none-any.whl", hash = "sha256:eb42333dad0bb5caf4e66460c6a4a1a36f0f057a040f35018f6c05a699baed86"}, + {file = "kubernetes-25.3.0.tar.gz", hash = "sha256:213befbb4e5aed95f94950c7eed0c2322fc5a2f8f40932e58d28fdd42d90836c"}, +] + +[package.dependencies] +certifi = ">=14.05.14" +google-auth = ">=1.0.1" +python-dateutil = ">=2.5.3" +pyyaml = ">=5.4.1" +requests = "*" +requests-oauthlib = "*" +setuptools = ">=21.0.0" +six = ">=1.9.0" +urllib3 = ">=1.24.2" +websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" + +[package.extras] +adal = ["adal (>=1.0.2)"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "platformdirs" +version = "3.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.21.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "proto-plus" +version = "1.22.2" +description = "Beautiful, Pythonic protocol buffers." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.22.2.tar.gz", hash = "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165"}, + {file = "proto_plus-1.22.2-py3-none-any.whl", hash = "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "3.20.3" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, + {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, + {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, + {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, + {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, + {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, + {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, + {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, + {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, + {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, + {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, + {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, + {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, + {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, + {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, + {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.0-py2.py3-none-any.whl", hash = "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57"}, + {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pydantic" +version = "1.10.7" +description = "Data validation and settings management using python type hints" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, + {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, + {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, + {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, + {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, + {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, + {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, + {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, + {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, + {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrsistent" +version = "0.19.3" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, + {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, + {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, + {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, + {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, + {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, +] + +[[package]] +name = "pytest" +version = "7.3.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "5.4.1" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, +] + +[[package]] +name = "requests" +version = "2.30.0" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.30.0-py3-none-any.whl", hash = "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294"}, + {file = "requests-2.30.0.tar.gz", hash = "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "requests-toolbelt" +version = "0.10.1" +description = "A utility belt for advanced users of python-requests" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"}, + {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +category = "main" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "setuptools" +version = "67.7.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, + {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "shapely" +version = "1.8.5.post1" +description = "Geometric objects, predicates, and operations" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d048f93e42ba578b82758c15d8ae037d08e69d91d9872bca5a1895b118f4e2b0"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99ab0ddc05e44acabdbe657c599fdb9b2d82e86c5493bdae216c0c4018a82dee"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a2f0da0109e81e0c101a2b4cd8412f73f5f299e7b5b2deaf64cd2a100ac118"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6fe855e7d45685926b6ba00aaeb5eba5862611f7465775dacd527e081a8ced6d"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec14ceca36f67cb48b34d02d7f65a9acae15cd72b48e303531893ba4a960f3ea"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a2b2a65fa7f97115c1cd989fe9d6f39281ca2a8a014f1d4904c1a6e34d7f25"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-win32.whl", hash = "sha256:21776184516a16bf82a0c3d6d6a312b3cd15a4cabafc61ee01cf2714a82e8396"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-win_amd64.whl", hash = "sha256:a354199219c8d836f280b88f2c5102c81bb044ccea45bd361dc38a79f3873714"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:783bad5f48e2708a0e2f695a34ed382e4162c795cb2f0368b39528ac1d6db7ed"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a23ef3882d6aa203dd3623a3d55d698f59bfbd9f8a3bfed52c2da05a7f0f8640"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab38f7b5196ace05725e407cb8cab9ff66edb8e6f7bb36a398e8f73f52a7aaa2"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d086591f744be483b34628b391d741e46f2645fe37594319e0a673cc2c26bcf"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4728666fff8cccc65a07448cae72c75a8773fea061c3f4f139c44adc429b18c3"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-win32.whl", hash = "sha256:84010db15eb364a52b74ea8804ef92a6a930dfc1981d17a369444b6ddec66efd"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-win_amd64.whl", hash = "sha256:48dcfffb9e225c0481120f4bdf622131c8c95f342b00b158cdbe220edbbe20b6"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2fd15397638df291c427a53d641d3e6fd60458128029c8c4f487190473a69a91"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a74631e511153366c6dbe3229fa93f877e3c87ea8369cd00f1d38c76b0ed9ace"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:66bdac74fbd1d3458fa787191a90fa0ae610f09e2a5ec398c36f968cc0ed743f"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-win32.whl", hash = "sha256:6d388c0c1bd878ed1af4583695690aa52234b02ed35f93a1c8486ff52a555838"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:be9423d5a3577ac2e92c7e758bd8a2b205f5e51a012177a590bc46fc51eb4834"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5d7f85c2d35d39ff53c9216bc76b7641c52326f7e09aaad1789a3611a0f812f2"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:adcf8a11b98af9375e32bff91de184f33a68dc48b9cb9becad4f132fa25cfa3c"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:753ed0e21ab108bd4282405b9b659f2e985e8502b1a72b978eaa51d3496dee19"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-win32.whl", hash = "sha256:65b21243d8f6bcd421210daf1fabb9de84de2c04353c5b026173b88d17c1a581"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:370b574c78dc5af3a198a6da5d9b3d7c04654bd2ef7e80e80a3a0992dfb2d9cd"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:532a55ee2a6c52d23d6f7d1567c8f0473635f3b270262c44e1b0c88096827e22"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3480657460e939f45a7d359ef0e172a081f249312557fe9aa78c4fd3a362d993"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b65f5d530ba91e49ffc7c589255e878d2506a8b96ffce69d3b7c4500a9a9eaf8"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:147066da0be41b147a61f8eb805dea3b13709dbc873a431ccd7306e24d712bc0"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2822111ddc5bcfb116e6c663e403579d0fe3f147d2a97426011a191c43a7458"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b47bb6f9369e8bf3e6dbd33e6a25a47ee02b2874792a529fe04a49bf8bc0df6"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-win32.whl", hash = "sha256:2e0a8c2e55f1be1312b51c92b06462ea89e6bb703fab4b114e7a846d941cfc40"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-win_amd64.whl", hash = "sha256:0d885cb0cf670c1c834df3f371de8726efdf711f18e2a75da5cfa82843a7ab65"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0b4ee3132ee90f07d63db3aea316c4c065ed7a26231458dda0874414a09d6ba3"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:02dd5d7dc6e46515d88874134dc8fcdc65826bca93c3eecee59d1910c42c1b17"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6a9a4a31cd6e86d0fbe8473ceed83d4fe760b19d949fb557ef668defafea0f6"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:38f0fbbcb8ca20c16451c966c1f527cc43968e121c8a048af19ed3e339a921cd"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78fb9d929b8ee15cfd424b6c10879ce1907f24e05fb83310fc47d2cd27088e40"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89164e7a9776a19e29f01369a98529321994e2e4d852b92b7e01d4d9804c55bf"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-win32.whl", hash = "sha256:8e59817b0fe63d34baedaabba8c393c0090f061917d18fc0bcc2f621937a8f73"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-win_amd64.whl", hash = "sha256:e9c30b311de2513555ab02464ebb76115d242842b29c412f5a9aa0cac57be9f6"}, + {file = "Shapely-1.8.5.post1.tar.gz", hash = "sha256:ef3be705c3eac282a28058e6c6e5503419b250f482320df2172abcbea642c831"}, +] + +[package.extras] +all = ["numpy", "pytest", "pytest-cov"] +test = ["pytest", "pytest-cov"] +vectorized = ["numpy"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "strip-hints" +version = "0.1.10" +description = "Function and command-line program to strip Python type hints." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "strip-hints-0.1.10.tar.gz", hash = "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f"}, +] + +[package.dependencies] +wheel = "*" + +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "termcolor" +version = "2.3.0" +description = "ANSI color formatting for output in terminal" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, + {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typer" +version = "0.9.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.5.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, +] + +[[package]] +name = "uritemplate" +version = "3.0.1" +description = "URI templates" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, + {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, +] + +[[package]] +name = "urllib3" +version = "1.26.15" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, + {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.23.0" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, + {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, +] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.11,<4" +platformdirs = ">=3.2,<4" + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] + +[[package]] +name = "websocket-client" +version = "1.5.1" +description = "WebSocket client for Python with low level API options" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websocket-client-1.5.1.tar.gz", hash = "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40"}, + {file = "websocket_client-1.5.1-py3-none-any.whl", hash = "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"}, +] + +[package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "wheel" +version = "0.40.0" +description = "A built-package format for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, + {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, +] + +[package.extras] +test = ["pytest (>=6.0.0)"] + +[[package]] +name = "wrapt" +version = "1.15.0" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, + {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, + {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, + {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, + {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, + {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, + {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, + {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, + {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, + {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, + {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, + {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, + {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, + {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, + {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, + {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, + {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, + {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, + {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "61dd42065ded5aa173586b15285aee373d40d89710fff18ae49e546999a29156" diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index a5e2cbb7..871e114b 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -1,30 +1,28 @@ -[project] +[tool.poetry] name = "turbo-template" -authors = [ - {name = "Example User", email = "user@example.com"}, -] -description = "Turbo Template" +version = "0.1.0" +authors = ["roberta nwokonko "] +description = "Turbo Template developed at Datatonic" readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.7", -] -requires-python = ">=3.7" -dynamic = ["version"] -dependencies = [ - "kfp == 1.8.21", + "Programming Language :: Python :: 3.9", ] -[project.optional-dependencies] -tests = [ - "google-cloud-bigquery == 2.30.0", - "pytest >= 7.3.1,<8.0.0", -] +[tool.poetry.dependencies] +python = "^3.9" +Jinja2 = ">=3.0.1,<4.0.0" +google-cloud-aiplatform = "1.24.1" +google-cloud-pipeline-components = "1.0.42" + +[tool.poetry.group.dev.dependencies] +pytest = ">=7.3.1,<8.0.0" +pre-commit = ">=2.14.1,<3.0.0" [build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta:__legacy__" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" [tool.flake8] max-line-length = 88 @@ -43,3 +41,4 @@ pythonpath = [ ] testpaths = "tests" junit_family = "xunit2" + diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..c807fdc1 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1845 @@ +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. + +[[package]] +name = "absl-py" +version = "1.4.0" +description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "absl-py-1.4.0.tar.gz", hash = "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"}, + {file = "absl_py-1.4.0-py3-none-any.whl", hash = "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47"}, +] + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "cachetools" +version = "5.3.0" +description = "Extensible memoizing collections and decorators" +category = "main" +optional = false +python-versions = "~=3.7" +files = [ + {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, + {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, +] + +[[package]] +name = "certifi" +version = "2023.5.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, +] + +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "cloudpickle" +version = "2.2.1" +description = "Extended pickling support for Python objects" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cloudpickle-2.2.1-py3-none-any.whl", hash = "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f"}, + {file = "cloudpickle-2.2.1.tar.gz", hash = "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "deprecated" +version = "1.2.13" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, + {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] + +[[package]] +name = "docstring-parser" +version = "0.15" +description = "Parse Python docstrings in reST, Google and Numpydoc format" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "docstring_parser-0.15-py3-none-any.whl", hash = "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9"}, + {file = "docstring_parser-0.15.tar.gz", hash = "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.12.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, + {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "fire" +version = "0.5.0" +description = "A library for automatically generating command line interfaces." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "fire-0.5.0.tar.gz", hash = "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6"}, +] + +[package.dependencies] +six = "*" +termcolor = "*" + +[[package]] +name = "google-api-core" +version = "2.10.2" +description = "Google API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.10.2.tar.gz", hash = "sha256:10c06f7739fe57781f87523375e8e1a3a4674bf6392cd6131a3222182b971320"}, + {file = "google_api_core-2.10.2-py3-none-any.whl", hash = "sha256:34f24bd1d5f72a8c4519773d99ca6bf080a6c4e041b4e9f024fe230191dda62e"}, +] + +[package.dependencies] +google-auth = ">=1.25.0,<3.0dev" +googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] + +[[package]] +name = "google-api-core" +version = "2.11.0" +description = "Google API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.11.0.tar.gz", hash = "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22"}, + {file = "google_api_core-2.11.0-py3-none-any.whl", hash = "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0dev" +googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio = [ + {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, +] +grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)", "grpcio-status (>=1.49.1,<2.0dev)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] + +[[package]] +name = "google-api-python-client" +version = "1.12.11" +description = "Google API Client Library for Python" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +files = [ + {file = "google-api-python-client-1.12.11.tar.gz", hash = "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9"}, + {file = "google_api_python_client-1.12.11-py2.py3-none-any.whl", hash = "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.21.0,<3dev", markers = "python_version >= \"3\""} +google-auth = {version = ">=1.16.0,<3dev", markers = "python_version >= \"3\""} +google-auth-httplib2 = ">=0.0.3" +httplib2 = ">=0.15.0,<1dev" +six = ">=1.13.0,<2dev" +uritemplate = ">=3.0.0,<4dev" + +[[package]] +name = "google-auth" +version = "2.18.0" +description = "Google Authentication Library" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +files = [ + {file = "google-auth-2.18.0.tar.gz", hash = "sha256:c66b488a8b005b23ccb97b1198b6cece516c91869091ac5b7c267422db2733c7"}, + {file = "google_auth-2.18.0-py2.py3-none-any.whl", hash = "sha256:ef3f3a67fa54d421a1c155864570f9a8de9179cedc937bda496b7a8ca338e936"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} +six = ">=1.9.0" +urllib3 = "<2.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0dev)"] + +[[package]] +name = "google-auth-httplib2" +version = "0.1.0" +description = "Google Authentication Library: httplib2 transport" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, + {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, +] + +[package.dependencies] +google-auth = "*" +httplib2 = ">=0.15.0" +six = "*" + +[[package]] +name = "google-cloud-aiplatform" +version = "1.24.1" +description = "Vertex AI API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-aiplatform-1.24.1.tar.gz", hash = "sha256:0ce9e97bf5c977397e52b3b7c4dc78610c135fbde11a60a6c0b77a4fdf776400"}, + {file = "google_cloud_aiplatform-1.24.1-py2.py3-none-any.whl", hash = "sha256:942765a6bad97e46e262dd6599dc5f171663ce952130e0b0b2eb97e0b1b98bfd"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-bigquery = ">=1.15.0,<4.0.0dev" +google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" +google-cloud-storage = ">=1.32.0,<3.0.0dev" +packaging = ">=14.3" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +shapely = "<2.0.0" + +[package.extras] +autologging = ["mlflow (>=1.27.0,<=2.1.1)"] +cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] +datasets = ["pyarrow (>=3.0.0,<8.0dev)"] +endpoint = ["requests (>=2.28.1)"] +full = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] +lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"] +metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"] +pipelines = ["pyyaml (>=5.3,<6)"] +prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<0.76.0)", "starlette (>=0.17.1)", "uvicorn[standard] (>=0.16.0)"] +private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] +tensorboard = ["tensorflow (>=2.3.0,<3.0.0dev)"] +testing = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "grpcio-testing", "ipython", "kfp", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "scikit-learn", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] +vizier = ["google-vizier (==0.0.4)"] +xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] + +[[package]] +name = "google-cloud-bigquery" +version = "3.10.0" +description = "Google BigQuery API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-bigquery-3.10.0.tar.gz", hash = "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df"}, + {file = "google_cloud_bigquery-3.10.0-py2.py3-none-any.whl", hash = "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-core = ">=1.6.0,<3.0.0dev" +google-resumable-media = ">=0.6.0,<3.0dev" +grpcio = [ + {version = ">=1.47.0,<2.0dev", markers = "python_version < \"3.11\""}, + {version = ">=1.49.1,<2.0dev", markers = "python_version >= \"3.11\""}, +] +packaging = ">=20.0.0" +proto-plus = ">=1.15.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +python-dateutil = ">=2.7.2,<3.0dev" +requests = ">=2.21.0,<3.0.0dev" + +[package.extras] +all = ["Shapely (>=1.8.4,<2.0dev)", "db-dtypes (>=0.3.0,<2.0.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)", "ipywidgets (>=7.7.0)", "opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)", "tqdm (>=4.7.4,<5.0.0dev)"] +bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "pyarrow (>=3.0.0)"] +geopandas = ["Shapely (>=1.8.4,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)"] +ipython = ["ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)"] +ipywidgets = ["ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)"] +opentelemetry = ["opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)"] +pandas = ["db-dtypes (>=0.3.0,<2.0.0dev)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)"] +tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] + +[[package]] +name = "google-cloud-core" +version = "2.3.2" +description = "Google Cloud API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-core-2.3.2.tar.gz", hash = "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a"}, + {file = "google_cloud_core-2.3.2-py2.py3-none-any.whl", hash = "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe"}, +] + +[package.dependencies] +google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" + +[package.extras] +grpc = ["grpcio (>=1.38.0,<2.0dev)"] + +[[package]] +name = "google-cloud-notebooks" +version = "1.4.4" +description = "" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-notebooks-1.4.4.tar.gz", hash = "sha256:76c1c21f2328c0777c4b0333d1a9adbeeea94cfc9af25fe589bef00a9dae41cd"}, + {file = "google_cloud_notebooks-1.4.4-py2.py3-none-any.whl", hash = "sha256:b0204d26663df0e460cd4158d6d0747e9e16d70c607e4da99572a77b51cf2e8d"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-notebooks" +version = "1.7.0" +description = "Google Cloud Notebooks API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-notebooks-1.7.0.tar.gz", hash = "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e"}, + {file = "google_cloud_notebooks-1.7.0-py2.py3-none-any.whl", hash = "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = [ + {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, + {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, +] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-pipeline-components" +version = "1.0.42" +description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "google_cloud_pipeline_components-1.0.42-py3-none-any.whl", hash = "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev" +google-cloud-aiplatform = ">=1.14.0,<2" +google-cloud-notebooks = ">=0.4.0" +google-cloud-storage = ">=2.2.1,<3" +googleapis-common-protos = ">=1.56.2,<2.0dev" +grpcio-status = "<=1.47.0" +kfp = ">=1.8.9,<2.0.0" +protobuf = ">=3.19.0,<4.0.0dev" + +[package.extras] +tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev)", "google-cloud-aiplatform (>=1.14.0,<2)", "google-cloud-notebooks (>=0.4.0)", "google-cloud-storage (>=2.2.1,<3)", "googleapis-common-protos (>=1.56.2,<2.0dev)", "grpcio-status (<=1.47.0)", "kfp (>=1.8.9,<2.0.0)", "mock (>=4.0.0)", "protobuf (>=3.19.0,<4.0.0dev)", "pytest (>=6.0.0)"] + +[[package]] +name = "google-cloud-resource-manager" +version = "1.6.3" +description = "Google Cloud Resource Manager API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-resource-manager-1.6.3.tar.gz", hash = "sha256:6cf8a9a74e65a03857896967c79307b75805c752ade3bc41224e6167774bc9c9"}, + {file = "google_cloud_resource_manager-1.6.3-py2.py3-none-any.whl", hash = "sha256:e330883c53c5e3e38a651da85ae0bce201a736b5cd5f9df10941160c6a66ce6e"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = ">=1.22.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-resource-manager" +version = "1.10.0" +description = "Google Cloud Resource Manager API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-resource-manager-1.10.0.tar.gz", hash = "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287"}, + {file = "google_cloud_resource_manager-1.10.0-py2.py3-none-any.whl", hash = "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = [ + {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, + {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, +] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-cloud-storage" +version = "2.9.0" +description = "Google Cloud Storage API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-storage-2.9.0.tar.gz", hash = "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3"}, + {file = "google_cloud_storage-2.9.0-py2.py3-none-any.whl", hash = "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" +google-cloud-core = ">=2.3.0,<3.0dev" +google-resumable-media = ">=2.3.2" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +protobuf = ["protobuf (<5.0.0dev)"] + +[[package]] +name = "google-crc32c" +version = "1.5.0" +description = "A python wrapper of the C library 'Google CRC32C'" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win32.whl", hash = "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win32.whl", hash = "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win32.whl", hash = "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win32.whl", hash = "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win32.whl", hash = "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93"}, +] + +[package.extras] +testing = ["pytest"] + +[[package]] +name = "google-resumable-media" +version = "2.5.0" +description = "Utilities for Google Media Downloads and Resumable Uploads" +category = "main" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "google-resumable-media-2.5.0.tar.gz", hash = "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93"}, + {file = "google_resumable_media-2.5.0-py2.py3-none-any.whl", hash = "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec"}, +] + +[package.dependencies] +google-crc32c = ">=1.0,<2.0dev" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] +requests = ["requests (>=2.18.0,<3.0.0dev)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.59.0" +description = "Common protobufs used in Google APIs" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.59.0.tar.gz", hash = "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44"}, + {file = "googleapis_common_protos-1.59.0-py2.py3-none-any.whl", hash = "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0dev", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.12.6" +description = "IAM API client library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.12.6.tar.gz", hash = "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f"}, + {file = "grpc_google_iam_v1-0.12.6-py2.py3-none-any.whl", hash = "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "grpcio" +version = "1.54.2" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.54.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c"}, + {file = "grpcio-1.54.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c0e3155fc5335ec7b3b70f15230234e529ca3607b20a562b6c75fb1b1218874c"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bf88004fe086c786dc56ef8dd6cb49c026833fdd6f42cb853008bce3f907148"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be88c081e33f20630ac3343d8ad9f1125f32987968e9c8c75c051c9800896e8"}, + {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:33d40954199bddbb6a78f8f6f2b2082660f381cd2583ec860a6c2fa7c8400c08"}, + {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b52d00d1793d290c81ad6a27058f5224a7d5f527867e5b580742e1bd211afeee"}, + {file = "grpcio-1.54.2-cp310-cp310-win32.whl", hash = "sha256:881d058c5ccbea7cc2c92085a11947b572498a27ef37d3eef4887f499054dca8"}, + {file = "grpcio-1.54.2-cp310-cp310-win_amd64.whl", hash = "sha256:0212e2f7fdf7592e4b9d365087da30cb4d71e16a6f213120c89b4f8fb35a3ab3"}, + {file = "grpcio-1.54.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:1e623e0cf99a0ac114f091b3083a1848dbc64b0b99e181473b5a4a68d4f6f821"}, + {file = "grpcio-1.54.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:66233ccd2a9371158d96e05d082043d47dadb18cbb294dc5accfdafc2e6b02a7"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:4cb283f630624ebb16c834e5ac3d7880831b07cbe76cb08ab7a271eeaeb8943e"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a1e601ee31ef30a9e2c601d0867e236ac54c922d32ed9f727b70dd5d82600d5"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8da84bbc61a4e92af54dc96344f328e5822d574f767e9b08e1602bb5ddc254a"}, + {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5008964885e8d23313c8e5ea0d44433be9bfd7e24482574e8cc43c02c02fc796"}, + {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2f5a1f1080ccdc7cbaf1171b2cf384d852496fe81ddedeb882d42b85727f610"}, + {file = "grpcio-1.54.2-cp311-cp311-win32.whl", hash = "sha256:b74ae837368cfffeb3f6b498688a123e6b960951be4dec0e869de77e7fa0439e"}, + {file = "grpcio-1.54.2-cp311-cp311-win_amd64.whl", hash = "sha256:8cdbcbd687e576d48f7886157c95052825ca9948c0ed2afdc0134305067be88b"}, + {file = "grpcio-1.54.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:782f4f8662a2157c4190d0f99eaaebc602899e84fb1e562a944e5025929e351c"}, + {file = "grpcio-1.54.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:714242ad0afa63a2e6dabd522ae22e1d76e07060b5af2ddda5474ba4f14c2c94"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:f900ed4ad7a0f1f05d35f955e0943944d5a75f607a836958c6b8ab2a81730ef2"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96a41817d2c763b1d0b32675abeb9179aa2371c72aefdf74b2d2b99a1b92417b"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70fcac7b94f4c904152809a050164650ac81c08e62c27aa9f156ac518029ebbe"}, + {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fd6c6c29717724acf9fc1847c4515d57e4dc12762452457b9cb37461f30a81bb"}, + {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c2392f5b5d84b71d853918687d806c1aa4308109e5ca158a16e16a6be71041eb"}, + {file = "grpcio-1.54.2-cp37-cp37m-win_amd64.whl", hash = "sha256:51630c92591d6d3fe488a7c706bd30a61594d144bac7dee20c8e1ce78294f474"}, + {file = "grpcio-1.54.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:b04202453941a63b36876a7172b45366dc0cde10d5fd7855c0f4a4e673c0357a"}, + {file = "grpcio-1.54.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:89dde0ac72a858a44a2feb8e43dc68c0c66f7857a23f806e81e1b7cc7044c9cf"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:09d4bfd84686cd36fd11fd45a0732c7628308d094b14d28ea74a81db0bce2ed3"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fc2b4edb938c8faa4b3c3ea90ca0dd89b7565a049e8e4e11b77e60e4ed2cc05"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61f7203e2767800edee7a1e1040aaaf124a35ce0c7fe0883965c6b762defe598"}, + {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e416c8baf925b5a1aff31f7f5aecc0060b25d50cce3a5a7255dc5cf2f1d4e5eb"}, + {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dc80c9c6b608bf98066a038e0172013a49cfa9a08d53335aefefda2c64fc68f4"}, + {file = "grpcio-1.54.2-cp38-cp38-win32.whl", hash = "sha256:8d6192c37a30a115f4663592861f50e130caed33efc4eec24d92ec881c92d771"}, + {file = "grpcio-1.54.2-cp38-cp38-win_amd64.whl", hash = "sha256:46a057329938b08e5f0e12ea3d7aed3ecb20a0c34c4a324ef34e00cecdb88a12"}, + {file = "grpcio-1.54.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:2296356b5c9605b73ed6a52660b538787094dae13786ba53080595d52df13a98"}, + {file = "grpcio-1.54.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:c72956972e4b508dd39fdc7646637a791a9665b478e768ffa5f4fe42123d5de1"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:9bdbb7624d65dc0ed2ed8e954e79ab1724526f09b1efa88dcd9a1815bf28be5f"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c44e1a765b31e175c391f22e8fc73b2a2ece0e5e6ff042743d8109b5d2eff9f"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cc928cfe6c360c1df636cf7991ab96f059666ac7b40b75a769410cc6217df9c"}, + {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a08920fa1a97d4b8ee5db2f31195de4a9def1a91bc003544eb3c9e6b8977960a"}, + {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4864f99aac207e3e45c5e26c6cbb0ad82917869abc2f156283be86c05286485c"}, + {file = "grpcio-1.54.2-cp39-cp39-win32.whl", hash = "sha256:b38b3de8cff5bc70f8f9c615f51b48eff7313fc9aca354f09f81b73036e7ddfa"}, + {file = "grpcio-1.54.2-cp39-cp39-win_amd64.whl", hash = "sha256:be48496b0e00460717225e7680de57c38be1d8629dc09dadcd1b3389d70d942b"}, + {file = "grpcio-1.54.2.tar.gz", hash = "sha256:50a9f075eeda5097aa9a182bb3877fe1272875e45370368ac0ee16ab9e22d019"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.54.2)"] + +[[package]] +name = "grpcio-status" +version = "1.47.0" +description = "Status proto mapping for gRPC" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.47.0.tar.gz", hash = "sha256:c9ce3213e84c6fd8801c31aca3ea4a6b3453eaa40b93a6c0a23ea8999808fa00"}, + {file = "grpcio_status-1.47.0-py3-none-any.whl", hash = "sha256:2154fdb8aad20452488712be6879657b508115ca06139fde8897ea8e9bc79367"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.47.0" +protobuf = ">=3.12.0" + +[[package]] +name = "httplib2" +version = "0.22.0" +description = "A comprehensive HTTP client library." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, + {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, +] + +[package.dependencies] +pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} + +[[package]] +name = "identify" +version = "2.5.24" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, + {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.17.3" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "kfp" +version = "1.8.21" +description = "KubeFlow Pipelines SDK" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "kfp-1.8.21.tar.gz", hash = "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c"}, +] + +[package.dependencies] +absl-py = ">=0.9,<2" +click = ">=7.1.2,<9" +cloudpickle = ">=2.0.0,<3" +Deprecated = ">=1.2.7,<2" +docstring-parser = ">=0.7.3,<1" +fire = ">=0.3.1,<1" +google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-python-client = ">=1.7.8,<2" +google-auth = ">=1.6.1,<3" +google-cloud-storage = ">=1.20.0,<3" +jsonschema = ">=3.0.1,<5" +kfp-pipeline-spec = ">=0.1.16,<0.2.0" +kfp-server-api = ">=1.1.2,<2.0.0" +kubernetes = ">=8.0.0,<26" +protobuf = ">=3.13.0,<4" +pydantic = ">=1.8.2,<2" +PyYAML = ">=5.3,<6" +requests-toolbelt = ">=0.8.0,<1" +strip-hints = ">=0.1.8,<1" +tabulate = ">=0.8.6,<1" +typer = ">=0.3.2,<1.0" +uritemplate = ">=3.0.1,<4" +urllib3 = "<2" + +[package.extras] +all = ["docker"] + +[[package]] +name = "kfp-pipeline-spec" +version = "0.1.16" +description = "Kubeflow Pipelines pipeline spec" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "kfp_pipeline_spec-0.1.16-py3-none-any.whl", hash = "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25"}, +] + +[package.dependencies] +protobuf = ">=3.13.0,<4" + +[[package]] +name = "kfp-server-api" +version = "1.8.5" +description = "Kubeflow Pipelines API" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "kfp-server-api-1.8.5.tar.gz", hash = "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de"}, +] + +[package.dependencies] +certifi = "*" +python-dateutil = "*" +six = ">=1.10" +urllib3 = ">=1.15" + +[[package]] +name = "kubernetes" +version = "25.3.0" +description = "Kubernetes python client" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "kubernetes-25.3.0-py2.py3-none-any.whl", hash = "sha256:eb42333dad0bb5caf4e66460c6a4a1a36f0f057a040f35018f6c05a699baed86"}, + {file = "kubernetes-25.3.0.tar.gz", hash = "sha256:213befbb4e5aed95f94950c7eed0c2322fc5a2f8f40932e58d28fdd42d90836c"}, +] + +[package.dependencies] +certifi = ">=14.05.14" +google-auth = ">=1.0.1" +python-dateutil = ">=2.5.3" +pyyaml = ">=5.4.1" +requests = "*" +requests-oauthlib = "*" +setuptools = ">=21.0.0" +six = ">=1.9.0" +urllib3 = ">=1.24.2" +websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" + +[package.extras] +adal = ["adal (>=1.0.2)"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "platformdirs" +version = "3.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.21.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "proto-plus" +version = "1.22.2" +description = "Beautiful, Pythonic protocol buffers." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.22.2.tar.gz", hash = "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165"}, + {file = "proto_plus-1.22.2-py3-none-any.whl", hash = "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "3.20.3" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, + {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, + {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, + {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, + {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, + {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, + {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, + {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, + {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, + {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, + {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, + {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, + {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, + {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, + {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, + {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.0-py2.py3-none-any.whl", hash = "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57"}, + {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pydantic" +version = "1.10.7" +description = "Data validation and settings management using python type hints" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, + {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, + {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, + {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, + {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, + {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, + {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, + {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, + {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, + {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrsistent" +version = "0.19.3" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, + {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, + {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, + {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, + {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, + {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, + {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, + {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, + {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, + {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, + {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, + {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, + {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, + {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, +] + +[[package]] +name = "pytest" +version = "7.3.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "5.4.1" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, +] + +[[package]] +name = "requests" +version = "2.30.0" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.30.0-py3-none-any.whl", hash = "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294"}, + {file = "requests-2.30.0.tar.gz", hash = "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "requests-toolbelt" +version = "0.10.1" +description = "A utility belt for advanced users of python-requests" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"}, + {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +category = "main" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "setuptools" +version = "67.7.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, + {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "shapely" +version = "1.8.5.post1" +description = "Geometric objects, predicates, and operations" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d048f93e42ba578b82758c15d8ae037d08e69d91d9872bca5a1895b118f4e2b0"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99ab0ddc05e44acabdbe657c599fdb9b2d82e86c5493bdae216c0c4018a82dee"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a2f0da0109e81e0c101a2b4cd8412f73f5f299e7b5b2deaf64cd2a100ac118"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6fe855e7d45685926b6ba00aaeb5eba5862611f7465775dacd527e081a8ced6d"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec14ceca36f67cb48b34d02d7f65a9acae15cd72b48e303531893ba4a960f3ea"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a2b2a65fa7f97115c1cd989fe9d6f39281ca2a8a014f1d4904c1a6e34d7f25"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-win32.whl", hash = "sha256:21776184516a16bf82a0c3d6d6a312b3cd15a4cabafc61ee01cf2714a82e8396"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-win_amd64.whl", hash = "sha256:a354199219c8d836f280b88f2c5102c81bb044ccea45bd361dc38a79f3873714"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:783bad5f48e2708a0e2f695a34ed382e4162c795cb2f0368b39528ac1d6db7ed"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a23ef3882d6aa203dd3623a3d55d698f59bfbd9f8a3bfed52c2da05a7f0f8640"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab38f7b5196ace05725e407cb8cab9ff66edb8e6f7bb36a398e8f73f52a7aaa2"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d086591f744be483b34628b391d741e46f2645fe37594319e0a673cc2c26bcf"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4728666fff8cccc65a07448cae72c75a8773fea061c3f4f139c44adc429b18c3"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-win32.whl", hash = "sha256:84010db15eb364a52b74ea8804ef92a6a930dfc1981d17a369444b6ddec66efd"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-win_amd64.whl", hash = "sha256:48dcfffb9e225c0481120f4bdf622131c8c95f342b00b158cdbe220edbbe20b6"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2fd15397638df291c427a53d641d3e6fd60458128029c8c4f487190473a69a91"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a74631e511153366c6dbe3229fa93f877e3c87ea8369cd00f1d38c76b0ed9ace"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:66bdac74fbd1d3458fa787191a90fa0ae610f09e2a5ec398c36f968cc0ed743f"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-win32.whl", hash = "sha256:6d388c0c1bd878ed1af4583695690aa52234b02ed35f93a1c8486ff52a555838"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:be9423d5a3577ac2e92c7e758bd8a2b205f5e51a012177a590bc46fc51eb4834"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5d7f85c2d35d39ff53c9216bc76b7641c52326f7e09aaad1789a3611a0f812f2"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:adcf8a11b98af9375e32bff91de184f33a68dc48b9cb9becad4f132fa25cfa3c"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:753ed0e21ab108bd4282405b9b659f2e985e8502b1a72b978eaa51d3496dee19"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-win32.whl", hash = "sha256:65b21243d8f6bcd421210daf1fabb9de84de2c04353c5b026173b88d17c1a581"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:370b574c78dc5af3a198a6da5d9b3d7c04654bd2ef7e80e80a3a0992dfb2d9cd"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:532a55ee2a6c52d23d6f7d1567c8f0473635f3b270262c44e1b0c88096827e22"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3480657460e939f45a7d359ef0e172a081f249312557fe9aa78c4fd3a362d993"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b65f5d530ba91e49ffc7c589255e878d2506a8b96ffce69d3b7c4500a9a9eaf8"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:147066da0be41b147a61f8eb805dea3b13709dbc873a431ccd7306e24d712bc0"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2822111ddc5bcfb116e6c663e403579d0fe3f147d2a97426011a191c43a7458"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b47bb6f9369e8bf3e6dbd33e6a25a47ee02b2874792a529fe04a49bf8bc0df6"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-win32.whl", hash = "sha256:2e0a8c2e55f1be1312b51c92b06462ea89e6bb703fab4b114e7a846d941cfc40"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-win_amd64.whl", hash = "sha256:0d885cb0cf670c1c834df3f371de8726efdf711f18e2a75da5cfa82843a7ab65"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0b4ee3132ee90f07d63db3aea316c4c065ed7a26231458dda0874414a09d6ba3"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:02dd5d7dc6e46515d88874134dc8fcdc65826bca93c3eecee59d1910c42c1b17"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6a9a4a31cd6e86d0fbe8473ceed83d4fe760b19d949fb557ef668defafea0f6"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:38f0fbbcb8ca20c16451c966c1f527cc43968e121c8a048af19ed3e339a921cd"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78fb9d929b8ee15cfd424b6c10879ce1907f24e05fb83310fc47d2cd27088e40"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89164e7a9776a19e29f01369a98529321994e2e4d852b92b7e01d4d9804c55bf"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-win32.whl", hash = "sha256:8e59817b0fe63d34baedaabba8c393c0090f061917d18fc0bcc2f621937a8f73"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-win_amd64.whl", hash = "sha256:e9c30b311de2513555ab02464ebb76115d242842b29c412f5a9aa0cac57be9f6"}, + {file = "Shapely-1.8.5.post1.tar.gz", hash = "sha256:ef3be705c3eac282a28058e6c6e5503419b250f482320df2172abcbea642c831"}, +] + +[package.extras] +all = ["numpy", "pytest", "pytest-cov"] +test = ["pytest", "pytest-cov"] +vectorized = ["numpy"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "strip-hints" +version = "0.1.10" +description = "Function and command-line program to strip Python type hints." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "strip-hints-0.1.10.tar.gz", hash = "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f"}, +] + +[package.dependencies] +wheel = "*" + +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "termcolor" +version = "2.3.0" +description = "ANSI color formatting for output in terminal" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, + {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typer" +version = "0.9.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.5.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, +] + +[[package]] +name = "uritemplate" +version = "3.0.1" +description = "URI templates" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, + {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, +] + +[[package]] +name = "urllib3" +version = "1.26.15" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, + {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.23.0" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, + {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, +] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.11,<4" +platformdirs = ">=3.2,<4" + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] + +[[package]] +name = "websocket-client" +version = "1.5.1" +description = "WebSocket client for Python with low level API options" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websocket-client-1.5.1.tar.gz", hash = "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40"}, + {file = "websocket_client-1.5.1-py3-none-any.whl", hash = "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"}, +] + +[package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "wheel" +version = "0.40.0" +description = "A built-package format for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, + {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, +] + +[package.extras] +test = ["pytest (>=6.0.0)"] + +[[package]] +name = "wrapt" +version = "1.15.0" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, + {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, + {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, + {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, + {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, + {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, + {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, + {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, + {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, + {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, + {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, + {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, + {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, + {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, + {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, + {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, + {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, + {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, + {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "184c13a1c78e0965c5b30675e7eb3b28ac7a39f537b1e551402348108b922376" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..3656a839 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,26 @@ +[tool.poetry] +name = "turbo-template" +version = "0.1.0" +description = "Turbo Template developed at Datatonic" +authors = ["roberta nwokonko "] + +[tool.poetry.dependencies] +python = "^3.9" +Jinja2 = ">=3.0.1,<4.0.0" +google-cloud-aiplatform = "1.24.1" +google-cloud-pipeline-components = "1.0.42" +kfp = "1.8.21" + +[tool.poetry.group.dev.dependencies] +pytest = ">=7.3.1,<8.0.0" +pre-commit = ">=2.14.1,<3.0.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +pythonpath = [ + ".", +] + From 432f7dbe687e1287917980cb33fe43ee49499056 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 16 May 2023 11:45:40 +0100 Subject: [PATCH 011/238] refactor(pyproject.toml): remove pyproject.toml from root of repo --- poetry.lock | 1845 ------------------------------------------------ pyproject.toml | 26 - 2 files changed, 1871 deletions(-) delete mode 100644 poetry.lock delete mode 100644 pyproject.toml diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index c807fdc1..00000000 --- a/poetry.lock +++ /dev/null @@ -1,1845 +0,0 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. - -[[package]] -name = "absl-py" -version = "1.4.0" -description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "absl-py-1.4.0.tar.gz", hash = "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"}, - {file = "absl_py-1.4.0-py3-none-any.whl", hash = "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47"}, -] - -[[package]] -name = "attrs" -version = "23.1.0" -description = "Classes Without Boilerplate" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] - -[[package]] -name = "cachetools" -version = "5.3.0" -description = "Extensible memoizing collections and decorators" -category = "main" -optional = false -python-versions = "~=3.7" -files = [ - {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, - {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, -] - -[[package]] -name = "certifi" -version = "2023.5.7" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, - {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, -] - -[[package]] -name = "cfgv" -version = "3.3.1" -description = "Validate configuration and produce human readable error messages." -category = "dev" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.1.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, - {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, -] - -[[package]] -name = "click" -version = "8.1.3" -description = "Composable command line interface toolkit" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "cloudpickle" -version = "2.2.1" -description = "Extended pickling support for Python objects" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "cloudpickle-2.2.1-py3-none-any.whl", hash = "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f"}, - {file = "cloudpickle-2.2.1.tar.gz", hash = "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "deprecated" -version = "1.2.13" -description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, - {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, -] - -[package.dependencies] -wrapt = ">=1.10,<2" - -[package.extras] -dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] - -[[package]] -name = "distlib" -version = "0.3.6" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] - -[[package]] -name = "docstring-parser" -version = "0.15" -description = "Parse Python docstrings in reST, Google and Numpydoc format" -category = "main" -optional = false -python-versions = ">=3.6,<4.0" -files = [ - {file = "docstring_parser-0.15-py3-none-any.whl", hash = "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9"}, - {file = "docstring_parser-0.15.tar.gz", hash = "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.1.1" -description = "Backport of PEP 654 (exception groups)" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, - {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "filelock" -version = "3.12.0" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, - {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, -] - -[package.extras] -docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "fire" -version = "0.5.0" -description = "A library for automatically generating command line interfaces." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "fire-0.5.0.tar.gz", hash = "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6"}, -] - -[package.dependencies] -six = "*" -termcolor = "*" - -[[package]] -name = "google-api-core" -version = "2.10.2" -description = "Google API client core library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-api-core-2.10.2.tar.gz", hash = "sha256:10c06f7739fe57781f87523375e8e1a3a4674bf6392cd6131a3222182b971320"}, - {file = "google_api_core-2.10.2-py3-none-any.whl", hash = "sha256:34f24bd1d5f72a8c4519773d99ca6bf080a6c4e041b4e9f024fe230191dda62e"}, -] - -[package.dependencies] -google-auth = ">=1.25.0,<3.0dev" -googleapis-common-protos = ">=1.56.2,<2.0dev" -grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} -grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" -requests = ">=2.18.0,<3.0.0dev" - -[package.extras] -grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)"] -grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] -grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] - -[[package]] -name = "google-api-core" -version = "2.11.0" -description = "Google API client core library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-api-core-2.11.0.tar.gz", hash = "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22"}, - {file = "google_api_core-2.11.0-py3-none-any.whl", hash = "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e"}, -] - -[package.dependencies] -google-auth = ">=2.14.1,<3.0dev" -googleapis-common-protos = ">=1.56.2,<2.0dev" -grpcio = [ - {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, - {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, -] -grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" -requests = ">=2.18.0,<3.0.0dev" - -[package.extras] -grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)", "grpcio-status (>=1.49.1,<2.0dev)"] -grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] -grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] - -[[package]] -name = "google-api-python-client" -version = "1.12.11" -description = "Google API Client Library for Python" -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -files = [ - {file = "google-api-python-client-1.12.11.tar.gz", hash = "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9"}, - {file = "google_api_python_client-1.12.11-py2.py3-none-any.whl", hash = "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.21.0,<3dev", markers = "python_version >= \"3\""} -google-auth = {version = ">=1.16.0,<3dev", markers = "python_version >= \"3\""} -google-auth-httplib2 = ">=0.0.3" -httplib2 = ">=0.15.0,<1dev" -six = ">=1.13.0,<2dev" -uritemplate = ">=3.0.0,<4dev" - -[[package]] -name = "google-auth" -version = "2.18.0" -description = "Google Authentication Library" -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" -files = [ - {file = "google-auth-2.18.0.tar.gz", hash = "sha256:c66b488a8b005b23ccb97b1198b6cece516c91869091ac5b7c267422db2733c7"}, - {file = "google_auth-2.18.0-py2.py3-none-any.whl", hash = "sha256:ef3f3a67fa54d421a1c155864570f9a8de9179cedc937bda496b7a8ca338e936"}, -] - -[package.dependencies] -cachetools = ">=2.0.0,<6.0" -pyasn1-modules = ">=0.2.1" -rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} -six = ">=1.9.0" -urllib3 = "<2.0" - -[package.extras] -aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"] -enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] -pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] -reauth = ["pyu2f (>=0.1.5)"] -requests = ["requests (>=2.20.0,<3.0.0dev)"] - -[[package]] -name = "google-auth-httplib2" -version = "0.1.0" -description = "Google Authentication Library: httplib2 transport" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, - {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, -] - -[package.dependencies] -google-auth = "*" -httplib2 = ">=0.15.0" -six = "*" - -[[package]] -name = "google-cloud-aiplatform" -version = "1.24.1" -description = "Vertex AI API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-aiplatform-1.24.1.tar.gz", hash = "sha256:0ce9e97bf5c977397e52b3b7c4dc78610c135fbde11a60a6c0b77a4fdf776400"}, - {file = "google_cloud_aiplatform-1.24.1-py2.py3-none-any.whl", hash = "sha256:942765a6bad97e46e262dd6599dc5f171663ce952130e0b0b2eb97e0b1b98bfd"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} -google-cloud-bigquery = ">=1.15.0,<4.0.0dev" -google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" -google-cloud-storage = ">=1.32.0,<3.0.0dev" -packaging = ">=14.3" -proto-plus = ">=1.22.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" -shapely = "<2.0.0" - -[package.extras] -autologging = ["mlflow (>=1.27.0,<=2.1.1)"] -cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] -datasets = ["pyarrow (>=3.0.0,<8.0dev)"] -endpoint = ["requests (>=2.28.1)"] -full = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] -lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"] -metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"] -pipelines = ["pyyaml (>=5.3,<6)"] -prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<0.76.0)", "starlette (>=0.17.1)", "uvicorn[standard] (>=0.16.0)"] -private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] -tensorboard = ["tensorflow (>=2.3.0,<3.0.0dev)"] -testing = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "grpcio-testing", "ipython", "kfp", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "scikit-learn", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] -vizier = ["google-vizier (==0.0.4)"] -xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] - -[[package]] -name = "google-cloud-bigquery" -version = "3.10.0" -description = "Google BigQuery API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-bigquery-3.10.0.tar.gz", hash = "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df"}, - {file = "google_cloud_bigquery-3.10.0-py2.py3-none-any.whl", hash = "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} -google-cloud-core = ">=1.6.0,<3.0.0dev" -google-resumable-media = ">=0.6.0,<3.0dev" -grpcio = [ - {version = ">=1.47.0,<2.0dev", markers = "python_version < \"3.11\""}, - {version = ">=1.49.1,<2.0dev", markers = "python_version >= \"3.11\""}, -] -packaging = ">=20.0.0" -proto-plus = ">=1.15.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" -python-dateutil = ">=2.7.2,<3.0dev" -requests = ">=2.21.0,<3.0.0dev" - -[package.extras] -all = ["Shapely (>=1.8.4,<2.0dev)", "db-dtypes (>=0.3.0,<2.0.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)", "ipywidgets (>=7.7.0)", "opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)", "tqdm (>=4.7.4,<5.0.0dev)"] -bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "pyarrow (>=3.0.0)"] -geopandas = ["Shapely (>=1.8.4,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)"] -ipython = ["ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)"] -ipywidgets = ["ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)"] -opentelemetry = ["opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)"] -pandas = ["db-dtypes (>=0.3.0,<2.0.0dev)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)"] -tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] - -[[package]] -name = "google-cloud-core" -version = "2.3.2" -description = "Google Cloud API client core library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-core-2.3.2.tar.gz", hash = "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a"}, - {file = "google_cloud_core-2.3.2-py2.py3-none-any.whl", hash = "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe"}, -] - -[package.dependencies] -google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" -google-auth = ">=1.25.0,<3.0dev" - -[package.extras] -grpc = ["grpcio (>=1.38.0,<2.0dev)"] - -[[package]] -name = "google-cloud-notebooks" -version = "1.4.4" -description = "" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-notebooks-1.4.4.tar.gz", hash = "sha256:76c1c21f2328c0777c4b0333d1a9adbeeea94cfc9af25fe589bef00a9dae41cd"}, - {file = "google_cloud_notebooks-1.4.4-py2.py3-none-any.whl", hash = "sha256:b0204d26663df0e460cd4158d6d0747e9e16d70c607e4da99572a77b51cf2e8d"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} -proto-plus = ">=1.22.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - -[[package]] -name = "google-cloud-notebooks" -version = "1.7.0" -description = "Google Cloud Notebooks API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-notebooks-1.7.0.tar.gz", hash = "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e"}, - {file = "google_cloud_notebooks-1.7.0-py2.py3-none-any.whl", hash = "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = [ - {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, - {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, -] -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - -[[package]] -name = "google-cloud-pipeline-components" -version = "1.0.42" -description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "google_cloud_pipeline_components-1.0.42-py3-none-any.whl", hash = "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623"}, -] - -[package.dependencies] -google-api-core = ">=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev" -google-cloud-aiplatform = ">=1.14.0,<2" -google-cloud-notebooks = ">=0.4.0" -google-cloud-storage = ">=2.2.1,<3" -googleapis-common-protos = ">=1.56.2,<2.0dev" -grpcio-status = "<=1.47.0" -kfp = ">=1.8.9,<2.0.0" -protobuf = ">=3.19.0,<4.0.0dev" - -[package.extras] -tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev)", "google-cloud-aiplatform (>=1.14.0,<2)", "google-cloud-notebooks (>=0.4.0)", "google-cloud-storage (>=2.2.1,<3)", "googleapis-common-protos (>=1.56.2,<2.0dev)", "grpcio-status (<=1.47.0)", "kfp (>=1.8.9,<2.0.0)", "mock (>=4.0.0)", "protobuf (>=3.19.0,<4.0.0dev)", "pytest (>=6.0.0)"] - -[[package]] -name = "google-cloud-resource-manager" -version = "1.6.3" -description = "Google Cloud Resource Manager API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-resource-manager-1.6.3.tar.gz", hash = "sha256:6cf8a9a74e65a03857896967c79307b75805c752ade3bc41224e6167774bc9c9"}, - {file = "google_cloud_resource_manager-1.6.3-py2.py3-none-any.whl", hash = "sha256:e330883c53c5e3e38a651da85ae0bce201a736b5cd5f9df10941160c6a66ce6e"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = ">=1.22.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - -[[package]] -name = "google-cloud-resource-manager" -version = "1.10.0" -description = "Google Cloud Resource Manager API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-resource-manager-1.10.0.tar.gz", hash = "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287"}, - {file = "google_cloud_resource_manager-1.10.0-py2.py3-none-any.whl", hash = "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = [ - {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, - {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, -] -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - -[[package]] -name = "google-cloud-storage" -version = "2.9.0" -description = "Google Cloud Storage API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-storage-2.9.0.tar.gz", hash = "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3"}, - {file = "google_cloud_storage-2.9.0-py2.py3-none-any.whl", hash = "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94"}, -] - -[package.dependencies] -google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" -google-auth = ">=1.25.0,<3.0dev" -google-cloud-core = ">=2.3.0,<3.0dev" -google-resumable-media = ">=2.3.2" -requests = ">=2.18.0,<3.0.0dev" - -[package.extras] -protobuf = ["protobuf (<5.0.0dev)"] - -[[package]] -name = "google-crc32c" -version = "1.5.0" -description = "A python wrapper of the C library 'Google CRC32C'" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, - {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, - {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346"}, - {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65"}, - {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b"}, - {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02"}, - {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4"}, - {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e"}, - {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c"}, - {file = "google_crc32c-1.5.0-cp310-cp310-win32.whl", hash = "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee"}, - {file = "google_crc32c-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289"}, - {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273"}, - {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298"}, - {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57"}, - {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438"}, - {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906"}, - {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183"}, - {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd"}, - {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c"}, - {file = "google_crc32c-1.5.0-cp311-cp311-win32.whl", hash = "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709"}, - {file = "google_crc32c-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-win32.whl", hash = "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740"}, - {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8"}, - {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37"}, - {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894"}, - {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-win32.whl", hash = "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4"}, - {file = "google_crc32c-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c"}, - {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7"}, - {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210"}, - {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd"}, - {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96"}, - {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61"}, - {file = "google_crc32c-1.5.0-cp39-cp39-win32.whl", hash = "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c"}, - {file = "google_crc32c-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93"}, -] - -[package.extras] -testing = ["pytest"] - -[[package]] -name = "google-resumable-media" -version = "2.5.0" -description = "Utilities for Google Media Downloads and Resumable Uploads" -category = "main" -optional = false -python-versions = ">= 3.7" -files = [ - {file = "google-resumable-media-2.5.0.tar.gz", hash = "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93"}, - {file = "google_resumable_media-2.5.0-py2.py3-none-any.whl", hash = "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec"}, -] - -[package.dependencies] -google-crc32c = ">=1.0,<2.0dev" - -[package.extras] -aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] -requests = ["requests (>=2.18.0,<3.0.0dev)"] - -[[package]] -name = "googleapis-common-protos" -version = "1.59.0" -description = "Common protobufs used in Google APIs" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "googleapis-common-protos-1.59.0.tar.gz", hash = "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44"}, - {file = "googleapis_common_protos-1.59.0-py2.py3-none-any.whl", hash = "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f"}, -] - -[package.dependencies] -grpcio = {version = ">=1.44.0,<2.0.0dev", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - -[package.extras] -grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] - -[[package]] -name = "grpc-google-iam-v1" -version = "0.12.6" -description = "IAM API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "grpc-google-iam-v1-0.12.6.tar.gz", hash = "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f"}, - {file = "grpc_google_iam_v1-0.12.6-py2.py3-none-any.whl", hash = "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c"}, -] - -[package.dependencies] -googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} -grpcio = ">=1.44.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - -[[package]] -name = "grpcio" -version = "1.54.2" -description = "HTTP/2-based RPC framework" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "grpcio-1.54.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c"}, - {file = "grpcio-1.54.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea"}, - {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c0e3155fc5335ec7b3b70f15230234e529ca3607b20a562b6c75fb1b1218874c"}, - {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bf88004fe086c786dc56ef8dd6cb49c026833fdd6f42cb853008bce3f907148"}, - {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be88c081e33f20630ac3343d8ad9f1125f32987968e9c8c75c051c9800896e8"}, - {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:33d40954199bddbb6a78f8f6f2b2082660f381cd2583ec860a6c2fa7c8400c08"}, - {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b52d00d1793d290c81ad6a27058f5224a7d5f527867e5b580742e1bd211afeee"}, - {file = "grpcio-1.54.2-cp310-cp310-win32.whl", hash = "sha256:881d058c5ccbea7cc2c92085a11947b572498a27ef37d3eef4887f499054dca8"}, - {file = "grpcio-1.54.2-cp310-cp310-win_amd64.whl", hash = "sha256:0212e2f7fdf7592e4b9d365087da30cb4d71e16a6f213120c89b4f8fb35a3ab3"}, - {file = "grpcio-1.54.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:1e623e0cf99a0ac114f091b3083a1848dbc64b0b99e181473b5a4a68d4f6f821"}, - {file = "grpcio-1.54.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:66233ccd2a9371158d96e05d082043d47dadb18cbb294dc5accfdafc2e6b02a7"}, - {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:4cb283f630624ebb16c834e5ac3d7880831b07cbe76cb08ab7a271eeaeb8943e"}, - {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a1e601ee31ef30a9e2c601d0867e236ac54c922d32ed9f727b70dd5d82600d5"}, - {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8da84bbc61a4e92af54dc96344f328e5822d574f767e9b08e1602bb5ddc254a"}, - {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5008964885e8d23313c8e5ea0d44433be9bfd7e24482574e8cc43c02c02fc796"}, - {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2f5a1f1080ccdc7cbaf1171b2cf384d852496fe81ddedeb882d42b85727f610"}, - {file = "grpcio-1.54.2-cp311-cp311-win32.whl", hash = "sha256:b74ae837368cfffeb3f6b498688a123e6b960951be4dec0e869de77e7fa0439e"}, - {file = "grpcio-1.54.2-cp311-cp311-win_amd64.whl", hash = "sha256:8cdbcbd687e576d48f7886157c95052825ca9948c0ed2afdc0134305067be88b"}, - {file = "grpcio-1.54.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:782f4f8662a2157c4190d0f99eaaebc602899e84fb1e562a944e5025929e351c"}, - {file = "grpcio-1.54.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:714242ad0afa63a2e6dabd522ae22e1d76e07060b5af2ddda5474ba4f14c2c94"}, - {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:f900ed4ad7a0f1f05d35f955e0943944d5a75f607a836958c6b8ab2a81730ef2"}, - {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96a41817d2c763b1d0b32675abeb9179aa2371c72aefdf74b2d2b99a1b92417b"}, - {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70fcac7b94f4c904152809a050164650ac81c08e62c27aa9f156ac518029ebbe"}, - {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fd6c6c29717724acf9fc1847c4515d57e4dc12762452457b9cb37461f30a81bb"}, - {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c2392f5b5d84b71d853918687d806c1aa4308109e5ca158a16e16a6be71041eb"}, - {file = "grpcio-1.54.2-cp37-cp37m-win_amd64.whl", hash = "sha256:51630c92591d6d3fe488a7c706bd30a61594d144bac7dee20c8e1ce78294f474"}, - {file = "grpcio-1.54.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:b04202453941a63b36876a7172b45366dc0cde10d5fd7855c0f4a4e673c0357a"}, - {file = "grpcio-1.54.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:89dde0ac72a858a44a2feb8e43dc68c0c66f7857a23f806e81e1b7cc7044c9cf"}, - {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:09d4bfd84686cd36fd11fd45a0732c7628308d094b14d28ea74a81db0bce2ed3"}, - {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fc2b4edb938c8faa4b3c3ea90ca0dd89b7565a049e8e4e11b77e60e4ed2cc05"}, - {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61f7203e2767800edee7a1e1040aaaf124a35ce0c7fe0883965c6b762defe598"}, - {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e416c8baf925b5a1aff31f7f5aecc0060b25d50cce3a5a7255dc5cf2f1d4e5eb"}, - {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dc80c9c6b608bf98066a038e0172013a49cfa9a08d53335aefefda2c64fc68f4"}, - {file = "grpcio-1.54.2-cp38-cp38-win32.whl", hash = "sha256:8d6192c37a30a115f4663592861f50e130caed33efc4eec24d92ec881c92d771"}, - {file = "grpcio-1.54.2-cp38-cp38-win_amd64.whl", hash = "sha256:46a057329938b08e5f0e12ea3d7aed3ecb20a0c34c4a324ef34e00cecdb88a12"}, - {file = "grpcio-1.54.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:2296356b5c9605b73ed6a52660b538787094dae13786ba53080595d52df13a98"}, - {file = "grpcio-1.54.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:c72956972e4b508dd39fdc7646637a791a9665b478e768ffa5f4fe42123d5de1"}, - {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:9bdbb7624d65dc0ed2ed8e954e79ab1724526f09b1efa88dcd9a1815bf28be5f"}, - {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c44e1a765b31e175c391f22e8fc73b2a2ece0e5e6ff042743d8109b5d2eff9f"}, - {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cc928cfe6c360c1df636cf7991ab96f059666ac7b40b75a769410cc6217df9c"}, - {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a08920fa1a97d4b8ee5db2f31195de4a9def1a91bc003544eb3c9e6b8977960a"}, - {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4864f99aac207e3e45c5e26c6cbb0ad82917869abc2f156283be86c05286485c"}, - {file = "grpcio-1.54.2-cp39-cp39-win32.whl", hash = "sha256:b38b3de8cff5bc70f8f9c615f51b48eff7313fc9aca354f09f81b73036e7ddfa"}, - {file = "grpcio-1.54.2-cp39-cp39-win_amd64.whl", hash = "sha256:be48496b0e00460717225e7680de57c38be1d8629dc09dadcd1b3389d70d942b"}, - {file = "grpcio-1.54.2.tar.gz", hash = "sha256:50a9f075eeda5097aa9a182bb3877fe1272875e45370368ac0ee16ab9e22d019"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.54.2)"] - -[[package]] -name = "grpcio-status" -version = "1.47.0" -description = "Status proto mapping for gRPC" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "grpcio-status-1.47.0.tar.gz", hash = "sha256:c9ce3213e84c6fd8801c31aca3ea4a6b3453eaa40b93a6c0a23ea8999808fa00"}, - {file = "grpcio_status-1.47.0-py3-none-any.whl", hash = "sha256:2154fdb8aad20452488712be6879657b508115ca06139fde8897ea8e9bc79367"}, -] - -[package.dependencies] -googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.47.0" -protobuf = ">=3.12.0" - -[[package]] -name = "httplib2" -version = "0.22.0" -description = "A comprehensive HTTP client library." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, - {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, -] - -[package.dependencies] -pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} - -[[package]] -name = "identify" -version = "2.5.24" -description = "File identification library for Python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, - {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jsonschema" -version = "4.17.3" -description = "An implementation of JSON Schema validation for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, - {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, -] - -[package.dependencies] -attrs = ">=17.4.0" -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "kfp" -version = "1.8.21" -description = "KubeFlow Pipelines SDK" -category = "main" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "kfp-1.8.21.tar.gz", hash = "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c"}, -] - -[package.dependencies] -absl-py = ">=0.9,<2" -click = ">=7.1.2,<9" -cloudpickle = ">=2.0.0,<3" -Deprecated = ">=1.2.7,<2" -docstring-parser = ">=0.7.3,<1" -fire = ">=0.3.1,<1" -google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" -google-api-python-client = ">=1.7.8,<2" -google-auth = ">=1.6.1,<3" -google-cloud-storage = ">=1.20.0,<3" -jsonschema = ">=3.0.1,<5" -kfp-pipeline-spec = ">=0.1.16,<0.2.0" -kfp-server-api = ">=1.1.2,<2.0.0" -kubernetes = ">=8.0.0,<26" -protobuf = ">=3.13.0,<4" -pydantic = ">=1.8.2,<2" -PyYAML = ">=5.3,<6" -requests-toolbelt = ">=0.8.0,<1" -strip-hints = ">=0.1.8,<1" -tabulate = ">=0.8.6,<1" -typer = ">=0.3.2,<1.0" -uritemplate = ">=3.0.1,<4" -urllib3 = "<2" - -[package.extras] -all = ["docker"] - -[[package]] -name = "kfp-pipeline-spec" -version = "0.1.16" -description = "Kubeflow Pipelines pipeline spec" -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "kfp_pipeline_spec-0.1.16-py3-none-any.whl", hash = "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25"}, -] - -[package.dependencies] -protobuf = ">=3.13.0,<4" - -[[package]] -name = "kfp-server-api" -version = "1.8.5" -description = "Kubeflow Pipelines API" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "kfp-server-api-1.8.5.tar.gz", hash = "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de"}, -] - -[package.dependencies] -certifi = "*" -python-dateutil = "*" -six = ">=1.10" -urllib3 = ">=1.15" - -[[package]] -name = "kubernetes" -version = "25.3.0" -description = "Kubernetes python client" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "kubernetes-25.3.0-py2.py3-none-any.whl", hash = "sha256:eb42333dad0bb5caf4e66460c6a4a1a36f0f057a040f35018f6c05a699baed86"}, - {file = "kubernetes-25.3.0.tar.gz", hash = "sha256:213befbb4e5aed95f94950c7eed0c2322fc5a2f8f40932e58d28fdd42d90836c"}, -] - -[package.dependencies] -certifi = ">=14.05.14" -google-auth = ">=1.0.1" -python-dateutil = ">=2.5.3" -pyyaml = ">=5.4.1" -requests = "*" -requests-oauthlib = "*" -setuptools = ">=21.0.0" -six = ">=1.9.0" -urllib3 = ">=1.24.2" -websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" - -[package.extras] -adal = ["adal (>=1.0.2)"] - -[[package]] -name = "markupsafe" -version = "2.1.2" -description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, - {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, -] - -[[package]] -name = "nodeenv" -version = "1.8.0" -description = "Node.js virtual environment builder" -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" -files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, -] - -[package.dependencies] -setuptools = "*" - -[[package]] -name = "oauthlib" -version = "3.2.2" -description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, - {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, -] - -[package.extras] -rsa = ["cryptography (>=3.0.0)"] -signals = ["blinker (>=1.4.0)"] -signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] - -[[package]] -name = "packaging" -version = "23.1" -description = "Core utilities for Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, -] - -[[package]] -name = "platformdirs" -version = "3.5.1" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, - {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, -] - -[package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pre-commit" -version = "2.21.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, - {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "proto-plus" -version = "1.22.2" -description = "Beautiful, Pythonic protocol buffers." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "proto-plus-1.22.2.tar.gz", hash = "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165"}, - {file = "proto_plus-1.22.2-py3-none-any.whl", hash = "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d"}, -] - -[package.dependencies] -protobuf = ">=3.19.0,<5.0.0dev" - -[package.extras] -testing = ["google-api-core[grpc] (>=1.31.5)"] - -[[package]] -name = "protobuf" -version = "3.20.3" -description = "Protocol Buffers" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, - {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, - {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, - {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, - {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, - {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, - {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, - {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, - {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, - {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, - {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, - {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, - {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, - {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, - {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, - {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, - {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, - {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, - {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, - {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, - {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, - {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, -] - -[[package]] -name = "pyasn1" -version = "0.5.0" -description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "pyasn1-0.5.0-py2.py3-none-any.whl", hash = "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57"}, - {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, -] - -[[package]] -name = "pyasn1-modules" -version = "0.3.0" -description = "A collection of ASN.1-based protocols modules" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, - {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, -] - -[package.dependencies] -pyasn1 = ">=0.4.6,<0.6.0" - -[[package]] -name = "pydantic" -version = "1.10.7" -description = "Data validation and settings management using python type hints" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, - {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, - {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, - {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, - {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, - {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, - {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, - {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, - {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, - {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, - {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, - {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, - {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, - {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, - {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, - {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, - {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, - {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, - {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, - {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, - {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, - {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, - {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, - {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, - {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, - {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, - {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, - {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, - {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, - {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, - {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, - {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, - {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, - {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, - {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, - {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, -] - -[package.dependencies] -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" -optional = false -python-versions = ">=3.6.8" -files = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pyrsistent" -version = "0.19.3" -description = "Persistent/Functional/Immutable data structures" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, - {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, - {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, - {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, - {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, - {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, -] - -[[package]] -name = "pytest" -version = "7.3.1" -description = "pytest: simple powerful testing with Python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, - {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] - -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pyyaml" -version = "5.4.1" -description = "YAML parser and emitter for Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, - {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, - {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, - {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, - {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, - {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, - {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, - {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, - {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, -] - -[[package]] -name = "requests" -version = "2.30.0" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.30.0-py3-none-any.whl", hash = "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294"}, - {file = "requests-2.30.0.tar.gz", hash = "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "requests-oauthlib" -version = "1.3.1" -description = "OAuthlib authentication support for Requests." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, - {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, -] - -[package.dependencies] -oauthlib = ">=3.0.0" -requests = ">=2.0.0" - -[package.extras] -rsa = ["oauthlib[signedtoken] (>=3.0.0)"] - -[[package]] -name = "requests-toolbelt" -version = "0.10.1" -description = "A utility belt for advanced users of python-requests" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"}, - {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"}, -] - -[package.dependencies] -requests = ">=2.0.1,<3.0.0" - -[[package]] -name = "rsa" -version = "4.9" -description = "Pure-Python RSA implementation" -category = "main" -optional = false -python-versions = ">=3.6,<4" -files = [ - {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, - {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, -] - -[package.dependencies] -pyasn1 = ">=0.1.3" - -[[package]] -name = "setuptools" -version = "67.7.2" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "shapely" -version = "1.8.5.post1" -description = "Geometric objects, predicates, and operations" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d048f93e42ba578b82758c15d8ae037d08e69d91d9872bca5a1895b118f4e2b0"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99ab0ddc05e44acabdbe657c599fdb9b2d82e86c5493bdae216c0c4018a82dee"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a2f0da0109e81e0c101a2b4cd8412f73f5f299e7b5b2deaf64cd2a100ac118"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6fe855e7d45685926b6ba00aaeb5eba5862611f7465775dacd527e081a8ced6d"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec14ceca36f67cb48b34d02d7f65a9acae15cd72b48e303531893ba4a960f3ea"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a2b2a65fa7f97115c1cd989fe9d6f39281ca2a8a014f1d4904c1a6e34d7f25"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-win32.whl", hash = "sha256:21776184516a16bf82a0c3d6d6a312b3cd15a4cabafc61ee01cf2714a82e8396"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-win_amd64.whl", hash = "sha256:a354199219c8d836f280b88f2c5102c81bb044ccea45bd361dc38a79f3873714"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:783bad5f48e2708a0e2f695a34ed382e4162c795cb2f0368b39528ac1d6db7ed"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a23ef3882d6aa203dd3623a3d55d698f59bfbd9f8a3bfed52c2da05a7f0f8640"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab38f7b5196ace05725e407cb8cab9ff66edb8e6f7bb36a398e8f73f52a7aaa2"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d086591f744be483b34628b391d741e46f2645fe37594319e0a673cc2c26bcf"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4728666fff8cccc65a07448cae72c75a8773fea061c3f4f139c44adc429b18c3"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-win32.whl", hash = "sha256:84010db15eb364a52b74ea8804ef92a6a930dfc1981d17a369444b6ddec66efd"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-win_amd64.whl", hash = "sha256:48dcfffb9e225c0481120f4bdf622131c8c95f342b00b158cdbe220edbbe20b6"}, - {file = "Shapely-1.8.5.post1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2fd15397638df291c427a53d641d3e6fd60458128029c8c4f487190473a69a91"}, - {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a74631e511153366c6dbe3229fa93f877e3c87ea8369cd00f1d38c76b0ed9ace"}, - {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:66bdac74fbd1d3458fa787191a90fa0ae610f09e2a5ec398c36f968cc0ed743f"}, - {file = "Shapely-1.8.5.post1-cp36-cp36m-win32.whl", hash = "sha256:6d388c0c1bd878ed1af4583695690aa52234b02ed35f93a1c8486ff52a555838"}, - {file = "Shapely-1.8.5.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:be9423d5a3577ac2e92c7e758bd8a2b205f5e51a012177a590bc46fc51eb4834"}, - {file = "Shapely-1.8.5.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5d7f85c2d35d39ff53c9216bc76b7641c52326f7e09aaad1789a3611a0f812f2"}, - {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:adcf8a11b98af9375e32bff91de184f33a68dc48b9cb9becad4f132fa25cfa3c"}, - {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:753ed0e21ab108bd4282405b9b659f2e985e8502b1a72b978eaa51d3496dee19"}, - {file = "Shapely-1.8.5.post1-cp37-cp37m-win32.whl", hash = "sha256:65b21243d8f6bcd421210daf1fabb9de84de2c04353c5b026173b88d17c1a581"}, - {file = "Shapely-1.8.5.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:370b574c78dc5af3a198a6da5d9b3d7c04654bd2ef7e80e80a3a0992dfb2d9cd"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:532a55ee2a6c52d23d6f7d1567c8f0473635f3b270262c44e1b0c88096827e22"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3480657460e939f45a7d359ef0e172a081f249312557fe9aa78c4fd3a362d993"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b65f5d530ba91e49ffc7c589255e878d2506a8b96ffce69d3b7c4500a9a9eaf8"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:147066da0be41b147a61f8eb805dea3b13709dbc873a431ccd7306e24d712bc0"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2822111ddc5bcfb116e6c663e403579d0fe3f147d2a97426011a191c43a7458"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b47bb6f9369e8bf3e6dbd33e6a25a47ee02b2874792a529fe04a49bf8bc0df6"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-win32.whl", hash = "sha256:2e0a8c2e55f1be1312b51c92b06462ea89e6bb703fab4b114e7a846d941cfc40"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-win_amd64.whl", hash = "sha256:0d885cb0cf670c1c834df3f371de8726efdf711f18e2a75da5cfa82843a7ab65"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0b4ee3132ee90f07d63db3aea316c4c065ed7a26231458dda0874414a09d6ba3"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:02dd5d7dc6e46515d88874134dc8fcdc65826bca93c3eecee59d1910c42c1b17"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6a9a4a31cd6e86d0fbe8473ceed83d4fe760b19d949fb557ef668defafea0f6"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:38f0fbbcb8ca20c16451c966c1f527cc43968e121c8a048af19ed3e339a921cd"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78fb9d929b8ee15cfd424b6c10879ce1907f24e05fb83310fc47d2cd27088e40"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89164e7a9776a19e29f01369a98529321994e2e4d852b92b7e01d4d9804c55bf"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-win32.whl", hash = "sha256:8e59817b0fe63d34baedaabba8c393c0090f061917d18fc0bcc2f621937a8f73"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-win_amd64.whl", hash = "sha256:e9c30b311de2513555ab02464ebb76115d242842b29c412f5a9aa0cac57be9f6"}, - {file = "Shapely-1.8.5.post1.tar.gz", hash = "sha256:ef3be705c3eac282a28058e6c6e5503419b250f482320df2172abcbea642c831"}, -] - -[package.extras] -all = ["numpy", "pytest", "pytest-cov"] -test = ["pytest", "pytest-cov"] -vectorized = ["numpy"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "strip-hints" -version = "0.1.10" -description = "Function and command-line program to strip Python type hints." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "strip-hints-0.1.10.tar.gz", hash = "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f"}, -] - -[package.dependencies] -wheel = "*" - -[[package]] -name = "tabulate" -version = "0.9.0" -description = "Pretty-print tabular data" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, - {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, -] - -[package.extras] -widechars = ["wcwidth"] - -[[package]] -name = "termcolor" -version = "2.3.0" -description = "ANSI color formatting for output in terminal" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, - {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, -] - -[package.extras] -tests = ["pytest", "pytest-cov"] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "typer" -version = "0.9.0" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, - {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, -] - -[package.dependencies] -click = ">=7.1.1,<9.0.0" -typing-extensions = ">=3.7.4.3" - -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] - -[[package]] -name = "typing-extensions" -version = "4.5.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, -] - -[[package]] -name = "uritemplate" -version = "3.0.1" -description = "URI templates" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, - {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, -] - -[[package]] -name = "urllib3" -version = "1.26.15" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "virtualenv" -version = "20.23.0" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, - {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, -] - -[package.dependencies] -distlib = ">=0.3.6,<1" -filelock = ">=3.11,<4" -platformdirs = ">=3.2,<4" - -[package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] - -[[package]] -name = "websocket-client" -version = "1.5.1" -description = "WebSocket client for Python with low level API options" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "websocket-client-1.5.1.tar.gz", hash = "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40"}, - {file = "websocket_client-1.5.1-py3-none-any.whl", hash = "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"}, -] - -[package.extras] -docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] -optional = ["python-socks", "wsaccel"] -test = ["websockets"] - -[[package]] -name = "wheel" -version = "0.40.0" -description = "A built-package format for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, - {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, -] - -[package.extras] -test = ["pytest (>=6.0.0)"] - -[[package]] -name = "wrapt" -version = "1.15.0" -description = "Module for decorators, wrappers and monkey patching." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, - {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, - {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, - {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, - {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, - {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, - {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, - {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, - {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, - {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, - {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, - {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, - {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, - {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, - {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, - {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, - {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, - {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, - {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.9" -content-hash = "184c13a1c78e0965c5b30675e7eb3b28ac7a39f537b1e551402348108b922376" diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 3656a839..00000000 --- a/pyproject.toml +++ /dev/null @@ -1,26 +0,0 @@ -[tool.poetry] -name = "turbo-template" -version = "0.1.0" -description = "Turbo Template developed at Datatonic" -authors = ["roberta nwokonko "] - -[tool.poetry.dependencies] -python = "^3.9" -Jinja2 = ">=3.0.1,<4.0.0" -google-cloud-aiplatform = "1.24.1" -google-cloud-pipeline-components = "1.0.42" -kfp = "1.8.21" - -[tool.poetry.group.dev.dependencies] -pytest = ">=7.3.1,<8.0.0" -pre-commit = ">=2.14.1,<3.0.0" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" - -[tool.pytest.ini_options] -pythonpath = [ - ".", -] - From bd5b47101c9e70f4a8d5d71450be3949d75c0033 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 16 May 2023 11:47:01 +0100 Subject: [PATCH 012/238] refactor(pyproject.toml): rename to pipelines --- pipelines/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index 871e114b..7e288fd9 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "turbo-template" +name = "pipelines" version = "0.1.0" authors = ["roberta nwokonko "] description = "Turbo Template developed at Datatonic" From 751a6eab41ecb868db2ee365e3e7e8effb23450e Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 16 May 2023 11:55:14 +0100 Subject: [PATCH 013/238] refactor(pyproject.toml): update author and description --- components/bigquery-components/pyproject.toml | 6 ++++-- components/vertex-components/pyproject.toml | 6 ++++-- pipelines/pyproject.toml | 4 +++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/components/bigquery-components/pyproject.toml b/components/bigquery-components/pyproject.toml index 083448b2..3a51450a 100644 --- a/components/bigquery-components/pyproject.toml +++ b/components/bigquery-components/pyproject.toml @@ -1,8 +1,10 @@ [tool.poetry] name = "bigquery-components" version = "0.1.0" -authors = ["roberta nwokonko "] -description = "BigQuery components created by Datatonic" +authors = [ + {name = "Example User", email = "user@example.com"}, +] +description = "Bigquery components for vertex pipelines" readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", diff --git a/components/vertex-components/pyproject.toml b/components/vertex-components/pyproject.toml index 9011c442..6ba44118 100644 --- a/components/vertex-components/pyproject.toml +++ b/components/vertex-components/pyproject.toml @@ -1,8 +1,10 @@ [tool.poetry] name = "vertex-components" version = "0.1.0" -authors = ["roberta nwokonko "] -description = "Vertex AI components made by Datatonic" +authors = [ + {name = "Example User", email = "user@example.com"}, +] +description = "Vertex components for training and prediction using the Chicago Taxi Dataset" readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index 7e288fd9..b1784b94 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -1,7 +1,9 @@ [tool.poetry] name = "pipelines" version = "0.1.0" -authors = ["roberta nwokonko "] +authors = [ + {name = "Example User", email = "user@example.com"}, +] description = "Turbo Template developed at Datatonic" readme = "README.md" classifiers = [ From 43817e5403b01c1f16ec56af8988331d05a9eee5 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 16 May 2023 11:58:41 +0100 Subject: [PATCH 014/238] build(cloudbuild-files): update the CI/CD pipelines in the cloudbuild directory to use a python:3.9 image --- cloudbuild/e2e-test.yaml | 2 +- cloudbuild/pr-checks.yaml | 2 +- cloudbuild/release.yaml | 2 +- cloudbuild/trigger-tests.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 3aa9a513..82554d8e 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -30,7 +30,7 @@ steps: # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines - - name: python:3.7 + - name: python:3.9 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/pr-checks.yaml b/cloudbuild/pr-checks.yaml index 5e132a7a..4a8f92cb 100644 --- a/cloudbuild/pr-checks.yaml +++ b/cloudbuild/pr-checks.yaml @@ -17,7 +17,7 @@ steps: # Install pipenv and deps, run pre-commit and unit tests # Then compile pipelines (to make sure they can compile) # need to run "git init" for pre-commit checks to work - - name: python:3.7 + - name: python:3.9 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index 63e60115..d09f451d 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -15,7 +15,7 @@ steps: # Install pipenv, install deps, compile pipelines - - name: python:3.7 + - name: python:3.9 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/trigger-tests.yaml b/cloudbuild/trigger-tests.yaml index a3387438..295e2c50 100644 --- a/cloudbuild/trigger-tests.yaml +++ b/cloudbuild/trigger-tests.yaml @@ -13,7 +13,7 @@ # limitations under the License. --- steps: - - name: 'python:3.7' + - name: 'python:3.9' args: - '-c' - | From 0cfb1edb840503c80b34c80ab28c2fe8c3077682 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Wed, 17 May 2023 13:04:13 +0100 Subject: [PATCH 015/238] refactor(cloudbuild): adjust python version --- cloudbuild/e2e-test.yaml | 2 +- cloudbuild/pr-checks.yaml | 2 +- cloudbuild/release.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 82554d8e..3aa9a513 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -30,7 +30,7 @@ steps: # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines - - name: python:3.9 + - name: python:3.7 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/pr-checks.yaml b/cloudbuild/pr-checks.yaml index 4a8f92cb..5e132a7a 100644 --- a/cloudbuild/pr-checks.yaml +++ b/cloudbuild/pr-checks.yaml @@ -17,7 +17,7 @@ steps: # Install pipenv and deps, run pre-commit and unit tests # Then compile pipelines (to make sure they can compile) # need to run "git init" for pre-commit checks to work - - name: python:3.9 + - name: python:3.7 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index d09f451d..63e60115 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -15,7 +15,7 @@ steps: # Install pipenv, install deps, compile pipelines - - name: python:3.9 + - name: python:3.7 entrypoint: /bin/sh args: - -c From 2f44007ab66e7981048c15094b13d95c48c3ff9d Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Wed, 17 May 2023 14:03:50 +0100 Subject: [PATCH 016/238] build(cloudbuild): update python image from 3.7 to 3.9 --- cloudbuild/e2e-test.yaml | 2 +- cloudbuild/pr-checks.yaml | 2 +- cloudbuild/release.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 3aa9a513..82554d8e 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -30,7 +30,7 @@ steps: # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines - - name: python:3.7 + - name: python:3.9 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/pr-checks.yaml b/cloudbuild/pr-checks.yaml index 5e132a7a..4a8f92cb 100644 --- a/cloudbuild/pr-checks.yaml +++ b/cloudbuild/pr-checks.yaml @@ -17,7 +17,7 @@ steps: # Install pipenv and deps, run pre-commit and unit tests # Then compile pipelines (to make sure they can compile) # need to run "git init" for pre-commit checks to work - - name: python:3.7 + - name: python:3.9 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index 63e60115..d09f451d 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -15,7 +15,7 @@ steps: # Install pipenv, install deps, compile pipelines - - name: python:3.7 + - name: python:3.9 entrypoint: /bin/sh args: - -c From 2d62d9f743216dc967df03df02a704bea8ecf03e Mon Sep 17 00:00:00 2001 From: ariadnafer Date: Fri, 12 May 2023 14:52:56 +0200 Subject: [PATCH 017/238] feat: enable multiquery, update queries and pipelines --- .../bigquery_components/bq_query_to_table.py | 20 +-------- .../tensorflow/prediction/pipeline.py | 30 +++++++------ .../tensorflow/prediction/queries/ingest.sql | 13 +++++- .../pipelines/tensorflow/training/pipeline.py | 45 +++++++------------ .../tensorflow/training/queries/ingest.sql | 29 ++++++++---- .../tensorflow/training/queries/sample.sql | 24 +++------- .../pipelines/xgboost/prediction/pipeline.py | 30 ++++++++----- .../xgboost/prediction/queries/ingest.sql | 9 ++++ .../pipelines/xgboost/training/pipeline.py | 44 +++++++----------- .../xgboost/training/queries/ingest.sql | 27 +++++++---- .../xgboost/training/queries/sample.sql | 12 +++-- 11 files changed, 145 insertions(+), 138 deletions(-) diff --git a/components/bigquery-components/src/bigquery_components/bq_query_to_table.py b/components/bigquery-components/src/bigquery_components/bq_query_to_table.py index 9530bd92..4b6afc29 100644 --- a/components/bigquery-components/src/bigquery_components/bq_query_to_table.py +++ b/components/bigquery-components/src/bigquery_components/bq_query_to_table.py @@ -22,25 +22,15 @@ def bq_query_to_table( query: str, bq_client_project_id: str, - destination_project_id: str, - dataset_id: str = None, - table_id: str = None, dataset_location: str = "EU", - query_job_config: dict = None, ) -> None: """ Run query & create a new BigQuery table Args: query (str): SQL query to execute, results are saved in a BigQuery table bq_client_project_id (str): project id that will be used by the bq client - destination_project_id (str): project id where BQ table will be created - dataset_id (str): dataset id where BQ table will be created - table_id (str): table name (without project id and dataset id) dataset_location (str): bq dataset location - query_job_config (dict): dict containing optional parameters required by the bq query operation. No need to specify destination param - See available parameters here - https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.job.QueryJobConfig.html Returns: None """ @@ -50,13 +40,7 @@ def bq_query_to_table( logging.getLogger().setLevel(logging.INFO) - if (dataset_id is not None) and (table_id is not None): - dest_table_ref = f"{destination_project_id}.{dataset_id}.{table_id}" - else: - dest_table_ref = None - if query_job_config is None: - query_job_config = {} - job_config = bigquery.QueryJobConfig(destination=dest_table_ref, **query_job_config) + job_config = bigquery.QueryJobConfig() bq_client = bigquery.client.Client( project=bq_client_project_id, location=dataset_location @@ -65,7 +49,7 @@ def bq_query_to_table( try: result = query_job.result() - logging.info(f"BQ table {dest_table_ref} created") + logging.info(f"BQ Job finished") except GoogleCloudError as e: logging.error(e) logging.error(query_job.error_result) diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index b011b0c1..3a9fdb02 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import os import pathlib @@ -29,9 +28,10 @@ def tensorflow_pipeline( project_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_project_id: str = os.environ.get("VERTEX_PROJECT_ID"), model_name: str = "simple_tensorflow", - dataset_id: str = "preprocessing", + preprocessing_dataset_id: str = "preprocessing", dataset_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_dataset_id: str = "chicago_taxi_trips", + prediction_dataset_id: str = "prediction", timestamp: str = "2022-12-01 00:00:00", batch_prediction_machine_type: str = "n1-standard-4", batch_prediction_min_replicas: int = 3, @@ -49,13 +49,14 @@ def tensorflow_pipeline( Args: project_id (str): project id of the Google Cloud project project_location (str): location of the Google Cloud project - pipeline_files_gcs_path (str): GCS path where the pipeline files are located ingestion_project_id (str): project id containing the source bigquery data for ingestion. This can be the same as `project_id` if the source data is in the same project where the ML pipeline is executed. model_name (str): name of model - model_label (str): label of model - dataset_id (str): id of BQ dataset used to store all staging data & predictions + preprocessing_dataset_id (str): id of BQ dataset used to + store all staging data . + prediction_dataset_id (str): id of BQ dataset used to + store all predictions. dataset_location (str): location of dataset ingestion_dataset_id (str): dataset id of ingestion data timestamp (str): Optional. Empty or a specific timestamp in ISO 8601 format @@ -91,6 +92,10 @@ def tensorflow_pipeline( queries_folder / "ingest.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, + prediction_dataset=f"{ingestion_project_id}.{prediction_dataset_id}", + preprocessing_dataset=f"{ingestion_project_id}.{preprocessing_dataset_id}", + ingested_table=ingested_table, + dataset_region=project_location, filter_column=time_column, filter_start_value=timestamp, ) @@ -98,14 +103,11 @@ def tensorflow_pipeline( # data ingestion and preprocessing operations kwargs = dict( bq_client_project_id=project_id, - destination_project_id=project_id, - dataset_id=dataset_id, dataset_location=dataset_location, - query_job_config=json.dumps(dict(write_disposition="WRITE_TRUNCATE")), ) - ingest = bq_query_to_table( - query=ingest_query, table_id=ingested_table, **kwargs - ).set_display_name("Ingest data") + ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( + "Ingest data" + ) # lookup champion model champion_model = ( @@ -120,8 +122,10 @@ def tensorflow_pipeline( ) # batch predict from BigQuery to BigQuery - bigquery_source_input_uri = f"bq://{project_id}.{dataset_id}.{ingested_table}" - bigquery_destination_output_uri = f"bq://{project_id}.{dataset_id}" + bigquery_source_input_uri = ( + f"bq://{project_id}.{preprocessing_dataset_id}.{ingested_table}" + ) + bigquery_destination_output_uri = f"bq://{project_id}.{prediction_dataset_id}" instance_config = {"instanceType": "object"} # predict data diff --git a/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql b/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql index e01a7c57..5a44d671 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql +++ b/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql @@ -14,8 +14,16 @@ -- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill +CREATE SCHEMA IF NOT EXISTS `{{ prediction_dataset }}` + OPTIONS ( + description = 'Prediction Dataset', + location = "{{ dataset_region }}"); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( with filter_start_values as ( - SELECT + SELECT IF("{{ filter_start_value }}" = '', CURRENT_DATETIME(), CAST("{{ filter_start_value }}" AS DATETIME)) as filter_start_value ) -- Ingest data between 2 and 3 months ago @@ -43,7 +51,7 @@ SELECT trip_miles, CAST( CASE WHEN trip_seconds is NULL then m.avg_trip_seconds WHEN trip_seconds <= 0 then m.avg_trip_seconds - ELSE trip_seconds + ELSE trip_seconds END AS FLOAT64) AS trip_seconds, payment_type, company, @@ -54,3 +62,4 @@ WHERE "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} AND `{{ field }}` IS NOT NULL {% endfor %} + ); diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 2767d210..56aea64c 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import os import pathlib @@ -101,6 +100,9 @@ def tensorflow_pipeline( queries_folder / "ingest.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + ingested_table=ingested_table, + dataset_region=project_location, filter_column=time_column, target_column=label_column_name, filter_start_value=timestamp, @@ -109,6 +111,8 @@ def tensorflow_pipeline( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=train_table, num_lots=10, lots=tuple(range(8)), ) @@ -116,6 +120,8 @@ def tensorflow_pipeline( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=valid_table, num_lots=10, lots="(8)", ) @@ -123,51 +129,34 @@ def tensorflow_pipeline( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=test_table, num_lots=10, lots="(9)", ) - data_cleaning_query = generate_query( - queries_folder / "engineer_features.sql", - source_dataset=dataset_id, - source_table=train_table, - ) # data ingestion and preprocessing operations - kwargs = dict( - bq_client_project_id=project_id, - destination_project_id=project_id, - dataset_id=dataset_id, - dataset_location=dataset_location, - query_job_config=json.dumps(dict(write_disposition="WRITE_TRUNCATE")), + kwargs = dict(bq_client_project_id=project_id, dataset_location=dataset_location) + ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( + "Ingest data" ) - ingest = bq_query_to_table( - query=ingest_query, table_id=ingested_table, **kwargs - ).set_display_name("Ingest data") - # exporting data to GCS from BQ split_train_data = ( - bq_query_to_table(query=split_train_query, table_id=train_table, **kwargs) + bq_query_to_table(query=split_train_query, **kwargs) .after(ingest) .set_display_name("Split train data") ) split_valid_data = ( - bq_query_to_table(query=split_valid_query, table_id=valid_table, **kwargs) + bq_query_to_table(query=split_valid_query, **kwargs) .after(ingest) .set_display_name("Split validation data") ) split_test_data = ( - bq_query_to_table(query=split_test_query, table_id=test_table, **kwargs) + bq_query_to_table(query=split_test_query, **kwargs) .after(ingest) .set_display_name("Split test data") ) - data_cleaning = ( - bq_query_to_table( - query=data_cleaning_query, table_id=preprocessed_table, **kwargs - ) - .after(split_train_data) - .set_display_name("Clean data") - ) # data extraction to gcs @@ -176,10 +165,10 @@ def tensorflow_pipeline( bq_client_project_id=project_id, source_project_id=project_id, dataset_id=dataset_id, - table_name=preprocessed_table, + table_name=train_table, dataset_location=dataset_location, ) - .after(data_cleaning) + .after(split_train_data) .set_display_name("Extract train data to storage") ).outputs["dataset"] valid_dataset = ( diff --git a/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql b/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql index de9459fc..7f9f3b0b 100644 --- a/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql +++ b/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql @@ -14,26 +14,36 @@ -- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill -with filter_start_values as ( - SELECT - IF("{{ filter_start_value }}" = '', CURRENT_DATETIME(), CAST("{{ filter_start_value }}" AS DATETIME)) as filter_start_value + +CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` + OPTIONS ( + description = 'Preprocessing Dataset', + location = "{{ dataset_region }}"); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( +WITH filter_start_values AS ( +SELECT + IF("{{ filter_start_value }}" = '', + CURRENT_DATETIME(), + CAST("{{ filter_start_value }}" AS DATETIME)) AS filter_start_value ) -- Ingest data between 2 and 3 months ago -,filtered_data as ( +,filtered_data AS ( SELECT * FROM `{{ source_dataset }}.{{ source_table }}`, filter_start_values WHERE DATE({{ filter_column }}) BETWEEN - DATE_SUB(DATE(CAST(filter_start_values.filter_start_value as DATETIME)), INTERVAL 3 MONTH) AND + DATE_SUB(DATE(CAST(filter_start_values.filter_start_value AS DATETIME)), INTERVAL 3 MONTH) AND DATE_SUB(DATE(filter_start_value), INTERVAL 2 MONTH) ) -- Use the average trip_seconds as a replacement for NULL or 0 values -,mean_time as ( +,mean_time AS ( SELECT CAST(avg(trip_seconds) AS INT64) as avg_trip_seconds FROM filtered_data ) - SELECT CAST(EXTRACT(DAYOFWEEK FROM trip_start_timestamp) AS FLOAT64) AS dayofweek, CAST(EXTRACT(HOUR FROM trip_start_timestamp) AS FLOAT64) AS hourofday, @@ -43,15 +53,16 @@ SELECT trip_miles, CAST( CASE WHEN trip_seconds is NULL then m.avg_trip_seconds WHEN trip_seconds <= 0 then m.avg_trip_seconds - ELSE trip_seconds + ELSE trip_seconds END AS FLOAT64) AS trip_seconds, payment_type, company, (fare + tips + tolls + extras) AS `{{ target_column }}`, -FROM filtered_data as t, mean_time as m +FROM filtered_data AS t, mean_time AS m WHERE trip_miles > 0 AND fare > 0 AND fare < 1500 {% for field in ["fare", "trip_start_timestamp", "pickup_longitude", "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} AND `{{ field }}` IS NOT NULL {% endfor %} +); diff --git a/pipelines/src/pipelines/tensorflow/training/queries/sample.sql b/pipelines/src/pipelines/tensorflow/training/queries/sample.sql index bf47bbf6..be8458be 100644 --- a/pipelines/src/pipelines/tensorflow/training/queries/sample.sql +++ b/pipelines/src/pipelines/tensorflow/training/queries/sample.sql @@ -1,20 +1,10 @@ --- Copyright 2022 Google LLC +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ target_table }}`; --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at - --- https://www.apache.org/licenses/LICENSE-2.0 - --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - -SELECT * +CREATE TABLE `{{ preprocessing_dataset }}.{{ target_table }}` AS ( +SELECT + * FROM - `{{ source_dataset }}.{{ source_table }}` AS t + `{{ source_dataset }}.{{ source_table }}` AS t WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - {{ num_lots }}) IN {{ lots }} + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + {{ num_lots }}) IN {{ lots }}); diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index ed474eb8..0c4504ec 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import os import pathlib @@ -29,9 +28,10 @@ def xgboost_pipeline( project_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_project_id: str = os.environ.get("VERTEX_PROJECT_ID"), model_name: str = "simple_xgboost", - dataset_id: str = "preprocessing", + preprocessing_dataset_id: str = "preprocessing", dataset_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_dataset_id: str = "chicago_taxi_trips", + prediction_dataset_id: str = "prediction", timestamp: str = "2022-12-01 00:00:00", batch_prediction_machine_type: str = "n1-standard-4", batch_prediction_min_replicas: int = 3, @@ -50,14 +50,17 @@ def xgboost_pipeline( for ingestion. This can be the same as `project_id` if the source data is in the same project where the ML pipeline is executed. model_name (str): name of model - dataset_id (str): id of BQ dataset used to store all staging data & predictions + preprocessing_dataset_id (str): id of BQ dataset used to + store all staging data . + prediction_dataset_id (str): id of BQ dataset used to + store all predictions. dataset_location (str): location of dataset ingestion_dataset_id (str): dataset id of ingestion data timestamp (str): Optional. Empty or a specific timestamp in ISO 8601 format (YYYY-MM-DDThh:mm:ss.sss±hh:mm or YYYY-MM-DDThh:mm:ss). If any time part is missing, it will be regarded as zero. batch_prediction_machine_type (str): Machine type to be used for Vertex Batch - Prediction. Example machine_types - n1-standard-4, n1-standard-16 etc + Prediction. Example machine_types - n1-standard-4, n1-standard-16 etc. batch_prediction_min_replicas (int): Minimum no of machines to distribute the Vertex Batch Prediction job for horizontal scalability batch_prediction_max_replicas (int): Maximum no of machines to distribute the @@ -85,6 +88,10 @@ def xgboost_pipeline( queries_folder / "ingest.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, + prediction_dataset=f"{ingestion_project_id}.{prediction_dataset_id}", + preprocessing_dataset=f"{ingestion_project_id}.{preprocessing_dataset_id}", + ingested_table=ingested_table, + dataset_region=project_location, filter_column=time_column, filter_start_value=timestamp, ) @@ -92,14 +99,11 @@ def xgboost_pipeline( # data ingestion and preprocessing operations kwargs = dict( bq_client_project_id=project_id, - destination_project_id=project_id, - dataset_id=dataset_id, dataset_location=dataset_location, - query_job_config=json.dumps(dict(write_disposition="WRITE_TRUNCATE")), ) - ingest = bq_query_to_table( - query=ingest_query, table_id=ingested_table, **kwargs - ).set_display_name("Ingest data") + ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( + "Ingest data" + ) # lookup champion model champion_model = ( @@ -114,8 +118,10 @@ def xgboost_pipeline( ) # batch predict from BigQuery to BigQuery - bigquery_source_input_uri = f"bq://{project_id}.{dataset_id}.{ingested_table}" - bigquery_destination_output_uri = f"bq://{project_id}.{dataset_id}" + bigquery_source_input_uri = ( + f"bq://{project_id}.{preprocessing_dataset_id}.{ingested_table}" + ) + bigquery_destination_output_uri = f"bq://{project_id}.{prediction_dataset_id}" batch_prediction = ( model_batch_predict( diff --git a/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql b/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql index e01a7c57..bf8fe57a 100644 --- a/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql +++ b/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql @@ -14,6 +14,14 @@ -- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill +CREATE SCHEMA IF NOT EXISTS `{{ prediction_dataset }}` + OPTIONS ( + description = 'Prediction Dataset', + location = "{{ dataset_region }}"); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( with filter_start_values as ( SELECT IF("{{ filter_start_value }}" = '', CURRENT_DATETIME(), CAST("{{ filter_start_value }}" AS DATETIME)) as filter_start_value @@ -54,3 +62,4 @@ WHERE "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} AND `{{ field }}` IS NOT NULL {% endfor %} + ); diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 27cb47ed..6c9ad9d9 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json import os import pathlib @@ -99,6 +98,9 @@ def xgboost_pipeline( queries_folder / "ingest.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + ingested_table=ingested_table, + dataset_region=project_location, filter_column=time_column, target_column=label_column_name, filter_start_value=timestamp, @@ -107,6 +109,8 @@ def xgboost_pipeline( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=train_table, num_lots=10, lots=tuple(range(8)), ) @@ -114,57 +118,43 @@ def xgboost_pipeline( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=valid_table, num_lots=10, lots="(8)", ) - data_cleaning_query = generate_query( - queries_folder / "engineer_features.sql", - source_dataset=dataset_id, - source_table=train_table, - ) split_test_query = generate_query( queries_folder / "sample.sql", source_dataset=dataset_id, source_table=ingested_table, + preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", + target_table=test_table, num_lots=10, lots="(9)", ) # data ingestion and preprocessing operations - kwargs = dict( - bq_client_project_id=project_id, - destination_project_id=project_id, - dataset_id=dataset_id, - dataset_location=dataset_location, - query_job_config=json.dumps(dict(write_disposition="WRITE_TRUNCATE")), + kwargs = dict(bq_client_project_id=project_id, dataset_location=dataset_location) + ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( + "Ingest data" ) - ingest = bq_query_to_table( - query=ingest_query, table_id=ingested_table, **kwargs - ).set_display_name("Ingest data") split_train_data = ( - bq_query_to_table(query=split_train_query, table_id=train_table, **kwargs) + bq_query_to_table(query=split_train_query, **kwargs) .after(ingest) .set_display_name("Split train data") ) split_valid_data = ( - bq_query_to_table(query=split_valid_query, table_id=valid_table, **kwargs) + bq_query_to_table(query=split_valid_query, **kwargs) .after(ingest) .set_display_name("Split validation data") ) split_test_data = ( - bq_query_to_table(query=split_test_query, table_id=test_table, **kwargs) + bq_query_to_table(query=split_test_query, **kwargs) .after(ingest) .set_display_name("Split test data") ) - data_cleaning = ( - bq_query_to_table( - query=data_cleaning_query, table_id=preprocessed_table, **kwargs - ) - .after(split_train_data) - .set_display_name("Clean data") - ) # data extraction to gcs @@ -173,10 +163,10 @@ def xgboost_pipeline( bq_client_project_id=project_id, source_project_id=project_id, dataset_id=dataset_id, - table_name=preprocessed_table, + table_name=train_table, dataset_location=dataset_location, ) - .after(data_cleaning) + .after(split_train_data) .set_display_name("Extract train data to storage") ).outputs["dataset"] valid_dataset = ( diff --git a/pipelines/src/pipelines/xgboost/training/queries/ingest.sql b/pipelines/src/pipelines/xgboost/training/queries/ingest.sql index de9459fc..fbb6df32 100644 --- a/pipelines/src/pipelines/xgboost/training/queries/ingest.sql +++ b/pipelines/src/pipelines/xgboost/training/queries/ingest.sql @@ -14,26 +14,36 @@ -- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill -with filter_start_values as ( - SELECT - IF("{{ filter_start_value }}" = '', CURRENT_DATETIME(), CAST("{{ filter_start_value }}" AS DATETIME)) as filter_start_value + +CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` + OPTIONS ( + description = 'Preprocessing Dataset', + location = "{{ dataset_region }}"); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( +WITH filter_start_values AS ( +SELECT + IF("{{ filter_start_value }}" = '', + CURRENT_DATETIME(), + CAST("{{ filter_start_value }}" AS DATETIME)) AS filter_start_value ) -- Ingest data between 2 and 3 months ago -,filtered_data as ( +,filtered_data AS ( SELECT * FROM `{{ source_dataset }}.{{ source_table }}`, filter_start_values WHERE DATE({{ filter_column }}) BETWEEN - DATE_SUB(DATE(CAST(filter_start_values.filter_start_value as DATETIME)), INTERVAL 3 MONTH) AND + DATE_SUB(DATE(CAST(filter_start_values.filter_start_value AS DATETIME)), INTERVAL 3 MONTH) AND DATE_SUB(DATE(filter_start_value), INTERVAL 2 MONTH) ) -- Use the average trip_seconds as a replacement for NULL or 0 values -,mean_time as ( +,mean_time AS ( SELECT CAST(avg(trip_seconds) AS INT64) as avg_trip_seconds FROM filtered_data ) - SELECT CAST(EXTRACT(DAYOFWEEK FROM trip_start_timestamp) AS FLOAT64) AS dayofweek, CAST(EXTRACT(HOUR FROM trip_start_timestamp) AS FLOAT64) AS hourofday, @@ -48,10 +58,11 @@ SELECT payment_type, company, (fare + tips + tolls + extras) AS `{{ target_column }}`, -FROM filtered_data as t, mean_time as m +FROM filtered_data AS t, mean_time AS m WHERE trip_miles > 0 AND fare > 0 AND fare < 1500 {% for field in ["fare", "trip_start_timestamp", "pickup_longitude", "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} AND `{{ field }}` IS NOT NULL {% endfor %} +); diff --git a/pipelines/src/pipelines/xgboost/training/queries/sample.sql b/pipelines/src/pipelines/xgboost/training/queries/sample.sql index bb2dc9ce..be8458be 100644 --- a/pipelines/src/pipelines/xgboost/training/queries/sample.sql +++ b/pipelines/src/pipelines/xgboost/training/queries/sample.sql @@ -1,6 +1,10 @@ -SELECT * +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ target_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ target_table }}` AS ( +SELECT + * FROM - `{{ source_dataset }}.{{ source_table }}` AS t + `{{ source_dataset }}.{{ source_table }}` AS t WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - {{ num_lots }}) IN {{ lots }} + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + {{ num_lots }}) IN {{ lots }}); From 299f834cedb6e6da3862a29f52d7abd44032c175 Mon Sep 17 00:00:00 2001 From: ariadnafer Date: Tue, 16 May 2023 13:53:18 +0200 Subject: [PATCH 018/238] refactor: replace custom BQ component with BigqueryQueryJobOp --- .../src/bigquery_components/__init__.py | 2 - .../bigquery_components/bq_query_to_table.py | 57 ---------------- pipelines/src/pipelines/__init__.py | 8 ++- .../tensorflow/prediction/pipeline.py | 12 ++-- .../pipelines/tensorflow/training/pipeline.py | 66 ++++--------------- .../training/queries/engineer_features.sql | 18 ----- .../tensorflow/training/queries/ingest.sql | 50 +++++++++----- .../tensorflow/training/queries/sample.sql | 10 --- .../pipelines/xgboost/prediction/pipeline.py | 13 ++-- .../pipelines/xgboost/training/pipeline.py | 66 ++++--------------- .../training/queries/engineer_features.sql | 4 -- .../xgboost/training/queries/ingest.sql | 50 +++++++++----- .../xgboost/training/queries/sample.sql | 10 --- 13 files changed, 107 insertions(+), 259 deletions(-) delete mode 100644 components/bigquery-components/src/bigquery_components/bq_query_to_table.py delete mode 100644 pipelines/src/pipelines/tensorflow/training/queries/engineer_features.sql delete mode 100644 pipelines/src/pipelines/tensorflow/training/queries/sample.sql delete mode 100644 pipelines/src/pipelines/xgboost/training/queries/engineer_features.sql delete mode 100644 pipelines/src/pipelines/xgboost/training/queries/sample.sql diff --git a/components/bigquery-components/src/bigquery_components/__init__.py b/components/bigquery-components/src/bigquery_components/__init__.py index 2e3870ac..e979abab 100644 --- a/components/bigquery-components/src/bigquery_components/__init__.py +++ b/components/bigquery-components/src/bigquery_components/__init__.py @@ -1,9 +1,7 @@ -from .bq_query_to_table import bq_query_to_table from .extract_bq_to_dataset import extract_bq_to_dataset __version__ = "0.0.1" __all__ = [ - "bq_query_to_table", "extract_bq_to_dataset", ] diff --git a/components/bigquery-components/src/bigquery_components/bq_query_to_table.py b/components/bigquery-components/src/bigquery_components/bq_query_to_table.py deleted file mode 100644 index 4b6afc29..00000000 --- a/components/bigquery-components/src/bigquery_components/bq_query_to_table.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2022 Google LLC - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# https://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from kfp.v2.dsl import component - - -@component( - base_image="python:3.7", - packages_to_install=["google-cloud-bigquery==2.30.0"], -) -def bq_query_to_table( - query: str, - bq_client_project_id: str, - dataset_location: str = "EU", -) -> None: - """ - Run query & create a new BigQuery table - Args: - query (str): SQL query to execute, results are saved in a BigQuery table - bq_client_project_id (str): project id that will be used by the bq client - dataset_location (str): bq dataset location - required by the bq query operation. No need to specify destination param - Returns: - None - """ - from google.cloud.exceptions import GoogleCloudError - from google.cloud import bigquery - import logging - - logging.getLogger().setLevel(logging.INFO) - - job_config = bigquery.QueryJobConfig() - - bq_client = bigquery.client.Client( - project=bq_client_project_id, location=dataset_location - ) - query_job = bq_client.query(query, job_config=job_config) - - try: - result = query_job.result() - logging.info(f"BQ Job finished") - except GoogleCloudError as e: - logging.error(e) - logging.error(query_job.error_result) - logging.error(query_job.errors) - raise e diff --git a/pipelines/src/pipelines/__init__.py b/pipelines/src/pipelines/__init__.py index 113da917..badd535e 100644 --- a/pipelines/src/pipelines/__init__.py +++ b/pipelines/src/pipelines/__init__.py @@ -30,4 +30,10 @@ def generate_query(input_file: Path, **replacements) -> str: with open(input_file, "r") as f: query_template = f.read() - return Template(query_template).render(**replacements) + # Render the template with the provided replacements + query = Template(query_template).render(**replacements) + + # Escape double quotes, newline and tab characters + query = query.replace('"', '\\"').replace("\n", "\\n").replace("\t", "\\t") + + return query diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index 3a9fdb02..60ad3b47 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -15,10 +15,10 @@ import os import pathlib +from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp from kfp.v2 import compiler, dsl from pipelines import generate_query -from bigquery_components import bq_query_to_table from vertex_components import lookup_model, model_batch_predict @@ -101,13 +101,9 @@ def tensorflow_pipeline( ) # data ingestion and preprocessing operations - kwargs = dict( - bq_client_project_id=project_id, - dataset_location=dataset_location, - ) - ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( - "Ingest data" - ) + ingest = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=ingest_query + ).set_display_name("Ingest data") # lookup champion model champion_model = ( diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 56aea64c..ba98bea1 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -15,9 +15,10 @@ import os import pathlib +from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp from kfp.v2 import compiler, dsl from pipelines import generate_query -from bigquery_components import bq_query_to_table, extract_bq_to_dataset +from bigquery_components import extract_bq_to_dataset from vertex_components import ( lookup_model, custom_train_job, @@ -106,57 +107,14 @@ def tensorflow_pipeline( filter_column=time_column, target_column=label_column_name, filter_start_value=timestamp, + train_table=train_table, + validation_table=valid_table, + test_table=test_table, ) - split_train_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=train_table, - num_lots=10, - lots=tuple(range(8)), - ) - split_valid_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=valid_table, - num_lots=10, - lots="(8)", - ) - split_test_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=test_table, - num_lots=10, - lots="(9)", - ) - - # data ingestion and preprocessing operations - kwargs = dict(bq_client_project_id=project_id, dataset_location=dataset_location) - ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( - "Ingest data" - ) - - split_train_data = ( - bq_query_to_table(query=split_train_query, **kwargs) - .after(ingest) - .set_display_name("Split train data") - ) - split_valid_data = ( - bq_query_to_table(query=split_valid_query, **kwargs) - .after(ingest) - .set_display_name("Split validation data") - ) - split_test_data = ( - bq_query_to_table(query=split_test_query, **kwargs) - .after(ingest) - .set_display_name("Split test data") - ) + ingest = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=ingest_query + ).set_display_name("Ingest data") # data extraction to gcs @@ -168,8 +126,9 @@ def tensorflow_pipeline( table_name=train_table, dataset_location=dataset_location, ) - .after(split_train_data) + .after(ingest) .set_display_name("Extract train data to storage") + .set_caching_options(False) ).outputs["dataset"] valid_dataset = ( extract_bq_to_dataset( @@ -179,8 +138,9 @@ def tensorflow_pipeline( table_name=valid_table, dataset_location=dataset_location, ) - .after(split_valid_data) + .after(ingest) .set_display_name("Extract validation data to storage") + .set_caching_options(False) ).outputs["dataset"] test_dataset = ( extract_bq_to_dataset( @@ -191,7 +151,7 @@ def tensorflow_pipeline( dataset_location=dataset_location, destination_gcs_uri=test_dataset_uri, ) - .after(split_test_data) + .after(ingest) .set_display_name("Extract test data to storage") .set_caching_options(False) ).outputs["dataset"] diff --git a/pipelines/src/pipelines/tensorflow/training/queries/engineer_features.sql b/pipelines/src/pipelines/tensorflow/training/queries/engineer_features.sql deleted file mode 100644 index b8094a35..00000000 --- a/pipelines/src/pipelines/tensorflow/training/queries/engineer_features.sql +++ /dev/null @@ -1,18 +0,0 @@ --- Copyright 2022 Google LLC - --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at - --- https://www.apache.org/licenses/LICENSE-2.0 - --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - -/* The Purpose of this query is to clean the data before the training*/ -SELECT - * -FROM `{{ source_dataset }}.{{ source_table }}` diff --git a/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql b/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql index 7f9f3b0b..b3a31606 100644 --- a/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql +++ b/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql @@ -1,20 +1,3 @@ --- Copyright 2022 Google LLC - --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at - --- https://www.apache.org/licenses/LICENSE-2.0 - --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - --- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead --- This allows us to set the filter_start_value to a specific time for testing or for backfill - CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` OPTIONS ( description = 'Preprocessing Dataset', @@ -66,3 +49,36 @@ WHERE AND `{{ field }}` IS NOT NULL {% endfor %} ); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ train_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ train_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (0, 1, 2, 3, 4, 5, 6, 7)); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ validation_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ validation_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (8)); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ test_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ test_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (9)); diff --git a/pipelines/src/pipelines/tensorflow/training/queries/sample.sql b/pipelines/src/pipelines/tensorflow/training/queries/sample.sql deleted file mode 100644 index be8458be..00000000 --- a/pipelines/src/pipelines/tensorflow/training/queries/sample.sql +++ /dev/null @@ -1,10 +0,0 @@ -DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ target_table }}`; - -CREATE TABLE `{{ preprocessing_dataset }}.{{ target_table }}` AS ( -SELECT - * -FROM - `{{ source_dataset }}.{{ source_table }}` AS t -WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - {{ num_lots }}) IN {{ lots }}); diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index 0c4504ec..7fdea71c 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -15,10 +15,10 @@ import os import pathlib +from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp from kfp.v2 import compiler, dsl from pipelines import generate_query -from bigquery_components import bq_query_to_table from vertex_components import lookup_model, model_batch_predict @@ -96,14 +96,9 @@ def xgboost_pipeline( filter_start_value=timestamp, ) - # data ingestion and preprocessing operations - kwargs = dict( - bq_client_project_id=project_id, - dataset_location=dataset_location, - ) - ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( - "Ingest data" - ) + ingest = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=ingest_query + ).set_display_name("Ingest data") # lookup champion model champion_model = ( diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 6c9ad9d9..385e41ab 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -15,9 +15,10 @@ import os import pathlib +from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp from kfp.v2 import compiler, dsl from pipelines import generate_query -from bigquery_components import bq_query_to_table, extract_bq_to_dataset +from bigquery_components import extract_bq_to_dataset from vertex_components import ( lookup_model, custom_train_job, @@ -104,57 +105,14 @@ def xgboost_pipeline( filter_column=time_column, target_column=label_column_name, filter_start_value=timestamp, + train_table=train_table, + validation_table=valid_table, + test_table=test_table, ) - split_train_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=train_table, - num_lots=10, - lots=tuple(range(8)), - ) - split_valid_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=valid_table, - num_lots=10, - lots="(8)", - ) - split_test_query = generate_query( - queries_folder / "sample.sql", - source_dataset=dataset_id, - source_table=ingested_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - target_table=test_table, - num_lots=10, - lots="(9)", - ) - - # data ingestion and preprocessing operations - kwargs = dict(bq_client_project_id=project_id, dataset_location=dataset_location) - ingest = bq_query_to_table(query=ingest_query, **kwargs).set_display_name( - "Ingest data" - ) - - split_train_data = ( - bq_query_to_table(query=split_train_query, **kwargs) - .after(ingest) - .set_display_name("Split train data") - ) - split_valid_data = ( - bq_query_to_table(query=split_valid_query, **kwargs) - .after(ingest) - .set_display_name("Split validation data") - ) - split_test_data = ( - bq_query_to_table(query=split_test_query, **kwargs) - .after(ingest) - .set_display_name("Split test data") - ) + ingest = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=ingest_query + ).set_display_name("Ingest data") # data extraction to gcs @@ -166,8 +124,9 @@ def xgboost_pipeline( table_name=train_table, dataset_location=dataset_location, ) - .after(split_train_data) + .after(ingest) .set_display_name("Extract train data to storage") + .set_caching_options(False) ).outputs["dataset"] valid_dataset = ( extract_bq_to_dataset( @@ -177,8 +136,9 @@ def xgboost_pipeline( table_name=valid_table, dataset_location=dataset_location, ) - .after(split_valid_data) + .after(ingest) .set_display_name("Extract validation data to storage") + .set_caching_options(False) ).outputs["dataset"] test_dataset = ( extract_bq_to_dataset( @@ -189,7 +149,7 @@ def xgboost_pipeline( dataset_location=dataset_location, destination_gcs_uri=test_dataset_uri, ) - .after(split_test_data) + .after(ingest) .set_display_name("Extract test data to storage") .set_caching_options(False) ).outputs["dataset"] diff --git a/pipelines/src/pipelines/xgboost/training/queries/engineer_features.sql b/pipelines/src/pipelines/xgboost/training/queries/engineer_features.sql deleted file mode 100644 index 148d9ce7..00000000 --- a/pipelines/src/pipelines/xgboost/training/queries/engineer_features.sql +++ /dev/null @@ -1,4 +0,0 @@ -/* The Purpose of this query is to clean the data before the training*/ -SELECT - * -FROM `{{ source_dataset }}.{{ source_table }}` diff --git a/pipelines/src/pipelines/xgboost/training/queries/ingest.sql b/pipelines/src/pipelines/xgboost/training/queries/ingest.sql index fbb6df32..10ecf037 100644 --- a/pipelines/src/pipelines/xgboost/training/queries/ingest.sql +++ b/pipelines/src/pipelines/xgboost/training/queries/ingest.sql @@ -1,20 +1,3 @@ --- Copyright 2022 Google LLC - --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at - --- https://www.apache.org/licenses/LICENSE-2.0 - --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - --- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead --- This allows us to set the filter_start_value to a specific time for testing or for backfill - CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` OPTIONS ( description = 'Preprocessing Dataset', @@ -66,3 +49,36 @@ WHERE AND `{{ field }}` IS NOT NULL {% endfor %} ); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ train_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ train_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (0, 1, 2, 3, 4, 5, 6, 7)); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ validation_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ validation_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (8)); + +DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ test_table }}`; + +CREATE TABLE `{{ preprocessing_dataset }}.{{ test_table }}` AS ( +SELECT + * +FROM + `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t +WHERE + MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), + 10) IN (9)); diff --git a/pipelines/src/pipelines/xgboost/training/queries/sample.sql b/pipelines/src/pipelines/xgboost/training/queries/sample.sql deleted file mode 100644 index be8458be..00000000 --- a/pipelines/src/pipelines/xgboost/training/queries/sample.sql +++ /dev/null @@ -1,10 +0,0 @@ -DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ target_table }}`; - -CREATE TABLE `{{ preprocessing_dataset }}.{{ target_table }}` AS ( -SELECT - * -FROM - `{{ source_dataset }}.{{ source_table }}` AS t -WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - {{ num_lots }}) IN {{ lots }}); From 8a7e368e139c625e9456675a551bf1a1c6e1710c Mon Sep 17 00:00:00 2001 From: ariadnafer Date: Thu, 18 May 2023 12:53:00 +0200 Subject: [PATCH 019/238] docs: rename files and add comments --- .../pipelines/tensorflow/prediction/queries/ingest.sql | 4 ++++ pipelines/src/pipelines/tensorflow/training/pipeline.py | 8 ++++---- .../training/queries/{ingest.sql => preprocessing.sql} | 4 ++++ .../src/pipelines/xgboost/prediction/queries/ingest.sql | 4 ++++ pipelines/src/pipelines/xgboost/training/pipeline.py | 8 ++++---- .../training/queries/{ingest.sql => preprocessing.sql} | 4 ++++ 6 files changed, 24 insertions(+), 8 deletions(-) rename pipelines/src/pipelines/tensorflow/training/queries/{ingest.sql => preprocessing.sql} (92%) rename pipelines/src/pipelines/xgboost/training/queries/{ingest.sql => preprocessing.sql} (92%) diff --git a/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql b/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql index 5a44d671..010cfaba 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql +++ b/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql @@ -14,11 +14,15 @@ -- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill + +-- If prediction dataset don't exist, create it CREATE SCHEMA IF NOT EXISTS `{{ prediction_dataset }}` OPTIONS ( description = 'Prediction Dataset', location = "{{ dataset_region }}"); +-- We recreate the ingestion table every time the pipeline run, +-- so we need to drop the generated in the previous run DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index ba98bea1..bfeaef8b 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -98,7 +98,7 @@ def tensorflow_pipeline( queries_folder = pathlib.Path(__file__).parent / "queries" ingest_query = generate_query( - queries_folder / "ingest.sql", + queries_folder / "preprocessing.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", @@ -127,7 +127,7 @@ def tensorflow_pipeline( dataset_location=dataset_location, ) .after(ingest) - .set_display_name("Extract train data to storage") + .set_display_name("Extract train") .set_caching_options(False) ).outputs["dataset"] valid_dataset = ( @@ -139,7 +139,7 @@ def tensorflow_pipeline( dataset_location=dataset_location, ) .after(ingest) - .set_display_name("Extract validation data to storage") + .set_display_name("Extract validation data") .set_caching_options(False) ).outputs["dataset"] test_dataset = ( @@ -152,7 +152,7 @@ def tensorflow_pipeline( destination_gcs_uri=test_dataset_uri, ) .after(ingest) - .set_display_name("Extract test data to storage") + .set_display_name("Extract test data") .set_caching_options(False) ).outputs["dataset"] diff --git a/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql b/pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql similarity index 92% rename from pipelines/src/pipelines/tensorflow/training/queries/ingest.sql rename to pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql index b3a31606..d1644958 100644 --- a/pipelines/src/pipelines/tensorflow/training/queries/ingest.sql +++ b/pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql @@ -1,8 +1,11 @@ +-- If preprocessing dataset don't exist, create it CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` OPTIONS ( description = 'Preprocessing Dataset', location = "{{ dataset_region }}"); +-- We recreate the ingestion table every time the pipeline run, +-- so we need to drop the generated in the previous run DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( @@ -50,6 +53,7 @@ WHERE {% endfor %} ); +-- Drop and creation of train, testing and validations tables DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ train_table }}`; CREATE TABLE `{{ preprocessing_dataset }}.{{ train_table }}` AS ( diff --git a/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql b/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql index bf8fe57a..d6cf4ada 100644 --- a/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql +++ b/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql @@ -14,11 +14,15 @@ -- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill + +-- If prediction dataset don't exist, create it CREATE SCHEMA IF NOT EXISTS `{{ prediction_dataset }}` OPTIONS ( description = 'Prediction Dataset', location = "{{ dataset_region }}"); +-- We recreate the ingestion table every time the pipeline run, +-- so we need to drop the generated in the previous run DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 385e41ab..d0a303b2 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -96,7 +96,7 @@ def xgboost_pipeline( queries_folder = pathlib.Path(__file__).parent / "queries" ingest_query = generate_query( - queries_folder / "ingest.sql", + queries_folder / "preprocessing.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", @@ -125,7 +125,7 @@ def xgboost_pipeline( dataset_location=dataset_location, ) .after(ingest) - .set_display_name("Extract train data to storage") + .set_display_name("Extract train data") .set_caching_options(False) ).outputs["dataset"] valid_dataset = ( @@ -137,7 +137,7 @@ def xgboost_pipeline( dataset_location=dataset_location, ) .after(ingest) - .set_display_name("Extract validation data to storage") + .set_display_name("Extract validation data") .set_caching_options(False) ).outputs["dataset"] test_dataset = ( @@ -150,7 +150,7 @@ def xgboost_pipeline( destination_gcs_uri=test_dataset_uri, ) .after(ingest) - .set_display_name("Extract test data to storage") + .set_display_name("Extract test data") .set_caching_options(False) ).outputs["dataset"] diff --git a/pipelines/src/pipelines/xgboost/training/queries/ingest.sql b/pipelines/src/pipelines/xgboost/training/queries/preprocessing.sql similarity index 92% rename from pipelines/src/pipelines/xgboost/training/queries/ingest.sql rename to pipelines/src/pipelines/xgboost/training/queries/preprocessing.sql index 10ecf037..3672262f 100644 --- a/pipelines/src/pipelines/xgboost/training/queries/ingest.sql +++ b/pipelines/src/pipelines/xgboost/training/queries/preprocessing.sql @@ -1,8 +1,11 @@ +-- If preprocessing dataset don't exist, create it CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` OPTIONS ( description = 'Preprocessing Dataset', location = "{{ dataset_region }}"); +-- We recreate the ingestion table every time the pipeline run, +-- so we need to drop the generated in the previous run DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( @@ -50,6 +53,7 @@ WHERE {% endfor %} ); +-- Drop and creation of train, testing and validations tables DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ train_table }}`; CREATE TABLE `{{ preprocessing_dataset }}.{{ train_table }}` AS ( From c087024d19dde1ff44b220cbcb03eb5aca27332c Mon Sep 17 00:00:00 2001 From: ariadnafer Date: Fri, 19 May 2023 23:23:45 +0200 Subject: [PATCH 020/238] fix: replace quotes in queries --- pipelines/src/pipelines/__init__.py | 5 +---- .../pipelines/tensorflow/prediction/pipeline.py | 2 +- .../queries/{ingest.sql => preprocessing.sql} | 14 +++++++------- .../tensorflow/training/queries/preprocessing.sql | 10 +++++----- .../src/pipelines/xgboost/prediction/pipeline.py | 2 +- .../queries/{ingest.sql => preprocessing.sql} | 14 +++++++------- .../xgboost/training/queries/preprocessing.sql | 10 +++++----- 7 files changed, 27 insertions(+), 30 deletions(-) rename pipelines/src/pipelines/tensorflow/prediction/queries/{ingest.sql => preprocessing.sql} (81%) rename pipelines/src/pipelines/xgboost/prediction/queries/{ingest.sql => preprocessing.sql} (81%) diff --git a/pipelines/src/pipelines/__init__.py b/pipelines/src/pipelines/__init__.py index badd535e..8eeeff45 100644 --- a/pipelines/src/pipelines/__init__.py +++ b/pipelines/src/pipelines/__init__.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +import json from pathlib import Path from jinja2 import Template @@ -33,7 +33,4 @@ def generate_query(input_file: Path, **replacements) -> str: # Render the template with the provided replacements query = Template(query_template).render(**replacements) - # Escape double quotes, newline and tab characters - query = query.replace('"', '\\"').replace("\n", "\\n").replace("\t", "\\t") - return query diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index 60ad3b47..8d6344ac 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -89,7 +89,7 @@ def tensorflow_pipeline( queries_folder = pathlib.Path(__file__).parent / "queries" ingest_query = generate_query( - queries_folder / "ingest.sql", + queries_folder / "preprocessing.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, prediction_dataset=f"{ingestion_project_id}.{prediction_dataset_id}", diff --git a/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql b/pipelines/src/pipelines/tensorflow/prediction/queries/preprocessing.sql similarity index 81% rename from pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql rename to pipelines/src/pipelines/tensorflow/prediction/queries/preprocessing.sql index 010cfaba..3e25a603 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/queries/ingest.sql +++ b/pipelines/src/pipelines/tensorflow/prediction/queries/preprocessing.sql @@ -1,25 +1,25 @@ -- Copyright 2022 Google LLC --- Licensed under the Apache License, Version 2.0 (the "License"); +-- Licensed under the Apache License, Version 2.0 (the 'License'); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- https://www.apache.org/licenses/LICENSE-2.0 -- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, +-- distributed under the License is distributed on an 'AS IS' BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. --- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead +-- Treat 'filter_start_value' as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill -- If prediction dataset don't exist, create it CREATE SCHEMA IF NOT EXISTS `{{ prediction_dataset }}` OPTIONS ( description = 'Prediction Dataset', - location = "{{ dataset_region }}"); + location = '{{ dataset_region }}'); -- We recreate the ingestion table every time the pipeline run, -- so we need to drop the generated in the previous run @@ -28,7 +28,7 @@ DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( with filter_start_values as ( SELECT - IF("{{ filter_start_value }}" = '', CURRENT_DATETIME(), CAST("{{ filter_start_value }}" AS DATETIME)) as filter_start_value + IF('{{ filter_start_value }}' = '', CURRENT_DATETIME(), CAST('{{ filter_start_value }}' AS DATETIME)) as filter_start_value ) -- Ingest data between 2 and 3 months ago ,filtered_data as ( @@ -62,8 +62,8 @@ SELECT FROM filtered_data as t, mean_time as m WHERE trip_miles > 0 AND fare > 0 AND fare < 1500 - {% for field in ["fare", "trip_start_timestamp", "pickup_longitude", - "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} + {% for field in ['fare', 'trip_start_timestamp', 'pickup_longitude', + 'pickup_latitude', 'dropoff_longitude', 'dropoff_latitude','payment_type','company'] %} AND `{{ field }}` IS NOT NULL {% endfor %} ); diff --git a/pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql b/pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql index d1644958..0182aac2 100644 --- a/pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql +++ b/pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql @@ -2,7 +2,7 @@ CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` OPTIONS ( description = 'Preprocessing Dataset', - location = "{{ dataset_region }}"); + location = '{{ dataset_region }}'); -- We recreate the ingestion table every time the pipeline run, -- so we need to drop the generated in the previous run @@ -11,9 +11,9 @@ DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( WITH filter_start_values AS ( SELECT - IF("{{ filter_start_value }}" = '', + IF('{{ filter_start_value }}' = '', CURRENT_DATETIME(), - CAST("{{ filter_start_value }}" AS DATETIME)) AS filter_start_value + CAST('{{ filter_start_value }}' AS DATETIME)) AS filter_start_value ) -- Ingest data between 2 and 3 months ago ,filtered_data AS ( @@ -47,8 +47,8 @@ SELECT FROM filtered_data AS t, mean_time AS m WHERE trip_miles > 0 AND fare > 0 AND fare < 1500 - {% for field in ["fare", "trip_start_timestamp", "pickup_longitude", - "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} + {% for field in ['fare', 'trip_start_timestamp', 'pickup_longitude', + 'pickup_latitude', 'dropoff_longitude', 'dropoff_latitude','payment_type','company'] %} AND `{{ field }}` IS NOT NULL {% endfor %} ); diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index 7fdea71c..4cd796c1 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -85,7 +85,7 @@ def xgboost_pipeline( queries_folder = pathlib.Path(__file__).parent / "queries" ingest_query = generate_query( - queries_folder / "ingest.sql", + queries_folder / "preprocessing.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, prediction_dataset=f"{ingestion_project_id}.{prediction_dataset_id}", diff --git a/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql b/pipelines/src/pipelines/xgboost/prediction/queries/preprocessing.sql similarity index 81% rename from pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql rename to pipelines/src/pipelines/xgboost/prediction/queries/preprocessing.sql index d6cf4ada..294223a4 100644 --- a/pipelines/src/pipelines/xgboost/prediction/queries/ingest.sql +++ b/pipelines/src/pipelines/xgboost/prediction/queries/preprocessing.sql @@ -1,25 +1,25 @@ -- Copyright 2022 Google LLC --- Licensed under the Apache License, Version 2.0 (the "License"); +-- Licensed under the Apache License, Version 2.0 (the 'License'); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- https://www.apache.org/licenses/LICENSE-2.0 -- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, +-- distributed under the License is distributed on an 'AS IS' BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. --- Treat "filter_start_value" as the current time, unless it is empty then use CURRENT_DATETIME() instead +-- Treat 'filter_start_value' as the current time, unless it is empty then use CURRENT_DATETIME() instead -- This allows us to set the filter_start_value to a specific time for testing or for backfill -- If prediction dataset don't exist, create it CREATE SCHEMA IF NOT EXISTS `{{ prediction_dataset }}` OPTIONS ( description = 'Prediction Dataset', - location = "{{ dataset_region }}"); + location = '{{ dataset_region }}'); -- We recreate the ingestion table every time the pipeline run, -- so we need to drop the generated in the previous run @@ -28,7 +28,7 @@ DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( with filter_start_values as ( SELECT - IF("{{ filter_start_value }}" = '', CURRENT_DATETIME(), CAST("{{ filter_start_value }}" AS DATETIME)) as filter_start_value + IF('{{ filter_start_value }}' = '', CURRENT_DATETIME(), CAST('{{ filter_start_value }}' AS DATETIME)) as filter_start_value ) -- Ingest data between 2 and 3 months ago ,filtered_data as ( @@ -62,8 +62,8 @@ SELECT FROM filtered_data as t, mean_time as m WHERE trip_miles > 0 AND fare > 0 AND fare < 1500 - {% for field in ["fare", "trip_start_timestamp", "pickup_longitude", - "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} + {% for field in ['fare', 'trip_start_timestamp', 'pickup_longitude', + 'pickup_latitude', 'dropoff_longitude', 'dropoff_latitude','payment_type','company'] %} AND `{{ field }}` IS NOT NULL {% endfor %} ); diff --git a/pipelines/src/pipelines/xgboost/training/queries/preprocessing.sql b/pipelines/src/pipelines/xgboost/training/queries/preprocessing.sql index 3672262f..a7291038 100644 --- a/pipelines/src/pipelines/xgboost/training/queries/preprocessing.sql +++ b/pipelines/src/pipelines/xgboost/training/queries/preprocessing.sql @@ -2,7 +2,7 @@ CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` OPTIONS ( description = 'Preprocessing Dataset', - location = "{{ dataset_region }}"); + location = '{{ dataset_region }}'); -- We recreate the ingestion table every time the pipeline run, -- so we need to drop the generated in the previous run @@ -11,9 +11,9 @@ DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( WITH filter_start_values AS ( SELECT - IF("{{ filter_start_value }}" = '', + IF('{{ filter_start_value }}' = '', CURRENT_DATETIME(), - CAST("{{ filter_start_value }}" AS DATETIME)) AS filter_start_value + CAST('{{ filter_start_value }}' AS DATETIME)) AS filter_start_value ) -- Ingest data between 2 and 3 months ago ,filtered_data AS ( @@ -47,8 +47,8 @@ SELECT FROM filtered_data AS t, mean_time AS m WHERE trip_miles > 0 AND fare > 0 AND fare < 1500 - {% for field in ["fare", "trip_start_timestamp", "pickup_longitude", - "pickup_latitude", "dropoff_longitude", "dropoff_latitude","payment_type","company"] %} + {% for field in ['fare', 'trip_start_timestamp', 'pickup_longitude', + 'pickup_latitude', 'dropoff_longitude', 'dropoff_latitude','payment_type','company'] %} AND `{{ field }}` IS NOT NULL {% endfor %} ); From e8b492f2343db6144d534d92da851d71c8ee150c Mon Sep 17 00:00:00 2001 From: ariadnafer Date: Sat, 20 May 2023 23:05:29 +0200 Subject: [PATCH 021/238] refactor: rename queries and pipeline steps --- pipelines/src/pipelines/__init__.py | 7 ++----- .../src/pipelines/tensorflow/prediction/pipeline.py | 8 ++++---- .../src/pipelines/tensorflow/training/pipeline.py | 12 ++++++------ .../src/pipelines/xgboost/prediction/pipeline.py | 8 ++++---- pipelines/src/pipelines/xgboost/training/pipeline.py | 12 ++++++------ 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/pipelines/src/pipelines/__init__.py b/pipelines/src/pipelines/__init__.py index 8eeeff45..113da917 100644 --- a/pipelines/src/pipelines/__init__.py +++ b/pipelines/src/pipelines/__init__.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import json + from pathlib import Path from jinja2 import Template @@ -30,7 +30,4 @@ def generate_query(input_file: Path, **replacements) -> str: with open(input_file, "r") as f: query_template = f.read() - # Render the template with the provided replacements - query = Template(query_template).render(**replacements) - - return query + return Template(query_template).render(**replacements) diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index 8d6344ac..7290a4c9 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -88,7 +88,7 @@ def tensorflow_pipeline( # operations queries_folder = pathlib.Path(__file__).parent / "queries" - ingest_query = generate_query( + preprocessing_query = generate_query( queries_folder / "preprocessing.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, @@ -101,8 +101,8 @@ def tensorflow_pipeline( ) # data ingestion and preprocessing operations - ingest = BigqueryQueryJobOp( - project=project_id, location=dataset_location, query=ingest_query + preprocessing = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=preprocessing_query ).set_display_name("Ingest data") # lookup champion model @@ -143,7 +143,7 @@ def tensorflow_pipeline( monitoring_skew_config=monitoring_skew_config, instance_config=instance_config, ) - .after(ingest) + .after(preprocessing) .set_display_name("Batch prediction job") ) diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index bfeaef8b..c59a6bd3 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -97,7 +97,7 @@ def tensorflow_pipeline( queries_folder = pathlib.Path(__file__).parent / "queries" - ingest_query = generate_query( + preprocessing_query = generate_query( queries_folder / "preprocessing.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, @@ -112,8 +112,8 @@ def tensorflow_pipeline( test_table=test_table, ) - ingest = BigqueryQueryJobOp( - project=project_id, location=dataset_location, query=ingest_query + preprocessing = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=preprocessing_query ).set_display_name("Ingest data") # data extraction to gcs @@ -126,7 +126,7 @@ def tensorflow_pipeline( table_name=train_table, dataset_location=dataset_location, ) - .after(ingest) + .after(preprocessing) .set_display_name("Extract train") .set_caching_options(False) ).outputs["dataset"] @@ -138,7 +138,7 @@ def tensorflow_pipeline( table_name=valid_table, dataset_location=dataset_location, ) - .after(ingest) + .after(preprocessing) .set_display_name("Extract validation data") .set_caching_options(False) ).outputs["dataset"] @@ -151,7 +151,7 @@ def tensorflow_pipeline( dataset_location=dataset_location, destination_gcs_uri=test_dataset_uri, ) - .after(ingest) + .after(preprocessing) .set_display_name("Extract test data") .set_caching_options(False) ).outputs["dataset"] diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index 4cd796c1..5592a121 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -84,7 +84,7 @@ def xgboost_pipeline( # operations queries_folder = pathlib.Path(__file__).parent / "queries" - ingest_query = generate_query( + preprocessing_query = generate_query( queries_folder / "preprocessing.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, @@ -96,8 +96,8 @@ def xgboost_pipeline( filter_start_value=timestamp, ) - ingest = BigqueryQueryJobOp( - project=project_id, location=dataset_location, query=ingest_query + preprocessing = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=preprocessing_query ).set_display_name("Ingest data") # lookup champion model @@ -135,7 +135,7 @@ def xgboost_pipeline( monitoring_alert_email_addresses=monitoring_alert_email_addresses, monitoring_skew_config=monitoring_skew_config, ) - .after(ingest) + .after(preprocessing) .set_display_name("Batch prediction job") ) diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index d0a303b2..0befe4db 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -95,7 +95,7 @@ def xgboost_pipeline( queries_folder = pathlib.Path(__file__).parent / "queries" - ingest_query = generate_query( + preprocessing_query = generate_query( queries_folder / "preprocessing.sql", source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", source_table=ingestion_table, @@ -110,8 +110,8 @@ def xgboost_pipeline( test_table=test_table, ) - ingest = BigqueryQueryJobOp( - project=project_id, location=dataset_location, query=ingest_query + preprocessing = BigqueryQueryJobOp( + project=project_id, location=dataset_location, query=preprocessing_query ).set_display_name("Ingest data") # data extraction to gcs @@ -124,7 +124,7 @@ def xgboost_pipeline( table_name=train_table, dataset_location=dataset_location, ) - .after(ingest) + .after(preprocessing) .set_display_name("Extract train data") .set_caching_options(False) ).outputs["dataset"] @@ -136,7 +136,7 @@ def xgboost_pipeline( table_name=valid_table, dataset_location=dataset_location, ) - .after(ingest) + .after(preprocessing) .set_display_name("Extract validation data") .set_caching_options(False) ).outputs["dataset"] @@ -149,7 +149,7 @@ def xgboost_pipeline( dataset_location=dataset_location, destination_gcs_uri=test_dataset_uri, ) - .after(ingest) + .after(preprocessing) .set_display_name("Extract test data") .set_caching_options(False) ).outputs["dataset"] From e0b5351ca2592273ba3847606df4a4462c980ba0 Mon Sep 17 00:00:00 2001 From: Felix Schaumann <89205956+felix-datatonic@users.noreply.github.com> Date: Mon, 22 May 2023 09:08:15 +0200 Subject: [PATCH 022/238] Update pipelines/src/pipelines/tensorflow/training/pipeline.py --- pipelines/src/pipelines/tensorflow/training/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index c59a6bd3..f6e70525 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -114,7 +114,7 @@ def tensorflow_pipeline( preprocessing = BigqueryQueryJobOp( project=project_id, location=dataset_location, query=preprocessing_query - ).set_display_name("Ingest data") + ).set_display_name("Ingest & preprocess data") # data extraction to gcs From ab771688758a5d3584ab18114a3396396fe8bb4a Mon Sep 17 00:00:00 2001 From: Felix Schaumann <89205956+felix-datatonic@users.noreply.github.com> Date: Mon, 22 May 2023 09:08:20 +0200 Subject: [PATCH 023/238] Update pipelines/src/pipelines/xgboost/training/pipeline.py --- pipelines/src/pipelines/xgboost/training/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 0befe4db..6737a627 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -112,7 +112,7 @@ def xgboost_pipeline( preprocessing = BigqueryQueryJobOp( project=project_id, location=dataset_location, query=preprocessing_query - ).set_display_name("Ingest data") + ).set_display_name("Ingest & preprocess data") # data extraction to gcs From 6b32a85ff853587893b4c13e5636af6411257ad5 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Mon, 22 May 2023 10:59:45 +0100 Subject: [PATCH 024/238] feat: changes to release ci/cd --- cloudbuild/release.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index 63e60115..4a48a1f7 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -35,12 +35,10 @@ steps: args: - -c - | - mkdir -p ${TAG_NAME}/training/assets && \ - mkdir -p ${TAG_NAME}/prediction/assets && \ - cp pipelines/training.json ${TAG_NAME}/training/training.json && \ - cp pipelines/prediction.json ${TAG_NAME}/prediction/prediction.json && \ - cp -r pipelines/pipelines/${_PIPELINE_TEMPLATE}/training/assets ${TAG_NAME}/training/ && \ - cp -r pipelines/pipelines/${_PIPELINE_TEMPLATE}/prediction/assets ${TAG_NAME}/prediction/ && \ + mkdir -p ${TAG_NAME}/assets && \ + cp pipelines/training.json ${TAG_NAME}/training.json && \ + cp pipelines/prediction.json ${TAG_NAME}/prediction.json && \ + cp -r pipelines/assets ${TAG_NAME}/assets && \ for dest in ${_PIPELINE_PUBLISH_GCS_PATHS} ; do \ gsutil cp -r ${TAG_NAME} $$dest ; \ done From af91f0ed5b3a39434c78307cc7f19141dcb793e9 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Mon, 22 May 2023 11:26:57 +0100 Subject: [PATCH 025/238] docs: readme update related to changes to ci/cd --- README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b57cd4c5..927ea440 100644 --- a/README.md +++ b/README.md @@ -242,14 +242,10 @@ Below is a diagram of how the files are published in each environment in the `e2 ``` . <-- GCS directory set by _PIPELINE_PUBLISH_GCS_PATH └── TAG_NAME or GIT COMMIT HASH <-- Git tag used for the release (release.yaml) OR git commit hash (e2e-test.yaml) - ├── prediction - │ ├── assets - │ │ └── some_useful_file.json - │ └── prediction.json <-- compiled prediction pipeline - └── training - ├── assets - │ └── training_task.py - └── training.json <-- compiled training pipeline + ├── training.json + ├── prediction.json + ├── assets + │ └── some_useful_file.json ``` 4. `terraform-plan.yaml` - Checks the Terraform configuration under `terraform/envs/` (e.g. `terraform/envs/test`), and produces a summary of any proposed changes that will be applied on merge to the main branch. From 48b7be22bd8d193f45f978e114fcff8daba608c6 Mon Sep 17 00:00:00 2001 From: ariadnafer Date: Mon, 22 May 2023 13:01:42 +0200 Subject: [PATCH 026/238] feat: radd missing tests --- Makefile | 12 ++ .../tests/test_extract_bq_to_dataset.py | 73 +++++++ .../tests/test_custom_training_job.py | 91 +++++++++ .../tests/test_import_model_evaluation.py | 51 +++++ pipelines/Pipfile | 1 + pipelines/Pipfile.lock | 189 ++++++++++++------ pipelines/pyproject.toml | 1 + 7 files changed, 352 insertions(+), 66 deletions(-) create mode 100644 components/bigquery-components/tests/test_extract_bq_to_dataset.py create mode 100644 components/vertex-components/tests/test_custom_training_job.py create mode 100644 components/vertex-components/tests/test_import_model_evaluation.py diff --git a/Makefile b/Makefile index 05a3c1e8..faa0fa43 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,18 @@ test-all-components: ## Run unit tests for all pipeline components $(MAKE) test-components GROUP=$$(basename $$component_group) ; \ done +test-components-coverage: ## Run tests with coverage + @cd "components/${GROUP}" && \ + pipenv run coverage run -m pytest && \ + pipenv run coverage report -m + +test-all-components-coverage: ## Run tests with coverage + @set -e && \ + for component_group in components/*/ ; do \ + echo "Test components under $$component_group" && \ + $(MAKE) test-components-coverage GROUP=$$(basename $$component_group) ; \ + done + sync-assets: ## Sync assets folder to GCS. Must specify pipeline= @if [ -d "./pipelines/src/pipelines/${PIPELINE_TEMPLATE}/$(pipeline)/assets/" ] ; then \ echo "Syncing assets to GCS" && \ diff --git a/components/bigquery-components/tests/test_extract_bq_to_dataset.py b/components/bigquery-components/tests/test_extract_bq_to_dataset.py new file mode 100644 index 00000000..8d83a62b --- /dev/null +++ b/components/bigquery-components/tests/test_extract_bq_to_dataset.py @@ -0,0 +1,73 @@ +import google.cloud.bigquery # noqa +from kfp.v2.dsl import Dataset +from unittest import mock + +import bigquery_components + +extract_bq_to_dataset = bigquery_components.extract_bq_to_dataset.python_func + + +def test_extract_bq_to_dataset(tmpdir): + with mock.patch("google.cloud.bigquery.client.Client") as mock_client, mock.patch( + "google.cloud.bigquery.job.ExtractJobConfig" + ) as mock_job_config, mock.patch("google.cloud.bigquery.table.Table") as mock_table: + + # Mock the Dataset path + mock_path = tmpdir + + # Set up the mock Client + mock_client.extract_table.return_value = "my-job" + + # Set up the mock Table + mock_table.return_value.table_ref = "my-table" + + # Set up the mock ExtractJob + mock_job_config.return_value = "mock-job-config" + + # Call the function + extract_bq_to_dataset( + bq_client_project_id="my-project-id", + source_project_id="source-project-id", + dataset_id="dataset-id", + table_name="table-name", + dataset=Dataset(uri=mock_path), + destination_gcs_uri="gs://mock_bucket", + dataset_location="EU", + extract_job_config=None, + skip_if_exists=False, + ) + + # Check that Client.extract_table was called correctly + mock_client.return_value.extract_table.assert_called_once_with( + mock_table.return_value, "gs://mock_bucket", job_config="mock-job-config" + ) + + +def test_extract_bq_to_dataset_skip_existing(tmpdir): + with mock.patch("google.cloud.bigquery.client.Client") as mock_client, mock.patch( + "google.cloud.bigquery.table.Table" + ), mock.patch("google.cloud.bigquery.job.ExtractJobConfig"), mock.patch( + "pathlib.Path.exists" + ) as mock_path_exists: + + # # Mock the Dataset path + mock_path = tmpdir + + # Mock that the destination already exists + mock_path_exists.return_value = True + + # Call the function with skip_if_exists set to True + extract_bq_to_dataset( + bq_client_project_id="my-project-id", + source_project_id="source-project-id", + dataset_id="dataset-id", + table_name="table-name", + dataset=Dataset(uri=mock_path), + destination_gcs_uri="gs://mock_bucket", + dataset_location="EU", + extract_job_config=None, + skip_if_exists=True, + ) + + # Ensure that Client.extract_table was not called + assert not mock_client.return_value.extract_table.called diff --git a/components/vertex-components/tests/test_custom_training_job.py b/components/vertex-components/tests/test_custom_training_job.py new file mode 100644 index 00000000..2658f318 --- /dev/null +++ b/components/vertex-components/tests/test_custom_training_job.py @@ -0,0 +1,91 @@ +import google.cloud.aiplatform as aip # noqa +from kfp.v2.dsl import Dataset, Metrics, Artifact +from unittest import mock +import pytest + + +import vertex_components + +custom_train_job = vertex_components.custom_train_job.python_func + + +def test_custom_train_job(tmpdir): + with mock.patch( + "google.cloud.aiplatform.CustomTrainingJob" + ) as mock_job, mock.patch("os.path.exists") as mock_exists, mock.patch( + "builtins.open", mock.mock_open(read_data="{}") + ) as mock_open: + + mock_exists.return_value = True + + mock_train_data = Dataset(uri=tmpdir) + mock_valid_data = Dataset(uri=tmpdir) + mock_test_data = Dataset(uri=tmpdir) + + mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) + mock_metrics = Metrics(uri=tmpdir) + + custom_train_job( + train_script_uri="gs://my-bucket/train_script.py", + train_data=mock_train_data, + valid_data=mock_valid_data, + test_data=mock_test_data, + project_id="my-project-id", + project_location="europe-west4", + model_display_name="my-model", + train_container_uri="gcr.io/my-project/my-image:latest", + serving_container_uri="gcr.io/my-project/my-serving-image:latest", + model=mock_model, + metrics=mock_metrics, + staging_bucket="gs://my-bucket", + job_name="my-job", + ) + + mock_job.assert_called_once_with( + project="my-project-id", + location="europe-west4", + staging_bucket="gs://my-bucket", + display_name="my-job", + script_path="/gcs/my-bucket/train_script.py", + container_uri="gcr.io/my-project/my-image:latest", + requirements=None, + model_serving_container_image_uri="gcr.io/my-project/my-serving-image:latest", # noqa: E501 + ) + + mock_open.assert_called_once_with(tmpdir, "r") + + +def test_custom_train_script_not_found(tmpdir): + with pytest.raises(ValueError), mock.patch( + "google.cloud.aiplatform.CustomTrainingJob" + ) as mock_job, mock.patch("os.path.exists") as mock_exists, mock.patch( + "builtins.open", mock.mock_open(read_data="{}") + ) as mock_open: + + mock_exists.return_value = False # Simulate script path not found + + mock_train_data = Dataset(uri=tmpdir) + mock_valid_data = Dataset(uri=tmpdir) + mock_test_data = Dataset(uri=tmpdir) + + mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) + mock_metrics = Metrics(uri=tmpdir) + + custom_train_job( + train_script_uri="gs://my-bucket/train_script.py", + train_data=mock_train_data, + valid_data=mock_valid_data, + test_data=mock_test_data, + project_id="my-project-id", + project_location="europe-west4", + model_display_name="my-model", + train_container_uri="gcr.io/my-project/my-image:latest", + serving_container_uri="gcr.io/my-project/my-serving-image:latest", + model=mock_model, + metrics=mock_metrics, + staging_bucket="gs://my-bucket", + job_name="my-job", + ) + + mock_job.assert_not_called() + mock_open.assert_not_called() diff --git a/components/vertex-components/tests/test_import_model_evaluation.py b/components/vertex-components/tests/test_import_model_evaluation.py new file mode 100644 index 00000000..494c78a2 --- /dev/null +++ b/components/vertex-components/tests/test_import_model_evaluation.py @@ -0,0 +1,51 @@ +from unittest import mock +from kfp.v2.dsl import Model, Metrics, Dataset + +import vertex_components +from google.cloud.aiplatform_v1 import ModelEvaluation + + +import_model_evaluation = vertex_components.import_model_evaluation.python_func + + +def test_import_model_evaluation(tmpdir): + with mock.patch( + "google.cloud.aiplatform_v1.ModelServiceClient" + ) as mock_service_client, mock.patch( + "builtins.open", + mock.mock_open(read_data='{"accuracy": 0.85, "problemType": "classification"}'), + create=True, + ) as mock_open, mock.patch( + "google.protobuf.json_format.ParseDict" + ) as mock_parse_dict: + + mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) + mock_metrics = Metrics(uri=tmpdir) + mock_dataset = Dataset(uri=tmpdir) + + mock_service_client_instance = mock_service_client.return_value + mock_service_client_instance.import_model_evaluation.return_value = ( + ModelEvaluation(name="model_evaluation_name") + ) + + # Set the return value for ParseDict to be a mock ModelEvaluation + mock_parse_dict.return_value = mock.MagicMock(spec=ModelEvaluation) + + model_evaluation_name = import_model_evaluation( + model=mock_model, + metrics=mock_metrics, + test_dataset=mock_dataset, + pipeline_job_id="1234", + project_location="my-location", + evaluation_name="Imported evaluation", + ) + + mock_service_client_instance.import_model_evaluation.assert_called_once_with( + parent=mock_model.metadata["resourceName"], + model_evaluation=mock_parse_dict.return_value, + ) + + # Check that open was called with the correct path + mock_open.assert_called_once_with(mock_metrics.uri) + + assert model_evaluation_name[0] == "model_evaluation_name" diff --git a/pipelines/Pipfile b/pipelines/Pipfile index 507b4e6d..9538e7fc 100644 --- a/pipelines/Pipfile +++ b/pipelines/Pipfile @@ -14,6 +14,7 @@ bigquery-components = {editable = true, path = "./../components/bigquery-compone [dev-packages] pytest = ">=7.3.1,<8.0.0" pre-commit = ">=2.14.1,<3.0.0" +coverage = "==7.2.5" [requires] python_version = "3.7" diff --git a/pipelines/Pipfile.lock b/pipelines/Pipfile.lock index 92d8d064..42ce01df 100644 --- a/pipelines/Pipfile.lock +++ b/pipelines/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c889b828537547fe3e76c0ca6f812f331bffdf13bfd8c72104145db94eb555e0" + "sha256": "2350f00a78fbfb02ef816a1ba3f5316c810e711a7cc685895cb135cb293d38cd" }, "pipfile-spec": 6, "requires": { @@ -192,11 +192,11 @@ }, "google-auth": { "hashes": [ - "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc", - "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f" + "sha256:55a395cdfd3f3dd3f649131d41f97c17b4ed8a2aac1be3502090c716314e8a37", + "sha256:d7a3249027e7f464fbbfd7ee8319a08ad09d2eea51578575c4bd360ffa049ccb" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.17.3" + "version": "==2.18.1" }, "google-auth-httplib2": { "hashes": [ @@ -246,11 +246,11 @@ }, "google-cloud-resource-manager": { "hashes": [ - "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a", - "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287" + "sha256:41a2204532f084c707fde0bc1a9bc95c7e0b739d7072dd0b8a25106667a56184", + "sha256:c974fb6f9810476cf7b63ea89394c1a8df47f7f2dc2303e728bb74b500bcde67" ], "markers": "python_version >= '3.7'", - "version": "==1.10.0" + "version": "==1.10.1" }, "google-cloud-storage": { "hashes": [ @@ -360,53 +360,53 @@ }, "grpcio": { "hashes": [ - "sha256:02000b005bc8b72ff50c477b6431e8886b29961159e8b8d03c00b3dd9139baed", - "sha256:031bbd26656e0739e4b2c81c172155fb26e274b8d0312d67aefc730bcba915b6", - "sha256:1209d6b002b26e939e4c8ea37a3d5b4028eb9555394ea69fb1adbd4b61a10bb8", - "sha256:125ed35aa3868efa82eabffece6264bf638cfdc9f0cd58ddb17936684aafd0f8", - "sha256:1382bc499af92901c2240c4d540c74eae8a671e4fe9839bfeefdfcc3a106b5e2", - "sha256:16bca8092dd994f2864fdab278ae052fad4913f36f35238b2dd11af2d55a87db", - "sha256:1c59d899ee7160638613a452f9a4931de22623e7ba17897d8e3e348c2e9d8d0b", - "sha256:1d109df30641d050e009105f9c9ca5a35d01e34d2ee2a4e9c0984d392fd6d704", - "sha256:1fa7d6ddd33abbd3c8b3d7d07c56c40ea3d1891ce3cd2aa9fa73105ed5331866", - "sha256:21c4a1aae861748d6393a3ff7867473996c139a77f90326d9f4104bebb22d8b8", - "sha256:224166f06ccdaf884bf35690bf4272997c1405de3035d61384ccb5b25a4c1ca8", - "sha256:2262bd3512ba9e9f0e91d287393df6f33c18999317de45629b7bd46c40f16ba9", - "sha256:2585b3c294631a39b33f9f967a59b0fad23b1a71a212eba6bc1e3ca6e6eec9ee", - "sha256:27fb030a4589d2536daec5ff5ba2a128f4f155149efab578fe2de2cb21596d3d", - "sha256:30fbbce11ffeb4f9f91c13fe04899aaf3e9a81708bedf267bf447596b95df26b", - "sha256:3930669c9e6f08a2eed824738c3d5699d11cd47a0ecc13b68ed11595710b1133", - "sha256:3b170e441e91e4f321e46d3cc95a01cb307a4596da54aca59eb78ab0fc03754d", - "sha256:3db71c6f1ab688d8dfc102271cedc9828beac335a3a4372ec54b8bf11b43fd29", - "sha256:48cb7af77238ba16c77879009003f6b22c23425e5ee59cb2c4c103ec040638a5", - "sha256:49eace8ea55fbc42c733defbda1e4feb6d3844ecd875b01bb8b923709e0f5ec8", - "sha256:533eaf5b2a79a3c6f35cbd6a095ae99cac7f4f9c0e08bdcf86c130efd3c32adf", - "sha256:5942a3e05630e1ef5b7b5752e5da6582460a2e4431dae603de89fc45f9ec5aa9", - "sha256:62117486460c83acd3b5d85c12edd5fe20a374630475388cfc89829831d3eb79", - "sha256:650f5f2c9ab1275b4006707411bb6d6bc927886874a287661c3c6f332d4c068b", - "sha256:6dc1e2c9ac292c9a484ef900c568ccb2d6b4dfe26dfa0163d5bc815bb836c78d", - "sha256:73c238ef6e4b64272df7eec976bb016c73d3ab5a6c7e9cd906ab700523d312f3", - "sha256:775a2f70501370e5ba54e1ee3464413bff9bd85bd9a0b25c989698c44a6fb52f", - "sha256:860fcd6db7dce80d0a673a1cc898ce6bc3d4783d195bbe0e911bf8a62c93ff3f", - "sha256:87f47bf9520bba4083d65ab911f8f4c0ac3efa8241993edd74c8dd08ae87552f", - "sha256:960b176e0bb2b4afeaa1cd2002db1e82ae54c9b6e27ea93570a42316524e77cf", - "sha256:a7caf553ccaf715ec05b28c9b2ab2ee3fdb4036626d779aa09cf7cbf54b71445", - "sha256:a947d5298a0bbdd4d15671024bf33e2b7da79a70de600ed29ba7e0fef0539ebb", - "sha256:a97b0d01ae595c997c1d9d8249e2d2da829c2d8a4bdc29bb8f76c11a94915c9a", - "sha256:b7655f809e3420f80ce3bf89737169a9dce73238af594049754a1128132c0da4", - "sha256:c33744d0d1a7322da445c0fe726ea6d4e3ef2dfb0539eadf23dce366f52f546c", - "sha256:c55a9cf5cba80fb88c850915c865b8ed78d5e46e1f2ec1b27692f3eaaf0dca7e", - "sha256:d2f62fb1c914a038921677cfa536d645cb80e3dd07dc4859a3c92d75407b90a5", - "sha256:d8ae6e0df3a608e99ee1acafaafd7db0830106394d54571c1ece57f650124ce9", - "sha256:e355ee9da9c1c03f174efea59292b17a95e0b7b4d7d2a389265f731a9887d5a9", - "sha256:e3e526062c690517b42bba66ffe38aaf8bc99a180a78212e7b22baa86902f690", - "sha256:eb0807323572642ab73fd86fe53d88d843ce617dd1ddf430351ad0759809a0ae", - "sha256:ebff0738be0499d7db74d20dca9f22a7b27deae31e1bf92ea44924fd69eb6251", - "sha256:ed36e854449ff6c2f8ee145f94851fe171298e1e793f44d4f672c4a0d78064e7", - "sha256:ed3d458ded32ff3a58f157b60cc140c88f7ac8c506a1c567b2a9ee8a2fd2ce54", - "sha256:f4a7dca8ccd8023d916b900aa3c626f1bd181bd5b70159479b142f957ff420e4" - ], - "version": "==1.54.0" + "sha256:0212e2f7fdf7592e4b9d365087da30cb4d71e16a6f213120c89b4f8fb35a3ab3", + "sha256:09d4bfd84686cd36fd11fd45a0732c7628308d094b14d28ea74a81db0bce2ed3", + "sha256:1e623e0cf99a0ac114f091b3083a1848dbc64b0b99e181473b5a4a68d4f6f821", + "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea", + "sha256:2296356b5c9605b73ed6a52660b538787094dae13786ba53080595d52df13a98", + "sha256:2a1e601ee31ef30a9e2c601d0867e236ac54c922d32ed9f727b70dd5d82600d5", + "sha256:2be88c081e33f20630ac3343d8ad9f1125f32987968e9c8c75c051c9800896e8", + "sha256:33d40954199bddbb6a78f8f6f2b2082660f381cd2583ec860a6c2fa7c8400c08", + "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c", + "sha256:46a057329938b08e5f0e12ea3d7aed3ecb20a0c34c4a324ef34e00cecdb88a12", + "sha256:4864f99aac207e3e45c5e26c6cbb0ad82917869abc2f156283be86c05286485c", + "sha256:4c44e1a765b31e175c391f22e8fc73b2a2ece0e5e6ff042743d8109b5d2eff9f", + "sha256:4cb283f630624ebb16c834e5ac3d7880831b07cbe76cb08ab7a271eeaeb8943e", + "sha256:5008964885e8d23313c8e5ea0d44433be9bfd7e24482574e8cc43c02c02fc796", + "sha256:50a9f075eeda5097aa9a182bb3877fe1272875e45370368ac0ee16ab9e22d019", + "sha256:51630c92591d6d3fe488a7c706bd30a61594d144bac7dee20c8e1ce78294f474", + "sha256:5cc928cfe6c360c1df636cf7991ab96f059666ac7b40b75a769410cc6217df9c", + "sha256:61f7203e2767800edee7a1e1040aaaf124a35ce0c7fe0883965c6b762defe598", + "sha256:66233ccd2a9371158d96e05d082043d47dadb18cbb294dc5accfdafc2e6b02a7", + "sha256:70fcac7b94f4c904152809a050164650ac81c08e62c27aa9f156ac518029ebbe", + "sha256:714242ad0afa63a2e6dabd522ae22e1d76e07060b5af2ddda5474ba4f14c2c94", + "sha256:782f4f8662a2157c4190d0f99eaaebc602899e84fb1e562a944e5025929e351c", + "sha256:7fc2b4edb938c8faa4b3c3ea90ca0dd89b7565a049e8e4e11b77e60e4ed2cc05", + "sha256:881d058c5ccbea7cc2c92085a11947b572498a27ef37d3eef4887f499054dca8", + "sha256:89dde0ac72a858a44a2feb8e43dc68c0c66f7857a23f806e81e1b7cc7044c9cf", + "sha256:8cdbcbd687e576d48f7886157c95052825ca9948c0ed2afdc0134305067be88b", + "sha256:8d6192c37a30a115f4663592861f50e130caed33efc4eec24d92ec881c92d771", + "sha256:96a41817d2c763b1d0b32675abeb9179aa2371c72aefdf74b2d2b99a1b92417b", + "sha256:9bdbb7624d65dc0ed2ed8e954e79ab1724526f09b1efa88dcd9a1815bf28be5f", + "sha256:9bf88004fe086c786dc56ef8dd6cb49c026833fdd6f42cb853008bce3f907148", + "sha256:a08920fa1a97d4b8ee5db2f31195de4a9def1a91bc003544eb3c9e6b8977960a", + "sha256:a2f5a1f1080ccdc7cbaf1171b2cf384d852496fe81ddedeb882d42b85727f610", + "sha256:b04202453941a63b36876a7172b45366dc0cde10d5fd7855c0f4a4e673c0357a", + "sha256:b38b3de8cff5bc70f8f9c615f51b48eff7313fc9aca354f09f81b73036e7ddfa", + "sha256:b52d00d1793d290c81ad6a27058f5224a7d5f527867e5b580742e1bd211afeee", + "sha256:b74ae837368cfffeb3f6b498688a123e6b960951be4dec0e869de77e7fa0439e", + "sha256:be48496b0e00460717225e7680de57c38be1d8629dc09dadcd1b3389d70d942b", + "sha256:c0e3155fc5335ec7b3b70f15230234e529ca3607b20a562b6c75fb1b1218874c", + "sha256:c2392f5b5d84b71d853918687d806c1aa4308109e5ca158a16e16a6be71041eb", + "sha256:c72956972e4b508dd39fdc7646637a791a9665b478e768ffa5f4fe42123d5de1", + "sha256:dc80c9c6b608bf98066a038e0172013a49cfa9a08d53335aefefda2c64fc68f4", + "sha256:e416c8baf925b5a1aff31f7f5aecc0060b25d50cce3a5a7255dc5cf2f1d4e5eb", + "sha256:f8da84bbc61a4e92af54dc96344f328e5822d574f767e9b08e1602bb5ddc254a", + "sha256:f900ed4ad7a0f1f05d35f955e0943944d5a75f607a836958c6b8ab2a81730ef2", + "sha256:fd6c6c29717724acf9fc1847c4515d57e4dc12762452457b9cb37461f30a81bb" + ], + "version": "==1.54.2" }, "grpcio-status": { "hashes": [ @@ -784,11 +784,11 @@ }, "setuptools": { "hashes": [ - "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", - "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" + "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", + "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" ], "markers": "python_version >= '3.7'", - "version": "==67.7.2" + "version": "==67.8.0" }, "shapely": { "hashes": [ @@ -1019,6 +1019,63 @@ "markers": "python_full_version >= '3.6.1'", "version": "==3.3.1" }, + "coverage": { + "hashes": [ + "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3", + "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a", + "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813", + "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0", + "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a", + "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd", + "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139", + "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b", + "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252", + "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790", + "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045", + "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce", + "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200", + "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718", + "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b", + "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f", + "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5", + "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade", + "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5", + "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a", + "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8", + "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33", + "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e", + "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c", + "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3", + "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969", + "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068", + "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2", + "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771", + "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed", + "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212", + "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614", + "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88", + "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3", + "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c", + "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84", + "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11", + "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1", + "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1", + "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e", + "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1", + "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd", + "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47", + "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a", + "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c", + "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31", + "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5", + "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6", + "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303", + "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5", + "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47" + ], + "index": "pypi", + "version": "==7.2.5" + }, "distlib": { "hashes": [ "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46", @@ -1068,11 +1125,11 @@ }, "nodeenv": { "hashes": [ - "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e", - "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b" + "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2", + "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==1.7.0" + "version": "==1.8.0" }, "packaging": { "hashes": [ @@ -1084,11 +1141,11 @@ }, "platformdirs": { "hashes": [ - "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4", - "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335" + "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f", + "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5" ], "markers": "python_version >= '3.7'", - "version": "==3.5.0" + "version": "==3.5.1" }, "pluggy": { "hashes": [ @@ -1151,11 +1208,11 @@ }, "setuptools": { "hashes": [ - "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", - "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" + "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", + "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" ], "markers": "python_version >= '3.7'", - "version": "==67.7.2" + "version": "==67.8.0" }, "tomli": { "hashes": [ diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index a5e2cbb7..693a3bd8 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -20,6 +20,7 @@ dependencies = [ tests = [ "google-cloud-bigquery == 2.30.0", "pytest >= 7.3.1,<8.0.0", + "coverage = ==7.2.5" ] [build-system] From 86b47b23e361fb90d6e44239335fbfc9c455433f Mon Sep 17 00:00:00 2001 From: ariadnafer Date: Mon, 22 May 2023 13:30:25 +0200 Subject: [PATCH 027/238] docs: add comments in tests --- .../tests/test_extract_bq_to_dataset.py | 6 +++--- .../vertex-components/tests/test_custom_training_job.py | 9 ++++++++- .../tests/test_import_model_evaluation.py | 9 +++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/components/bigquery-components/tests/test_extract_bq_to_dataset.py b/components/bigquery-components/tests/test_extract_bq_to_dataset.py index 8d83a62b..db00da21 100644 --- a/components/bigquery-components/tests/test_extract_bq_to_dataset.py +++ b/components/bigquery-components/tests/test_extract_bq_to_dataset.py @@ -37,7 +37,7 @@ def test_extract_bq_to_dataset(tmpdir): skip_if_exists=False, ) - # Check that Client.extract_table was called correctly + # Check that client.extract_table was called correctly mock_client.return_value.extract_table.assert_called_once_with( mock_table.return_value, "gs://mock_bucket", job_config="mock-job-config" ) @@ -50,13 +50,13 @@ def test_extract_bq_to_dataset_skip_existing(tmpdir): "pathlib.Path.exists" ) as mock_path_exists: - # # Mock the Dataset path + # Mock the Dataset path mock_path = tmpdir # Mock that the destination already exists mock_path_exists.return_value = True - # Call the function with skip_if_exists set to True + # Call the function extract_bq_to_dataset( bq_client_project_id="my-project-id", source_project_id="source-project-id", diff --git a/components/vertex-components/tests/test_custom_training_job.py b/components/vertex-components/tests/test_custom_training_job.py index 2658f318..cb075c16 100644 --- a/components/vertex-components/tests/test_custom_training_job.py +++ b/components/vertex-components/tests/test_custom_training_job.py @@ -16,8 +16,10 @@ def test_custom_train_job(tmpdir): "builtins.open", mock.mock_open(read_data="{}") ) as mock_open: + # Mock that the training script exists mock_exists.return_value = True + # Mock Artifacts mock_train_data = Dataset(uri=tmpdir) mock_valid_data = Dataset(uri=tmpdir) mock_test_data = Dataset(uri=tmpdir) @@ -25,6 +27,7 @@ def test_custom_train_job(tmpdir): mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) mock_metrics = Metrics(uri=tmpdir) + # Call function custom_train_job( train_script_uri="gs://my-bucket/train_script.py", train_data=mock_train_data, @@ -41,6 +44,7 @@ def test_custom_train_job(tmpdir): job_name="my-job", ) + # Assert custom training job is called mock_job.assert_called_once_with( project="my-project-id", location="europe-west4", @@ -52,6 +56,7 @@ def test_custom_train_job(tmpdir): model_serving_container_image_uri="gcr.io/my-project/my-serving-image:latest", # noqa: E501 ) + # Assert metrics loading mock_open.assert_called_once_with(tmpdir, "r") @@ -62,7 +67,8 @@ def test_custom_train_script_not_found(tmpdir): "builtins.open", mock.mock_open(read_data="{}") ) as mock_open: - mock_exists.return_value = False # Simulate script path not found + # Mock that the training script is not found + mock_exists.return_value = False mock_train_data = Dataset(uri=tmpdir) mock_valid_data = Dataset(uri=tmpdir) @@ -87,5 +93,6 @@ def test_custom_train_script_not_found(tmpdir): job_name="my-job", ) + # Assert the custom training job is not executed mock_job.assert_not_called() mock_open.assert_not_called() diff --git a/components/vertex-components/tests/test_import_model_evaluation.py b/components/vertex-components/tests/test_import_model_evaluation.py index 494c78a2..fe1f85eb 100644 --- a/components/vertex-components/tests/test_import_model_evaluation.py +++ b/components/vertex-components/tests/test_import_model_evaluation.py @@ -19,11 +19,15 @@ def test_import_model_evaluation(tmpdir): "google.protobuf.json_format.ParseDict" ) as mock_parse_dict: + # Mock Artifacts mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) mock_metrics = Metrics(uri=tmpdir) mock_dataset = Dataset(uri=tmpdir) + # Create an instance of the mocked ModelServiceClient. mock_service_client_instance = mock_service_client.return_value + # When import_model_evaluation is called during the test, + # it will return a new ModelEvaluation with the specified name. mock_service_client_instance.import_model_evaluation.return_value = ( ModelEvaluation(name="model_evaluation_name") ) @@ -31,6 +35,7 @@ def test_import_model_evaluation(tmpdir): # Set the return value for ParseDict to be a mock ModelEvaluation mock_parse_dict.return_value = mock.MagicMock(spec=ModelEvaluation) + # Call the function model_evaluation_name = import_model_evaluation( model=mock_model, metrics=mock_metrics, @@ -40,6 +45,8 @@ def test_import_model_evaluation(tmpdir): evaluation_name="Imported evaluation", ) + # Assert that the import_model_evaluation method of + # the mocked ModelServiceClient was called mock_service_client_instance.import_model_evaluation.assert_called_once_with( parent=mock_model.metadata["resourceName"], model_evaluation=mock_parse_dict.return_value, @@ -48,4 +55,6 @@ def test_import_model_evaluation(tmpdir): # Check that open was called with the correct path mock_open.assert_called_once_with(mock_metrics.uri) + # Assert that the return value of the import_model_evaluation + # function is as expected. assert model_evaluation_name[0] == "model_evaluation_name" From c960f134ea499e728fa63f6c73b946feda4ee2dc Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 09:13:57 +0100 Subject: [PATCH 028/238] refactor(pyproject.toml): change description --- components/vertex-components/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/vertex-components/pyproject.toml b/components/vertex-components/pyproject.toml index 6ba44118..e6dc1afd 100644 --- a/components/vertex-components/pyproject.toml +++ b/components/vertex-components/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = [ {name = "Example User", email = "user@example.com"}, ] -description = "Vertex components for training and prediction using the Chicago Taxi Dataset" +description = "KFP components for interacting with Vertex AI" readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", From 644660e79ce7acfad2ec945aca56dfd22fd12875 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 09:28:34 +0100 Subject: [PATCH 029/238] refactor(pyproject.toml): change description --- pipelines/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index b1784b94..6ef6e2c2 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = [ {name = "Example User", email = "user@example.com"}, ] -description = "Turbo Template developed at Datatonic" +description = "Turbo Template" readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", From 6d9c43fce26fb6c9d4bf0645d526ee7a4bd6aa22 Mon Sep 17 00:00:00 2001 From: ariadnafer Date: Tue, 23 May 2023 11:16:11 +0200 Subject: [PATCH 030/238] refactor: change patch formatting in all tests --- .../tests/test_extract_bq_to_dataset.py | 119 +++++++-------- .../tests/test_custom_training_job.py | 137 +++++++++--------- .../tests/test_import_model_evaluation.py | 97 ++++++------- .../tests/test_lookup_model.py | 126 +++++++--------- .../tests/test_model_batch_predict.py | 59 ++++---- .../tests/test_update_best_model.py | 43 +++--- 6 files changed, 275 insertions(+), 306 deletions(-) diff --git a/components/bigquery-components/tests/test_extract_bq_to_dataset.py b/components/bigquery-components/tests/test_extract_bq_to_dataset.py index db00da21..be3084e6 100644 --- a/components/bigquery-components/tests/test_extract_bq_to_dataset.py +++ b/components/bigquery-components/tests/test_extract_bq_to_dataset.py @@ -7,67 +7,58 @@ extract_bq_to_dataset = bigquery_components.extract_bq_to_dataset.python_func -def test_extract_bq_to_dataset(tmpdir): - with mock.patch("google.cloud.bigquery.client.Client") as mock_client, mock.patch( - "google.cloud.bigquery.job.ExtractJobConfig" - ) as mock_job_config, mock.patch("google.cloud.bigquery.table.Table") as mock_table: - - # Mock the Dataset path - mock_path = tmpdir - - # Set up the mock Client - mock_client.extract_table.return_value = "my-job" - - # Set up the mock Table - mock_table.return_value.table_ref = "my-table" - - # Set up the mock ExtractJob - mock_job_config.return_value = "mock-job-config" - - # Call the function - extract_bq_to_dataset( - bq_client_project_id="my-project-id", - source_project_id="source-project-id", - dataset_id="dataset-id", - table_name="table-name", - dataset=Dataset(uri=mock_path), - destination_gcs_uri="gs://mock_bucket", - dataset_location="EU", - extract_job_config=None, - skip_if_exists=False, - ) - - # Check that client.extract_table was called correctly - mock_client.return_value.extract_table.assert_called_once_with( - mock_table.return_value, "gs://mock_bucket", job_config="mock-job-config" - ) - - -def test_extract_bq_to_dataset_skip_existing(tmpdir): - with mock.patch("google.cloud.bigquery.client.Client") as mock_client, mock.patch( - "google.cloud.bigquery.table.Table" - ), mock.patch("google.cloud.bigquery.job.ExtractJobConfig"), mock.patch( - "pathlib.Path.exists" - ) as mock_path_exists: - - # Mock the Dataset path - mock_path = tmpdir - - # Mock that the destination already exists - mock_path_exists.return_value = True - - # Call the function - extract_bq_to_dataset( - bq_client_project_id="my-project-id", - source_project_id="source-project-id", - dataset_id="dataset-id", - table_name="table-name", - dataset=Dataset(uri=mock_path), - destination_gcs_uri="gs://mock_bucket", - dataset_location="EU", - extract_job_config=None, - skip_if_exists=True, - ) - - # Ensure that Client.extract_table was not called - assert not mock_client.return_value.extract_table.called +@mock.patch("google.cloud.bigquery.client.Client") +@mock.patch("google.cloud.bigquery.table.Table") +@mock.patch("google.cloud.bigquery.job.ExtractJobConfig") +def test_extract_bq_to_dataset(mock_job_config, mock_table, mock_client, tmpdir): + """ + Checks that the extract_bq_to_dataset is called correctly + """ + mock_path = tmpdir + mock_client.extract_table.return_value = "my-job" + mock_table.return_value.table_ref = "my-table" + mock_job_config.return_value = "mock-job-config" + + extract_bq_to_dataset( + bq_client_project_id="my-project-id", + source_project_id="source-project-id", + dataset_id="dataset-id", + table_name="table-name", + dataset=Dataset(uri=mock_path), + destination_gcs_uri="gs://mock_bucket", + dataset_location="EU", + extract_job_config=None, + skip_if_exists=False, + ) + + mock_client.return_value.extract_table.assert_called_once_with( + mock_table.return_value, "gs://mock_bucket", job_config="mock-job-config" + ) + + +@mock.patch("google.cloud.bigquery.client.Client") +@mock.patch("google.cloud.bigquery.table.Table") +@mock.patch("google.cloud.bigquery.job.ExtractJobConfig") +@mock.patch("pathlib.Path.exists") +def test_extract_bq_to_dataset_skip_existing( + mock_path_exists, mock_job_config, mock_table, mock_client, tmpdir +): + """ + Checks that when the dataset exists the method is not called + """ + mock_path = tmpdir + mock_path_exists.return_value = True + + extract_bq_to_dataset( + bq_client_project_id="my-project-id", + source_project_id="source-project-id", + dataset_id="dataset-id", + table_name="table-name", + dataset=Dataset(uri=mock_path), + destination_gcs_uri="gs://mock_bucket", + dataset_location="EU", + extract_job_config=None, + skip_if_exists=True, + ) + + assert not mock_client.return_value.extract_table.called diff --git a/components/vertex-components/tests/test_custom_training_job.py b/components/vertex-components/tests/test_custom_training_job.py index cb075c16..46ec46ca 100644 --- a/components/vertex-components/tests/test_custom_training_job.py +++ b/components/vertex-components/tests/test_custom_training_job.py @@ -9,74 +9,69 @@ custom_train_job = vertex_components.custom_train_job.python_func -def test_custom_train_job(tmpdir): - with mock.patch( - "google.cloud.aiplatform.CustomTrainingJob" - ) as mock_job, mock.patch("os.path.exists") as mock_exists, mock.patch( - "builtins.open", mock.mock_open(read_data="{}") - ) as mock_open: - - # Mock that the training script exists - mock_exists.return_value = True - - # Mock Artifacts - mock_train_data = Dataset(uri=tmpdir) - mock_valid_data = Dataset(uri=tmpdir) - mock_test_data = Dataset(uri=tmpdir) - - mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) - mock_metrics = Metrics(uri=tmpdir) - - # Call function - custom_train_job( - train_script_uri="gs://my-bucket/train_script.py", - train_data=mock_train_data, - valid_data=mock_valid_data, - test_data=mock_test_data, - project_id="my-project-id", - project_location="europe-west4", - model_display_name="my-model", - train_container_uri="gcr.io/my-project/my-image:latest", - serving_container_uri="gcr.io/my-project/my-serving-image:latest", - model=mock_model, - metrics=mock_metrics, - staging_bucket="gs://my-bucket", - job_name="my-job", - ) - - # Assert custom training job is called - mock_job.assert_called_once_with( - project="my-project-id", - location="europe-west4", - staging_bucket="gs://my-bucket", - display_name="my-job", - script_path="/gcs/my-bucket/train_script.py", - container_uri="gcr.io/my-project/my-image:latest", - requirements=None, - model_serving_container_image_uri="gcr.io/my-project/my-serving-image:latest", # noqa: E501 - ) - - # Assert metrics loading - mock_open.assert_called_once_with(tmpdir, "r") - - -def test_custom_train_script_not_found(tmpdir): - with pytest.raises(ValueError), mock.patch( - "google.cloud.aiplatform.CustomTrainingJob" - ) as mock_job, mock.patch("os.path.exists") as mock_exists, mock.patch( - "builtins.open", mock.mock_open(read_data="{}") - ) as mock_open: - - # Mock that the training script is not found - mock_exists.return_value = False - - mock_train_data = Dataset(uri=tmpdir) - mock_valid_data = Dataset(uri=tmpdir) - mock_test_data = Dataset(uri=tmpdir) - - mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) - mock_metrics = Metrics(uri=tmpdir) - +@mock.patch("google.cloud.aiplatform.CustomTrainingJob") +@mock.patch("os.path.exists") +@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="{}") +def test_custom_train_job(mock_open, mock_exists, mock_job, tmpdir): + """ + Checks that the custom job method is called + """ + mock_exists.return_value = True + + mock_train_data = Dataset(uri=tmpdir) + mock_valid_data = Dataset(uri=tmpdir) + mock_test_data = Dataset(uri=tmpdir) + mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) + mock_metrics = Metrics(uri=tmpdir) + + custom_train_job( + train_script_uri="gs://my-bucket/train_script.py", + train_data=mock_train_data, + valid_data=mock_valid_data, + test_data=mock_test_data, + project_id="my-project-id", + project_location="europe-west4", + model_display_name="my-model", + train_container_uri="gcr.io/my-project/my-image:latest", + serving_container_uri="gcr.io/my-project/my-serving-image:latest", + model=mock_model, + metrics=mock_metrics, + staging_bucket="gs://my-bucket", + job_name="my-job", + ) + + mock_job.assert_called_once_with( + project="my-project-id", + location="europe-west4", + staging_bucket="gs://my-bucket", + display_name="my-job", + script_path="/gcs/my-bucket/train_script.py", + container_uri="gcr.io/my-project/my-image:latest", + requirements=None, + model_serving_container_image_uri="gcr.io/my-project/my-serving-image:latest", # noqa: E501 + ) + + # Assert metrics loading + mock_open.assert_called_once_with(tmpdir, "r") + + +@mock.patch("google.cloud.aiplatform.CustomTrainingJob") +@mock.patch("os.path.exists") +@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="{}") +def test_custom_train_script_not_found(mock_open, mock_exists, mock_job, tmpdir): + """ + Checks that when the training script is not found + the method fails + """ + mock_exists.return_value = False + + mock_train_data = Dataset(uri=tmpdir) + mock_valid_data = Dataset(uri=tmpdir) + mock_test_data = Dataset(uri=tmpdir) + mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) + mock_metrics = Metrics(uri=tmpdir) + + with pytest.raises(ValueError): custom_train_job( train_script_uri="gs://my-bucket/train_script.py", train_data=mock_train_data, @@ -93,6 +88,6 @@ def test_custom_train_script_not_found(tmpdir): job_name="my-job", ) - # Assert the custom training job is not executed - mock_job.assert_not_called() - mock_open.assert_not_called() + # Assert the custom training job is not executed + mock_job.assert_not_called() + mock_open.assert_not_called() diff --git a/components/vertex-components/tests/test_import_model_evaluation.py b/components/vertex-components/tests/test_import_model_evaluation.py index fe1f85eb..56cdf8bd 100644 --- a/components/vertex-components/tests/test_import_model_evaluation.py +++ b/components/vertex-components/tests/test_import_model_evaluation.py @@ -8,53 +8,50 @@ import_model_evaluation = vertex_components.import_model_evaluation.python_func -def test_import_model_evaluation(tmpdir): - with mock.patch( - "google.cloud.aiplatform_v1.ModelServiceClient" - ) as mock_service_client, mock.patch( - "builtins.open", - mock.mock_open(read_data='{"accuracy": 0.85, "problemType": "classification"}'), - create=True, - ) as mock_open, mock.patch( - "google.protobuf.json_format.ParseDict" - ) as mock_parse_dict: - - # Mock Artifacts - mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) - mock_metrics = Metrics(uri=tmpdir) - mock_dataset = Dataset(uri=tmpdir) - - # Create an instance of the mocked ModelServiceClient. - mock_service_client_instance = mock_service_client.return_value - # When import_model_evaluation is called during the test, - # it will return a new ModelEvaluation with the specified name. - mock_service_client_instance.import_model_evaluation.return_value = ( - ModelEvaluation(name="model_evaluation_name") - ) - - # Set the return value for ParseDict to be a mock ModelEvaluation - mock_parse_dict.return_value = mock.MagicMock(spec=ModelEvaluation) - - # Call the function - model_evaluation_name = import_model_evaluation( - model=mock_model, - metrics=mock_metrics, - test_dataset=mock_dataset, - pipeline_job_id="1234", - project_location="my-location", - evaluation_name="Imported evaluation", - ) - - # Assert that the import_model_evaluation method of - # the mocked ModelServiceClient was called - mock_service_client_instance.import_model_evaluation.assert_called_once_with( - parent=mock_model.metadata["resourceName"], - model_evaluation=mock_parse_dict.return_value, - ) - - # Check that open was called with the correct path - mock_open.assert_called_once_with(mock_metrics.uri) - - # Assert that the return value of the import_model_evaluation - # function is as expected. - assert model_evaluation_name[0] == "model_evaluation_name" +@mock.patch("google.cloud.aiplatform_v1.ModelServiceClient") +@mock.patch( + "builtins.open", + new_callable=mock.mock_open, + read_data='{"accuracy": 0.85, "problemType": "classification"}', +) +@mock.patch("google.protobuf.json_format.ParseDict") +def test_import_model_evaluation( + mock_parse_dict, mock_open, mock_service_client, tmpdir +): + """ + Checks that when the model evaluation is running and it is writing the metrics + """ + mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) + mock_metrics = Metrics(uri=tmpdir) + mock_dataset = Dataset(uri=tmpdir) + + # Create an instance of the mocked ModelServiceClient. + service_client_instance = mock.MagicMock() + mock_service_client.return_value = service_client_instance + # When import_model_evaluation is called during the test, + # it will return a new ModelEvaluation with the specified name. + service_client_instance.import_model_evaluation.return_value = ModelEvaluation( + name="model_evaluation_name" + ) + + # Set the return value for ParseDict to be a mock ModelEvaluation + mock_parse_dict.return_value = mock.MagicMock(spec=ModelEvaluation) + + model_evaluation_name = import_model_evaluation( + model=mock_model, + metrics=mock_metrics, + test_dataset=mock_dataset, + pipeline_job_id="1234", + project_location="my-location", + evaluation_name="Imported evaluation", + ) + + service_client_instance.import_model_evaluation.assert_called_once_with( + parent=mock_model.metadata["resourceName"], + model_evaluation=mock_parse_dict.return_value, + ) + + # Check that open was called with the correct path + mock_open.assert_called_once_with(mock_metrics.uri) + + assert model_evaluation_name[0] == "model_evaluation_name" diff --git a/components/vertex-components/tests/test_lookup_model.py b/components/vertex-components/tests/test_lookup_model.py index 1ba31f81..6950dd1b 100644 --- a/components/vertex-components/tests/test_lookup_model.py +++ b/components/vertex-components/tests/test_lookup_model.py @@ -22,93 +22,75 @@ lookup_model = vertex_components.lookup_model.python_func -def test_lookup_model(tmpdir): +@mock.patch("google.cloud.aiplatform.Model") +def test_lookup_model(mock_model, tmpdir): """ Assert lookup_model produces expected resource name, and that list method is called with the correct arguemnts - - Args: - tmpdir: built-in pytest tmpdir fixture - - Returns: - None """ - with mock.patch("google.cloud.aiplatform.Model") as mock_model: - - # Mock attribute and method - - mock_path = tmpdir - mock_model.resource_name = "my-model-resource-name" - mock_model.uri = mock_path - mock_model.list.return_value = [mock_model] - - # Invoke the model look up - found_model_resource_name, _ = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - model=Model(uri=mock_path), - ) - - assert found_model_resource_name == "my-model-resource-name" - - # Check the list method was called once with the correct arguments - mock_model.list.assert_called_once_with( - filter='display_name="my-model"', - order_by="create_time desc", - location="europe-west4", - project="my-project-id", - ) - -def test_lookup_model_when_no_models(tmpdir): + # Mock attribute and method + mock_path = tmpdir + mock_model.resource_name = "my-model-resource-name" + mock_model.uri = mock_path + mock_model.list.return_value = [mock_model] + + # Invoke the model look up + found_model_resource_name, _ = lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=False, + model=Model(uri=mock_path), + ) + + assert found_model_resource_name == "my-model-resource-name" + + # Check the list method was called once with the correct arguments + mock_model.list.assert_called_once_with( + filter='display_name="my-model"', + order_by="create_time desc", + location="europe-west4", + project="my-project-id", + ) + + +@mock.patch("google.cloud.aiplatform.Model") +def test_lookup_model_when_no_models(mock_model, tmpdir): """ Checks that when there are no models and fail_on_model_found = False, lookup_model returns an empty string. - - Args: - tmpdir: built-in pytest tmpdir fixture - - Returns: - None """ - with mock.patch("google.cloud.aiplatform.Model") as mock_model: - mock_model.list.return_value = [] - exported_model_resource_name, _ = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - model=Model(uri=str(tmpdir)), - ) + mock_model.list.return_value = [] + exported_model_resource_name, _ = lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=False, + model=Model(uri=str(tmpdir)), + ) + print(exported_model_resource_name) assert exported_model_resource_name == "" -def test_lookup_model_when_no_models_fail(tmpdir): +@mock.patch("google.cloud.aiplatform.Model") +def test_lookup_model_when_no_models_fail(mock_model, tmpdir): """ Checks that when there are no models and fail_on_model_found = True, lookup_model raises a RuntimeError. - - Args: - tmpdir: built-in pytest tmpdir fixture - - Returns: - None """ - with mock.patch("google.cloud.aiplatform.Model") as mock_model: - mock_model.list.return_value = [] + mock_model.list.return_value = [] - # Verify that a ValueError is raised - with pytest.raises(RuntimeError): - lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=True, - model=Model(uri=str(tmpdir)), - ) + # Verify that a ValueError is raised + with pytest.raises(RuntimeError): + lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=True, + model=Model(uri=str(tmpdir)), + ) diff --git a/components/vertex-components/tests/test_model_batch_predict.py b/components/vertex-components/tests/test_model_batch_predict.py index 10d9e808..76bca67d 100644 --- a/components/vertex-components/tests/test_model_batch_predict.py +++ b/components/vertex-components/tests/test_model_batch_predict.py @@ -13,7 +13,7 @@ # limitations under the License. import json import pytest -from unittest.mock import Mock, patch +from unittest import mock from kfp.v2.dsl import Model from google.cloud.aiplatform_v1beta1.types.job_state import JobState @@ -30,7 +30,19 @@ "targetField": "col", } +mock_job1 = mock.Mock() +mock_job1.name = "mock-batch-job" +mock_job1.state = JobState.JOB_STATE_SUCCEEDED + +@mock.patch( + "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.create_batch_prediction_job", # noqa : E501 + return_value=mock_job1, +) +@mock.patch( + "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.get_batch_prediction_job", # noqa : E501 + return_value=mock_job1, +) @pytest.mark.parametrize( ( "source_format,destination_format,source_uri,monitoring_training_dataset," @@ -44,6 +56,8 @@ ], ) def test_model_batch_predict( + create_job, + get_job, tmpdir, source_format, destination_format, @@ -55,37 +69,22 @@ def test_model_batch_predict( """ Asserts model_batch_predict successfully creates requests given different arguments. """ - mock_resource_name = "mock-batch-job" - - mock_job1 = Mock() - mock_job1.name = mock_resource_name - mock_job1.state = JobState.JOB_STATE_SUCCEEDED - mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) - with patch( - "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.create_batch_prediction_job", # noqa: E501 - return_value=mock_job1, - ) as create_job, patch( - "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.get_batch_prediction_job", # noqa: E501 - return_value=mock_job1, - ) as get_job: - (gcp_resources,) = model_batch_predict( - model=mock_model, - job_display_name="", - project_location="", - project_id="", - source_uri=source_uri, - destination_uri=destination_format, - source_format=source_format, - destination_format=destination_format, - monitoring_training_dataset=monitoring_training_dataset, - monitoring_alert_email_addresses=monitoring_alert_email_addresses, - monitoring_skew_config=monitoring_skew_config, - ) + (gcp_resources,) = model_batch_predict( + model=mock_model, + job_display_name="", + project_location="", + project_id="", + source_uri=source_uri, + destination_uri=destination_format, + source_format=source_format, + destination_format=destination_format, + monitoring_training_dataset=monitoring_training_dataset, + monitoring_alert_email_addresses=monitoring_alert_email_addresses, + monitoring_skew_config=monitoring_skew_config, + ) create_job.assert_called_once() get_job.assert_called_once() - assert ( - json.loads(gcp_resources)["resources"][0]["resourceUri"] == mock_resource_name - ) + assert json.loads(gcp_resources)["resources"][0]["resourceUri"] == mock_job1.name diff --git a/components/vertex-components/tests/test_update_best_model.py b/components/vertex-components/tests/test_update_best_model.py index bce9a9df..4ef6ba21 100644 --- a/components/vertex-components/tests/test_update_best_model.py +++ b/components/vertex-components/tests/test_update_best_model.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from unittest.mock import patch +from unittest import mock from kfp.v2.dsl import Model @@ -21,26 +21,31 @@ update_best_model = vertex_components.update_best_model.python_func -def test_model_batch_predict(tmpdir): +@mock.patch("google.cloud.aiplatform.Model") +@mock.patch("google.cloud.aiplatform.model_evaluation.ModelEvaluation") +@mock.patch("google.cloud.aiplatform.models.ModelRegistry") +@mock.patch("google.protobuf.json_format.MessageToDict") +def test_model_batch_predict( + mock_message_to_dict, + mock_model_registry, + mock_model_evaluation, + mock_model_class, + tmpdir, +): """ Asserts model_batch_predict successfully creates requests given different arguments. """ mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) mock_message = {"metrics": {"rmse": 0.01}} - - with patch("google.cloud.aiplatform.Model",), patch( - "google.cloud.aiplatform.model_evaluation.ModelEvaluation", - ), patch("google.cloud.aiplatform.models.ModelRegistry",), patch( - "google.protobuf.json_format.MessageToDict", return_value=mock_message - ): - - (challenger_wins,) = update_best_model( - challenger=mock_model, - challenger_evaluation="", - parent_model="", - project_id="", - project_location="", - eval_metric="rmse", - eval_lower_is_better=True, - ) - assert not challenger_wins + mock_message_to_dict.return_value = mock_message + + (challenger_wins,) = update_best_model( + challenger=mock_model, + challenger_evaluation="", + parent_model="", + project_id="", + project_location="", + eval_metric="rmse", + eval_lower_is_better=True, + ) + assert not challenger_wins From be883a52798031a14540142b4d4e726b4321c8a0 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 12:09:16 +0100 Subject: [PATCH 031/238] build(Makefile): change all pipenv commands to poetry commands --- Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 05a3c1e8..674e9f6a 100644 --- a/Makefile +++ b/Makefile @@ -20,24 +20,24 @@ help: ## Display this help screen pre-commit: ## Runs the pre-commit checks over entire repo @cd pipelines && \ - pipenv run pre-commit run --all-files + poetry run pre-commit run --all-files setup: ## Set up local environment for Python development on pipelines - @pip install pipenv && \ + @pip install poetry && \ cd pipelines && \ - pipenv install --dev + poetry install --dev test-trigger: ## Runs unit tests for the pipeline trigger code @cd pipelines && \ - pipenv run python -m pytest tests/trigger + poetry run python -m pytest tests/trigger compile-pipeline: ## Compile the pipeline to training.json or prediction.json. Must specify pipeline= @cd pipelines/src && \ - pipenv run python -m pipelines.${PIPELINE_TEMPLATE}.${pipeline}.pipeline + poetry run python -m pipelines.${PIPELINE_TEMPLATE}.${pipeline}.pipeline setup-components: ## Run unit tests for a component group @cd "components/${GROUP}" && \ - pipenv install --dev + poetry install --dev setup-all-components: ## Run unit tests for all pipeline components @set -e && \ @@ -48,7 +48,7 @@ setup-all-components: ## Run unit tests for all pipeline components test-components: ## Run unit tests for a component group @cd "components/${GROUP}" && \ - pipenv run pytest + poetry run pytest test-all-components: ## Run unit tests for all pipeline components @set -e && \ @@ -69,7 +69,7 @@ run: ## Compile pipeline, copy assets to GCS, and run pipeline in sandbox enviro @ $(MAKE) compile-pipeline && \ $(MAKE) sync-assets && \ cd pipelines/src && \ - pipenv run python -m pipelines.trigger --template_path=./$(pipeline).json --enable_caching=$(enable_pipeline_caching) + poetry run python -m pipelines.trigger --template_path=./$(pipeline).json --enable_caching=$(enable_pipeline_caching) sync_assets ?= true e2e-tests: ## (Optionally) copy assets to GCS, and perform end-to-end (E2E) pipeline tests. Must specify pipeline=. Optionally specify enable_pipeline_caching= (defaults to default Vertex caching behaviour). Optionally specify sync_assets= (defaults to true) @@ -79,7 +79,7 @@ e2e-tests: ## (Optionally) copy assets to GCS, and perform end-to-end (E2E) pipe echo "Skipping syncing assets to GCS"; \ fi && \ cd pipelines && \ - pipenv run pytest --log-cli-level=INFO tests/${PIPELINE_TEMPLATE}/$(pipeline) --enable_caching=$(enable_pipeline_caching) + poetry run pytest --log-cli-level=INFO tests/${PIPELINE_TEMPLATE}/$(pipeline) --enable_caching=$(enable_pipeline_caching) env ?= dev deploy-infra: ## Deploy the Terraform infrastructure to your project. Requires VERTEX_PROJECT_ID and VERTEX_LOCATION env variables to be set in env.sh. Optionally specify env= (default = dev) From 170a746bf95634ff7eecc57e68c9970c52340696 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 13:47:27 +0100 Subject: [PATCH 032/238] style(pyproject.toml): correct authors description to string type --- pipelines/pyproject.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index 6ef6e2c2..0c5c74d6 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -1,9 +1,7 @@ [tool.poetry] name = "pipelines" version = "0.1.0" -authors = [ - {name = "Example User", email = "user@example.com"}, -] +authors = ["Example User "] description = "Turbo Template" readme = "README.md" classifiers = [ @@ -43,4 +41,3 @@ pythonpath = [ ] testpaths = "tests" junit_family = "xunit2" - From 5c3274f75d605b42b735184fcbc8cec440ffe9c4 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 13:55:51 +0100 Subject: [PATCH 033/238] build(Makefile): fix makefile setup instructions --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 674e9f6a..76c3f771 100644 --- a/Makefile +++ b/Makefile @@ -19,13 +19,14 @@ help: ## Display this help screen @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' pre-commit: ## Runs the pre-commit checks over entire repo + @pip install pre-commit \ @cd pipelines && \ poetry run pre-commit run --all-files setup: ## Set up local environment for Python development on pipelines @pip install poetry && \ cd pipelines && \ - poetry install --dev + poetry install --with dev test-trigger: ## Runs unit tests for the pipeline trigger code @cd pipelines && \ From 9ede2582114c5716d74aadd5cfd3fb8b3416ebb1 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 14:45:36 +0100 Subject: [PATCH 034/238] build(Makefile): fix pr-commit issue --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 76c3f771..910dc0b8 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ help: ## Display this help screen pre-commit: ## Runs the pre-commit checks over entire repo @pip install pre-commit \ - @cd pipelines && \ + cd pipelines && \ poetry run pre-commit run --all-files setup: ## Set up local environment for Python development on pipelines From a5d5d8508b272e38bb6e961838faf4d8ad85e0dd Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 23 May 2023 15:06:13 +0100 Subject: [PATCH 035/238] feat: changes to release ci --- cloudbuild/release.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index 4a48a1f7..bf771d5b 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -21,7 +21,6 @@ steps: - -c - | make setup && \ - make compile-all-components && \ make compile-pipeline pipeline=training && \ make compile-pipeline pipeline=prediction env: @@ -36,8 +35,8 @@ steps: - -c - | mkdir -p ${TAG_NAME}/assets && \ - cp pipelines/training.json ${TAG_NAME}/training.json && \ - cp pipelines/prediction.json ${TAG_NAME}/prediction.json && \ + cp pipelines/src/training.json ${TAG_NAME}/training.json && \ + cp pipelines/src/prediction.json ${TAG_NAME}/prediction.json && \ cp -r pipelines/assets ${TAG_NAME}/assets && \ for dest in ${_PIPELINE_PUBLISH_GCS_PATHS} ; do \ gsutil cp -r ${TAG_NAME} $$dest ; \ From 7cf5b167474fe124b7a637b405d8518ef4c79bf0 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 15:20:33 +0100 Subject: [PATCH 036/238] build(Makefile): fix pre-commit checks --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 910dc0b8..ed496716 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ help: ## Display this help screen @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' pre-commit: ## Runs the pre-commit checks over entire repo - @pip install pre-commit \ + @pip install pre-commit && \ cd pipelines && \ poetry run pre-commit run --all-files From 68c648486141b64f8f388e99c15e18b125054b04 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 15:23:45 +0100 Subject: [PATCH 037/238] build(Makefile): fix pre-commit issue --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index ed496716..f342554d 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,6 @@ help: ## Display this help screen @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' pre-commit: ## Runs the pre-commit checks over entire repo - @pip install pre-commit && \ cd pipelines && \ poetry run pre-commit run --all-files From d40d879777ca9989a704c00f082ffdaeee0ebf6e Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 23 May 2023 15:36:24 +0100 Subject: [PATCH 038/238] feat: fix nested assets folder in release --- cloudbuild/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index bf771d5b..0f262a52 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -37,7 +37,7 @@ steps: mkdir -p ${TAG_NAME}/assets && \ cp pipelines/src/training.json ${TAG_NAME}/training.json && \ cp pipelines/src/prediction.json ${TAG_NAME}/prediction.json && \ - cp -r pipelines/assets ${TAG_NAME}/assets && \ + cp -r pipelines/assets/* ${TAG_NAME}/assets/ && \ for dest in ${_PIPELINE_PUBLISH_GCS_PATHS} ; do \ gsutil cp -r ${TAG_NAME} $$dest ; \ done From b9faf753ad4bfea40d4c82959621a109981a15af Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 15:37:12 +0100 Subject: [PATCH 039/238] build(.pre-commit-config.yaml): update from python 3.7 to 3.9 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3bd0bc1e..66dc32d3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ --- default_language_version: - python: python3.7 + python: python3.9 repos: - repo: https://github.com/gruntwork-io/pre-commit From 610bbd341cf93ebdb989bed8967cec8963944e08 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 16:26:10 +0100 Subject: [PATCH 040/238] refactor(.pre-commit-config.yaml): fix pre-commit hooks --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 66dc32d3..a3212993 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,10 +32,10 @@ repos: - id: no-commit-to-branch args: [--branch, main] - - repo: https://github.com/jumanjihouse/pre-commit-hooks - rev: 1.11.0 - hooks: - - id: git-dirty + # - repo: https://github.com/jumanjihouse/pre-commit-hooks + # rev: 1.11.0 + # hooks: + # - id: git-dirty - repo: https://github.com/adrienverge/yamllint.git rev: v1.26.3 From 5bdad5a3e5627c9fd8d354da4d33341ea0c31d19 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 16:29:28 +0100 Subject: [PATCH 041/238] refactor(.pre-commit-config.yaml): fix pre-commit --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a3212993..66dc32d3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,10 +32,10 @@ repos: - id: no-commit-to-branch args: [--branch, main] - # - repo: https://github.com/jumanjihouse/pre-commit-hooks - # rev: 1.11.0 - # hooks: - # - id: git-dirty + - repo: https://github.com/jumanjihouse/pre-commit-hooks + rev: 1.11.0 + hooks: + - id: git-dirty - repo: https://github.com/adrienverge/yamllint.git rev: v1.26.3 From ff14b8ce53596f5a8c9f0fe73f4a50c65aacb486 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 23 May 2023 16:33:11 +0100 Subject: [PATCH 042/238] feat: delete assets --- .../tensorflow/prediction/assets/.gitkeep | 0 .../training/assets/train_tf_model.py | 299 ------------------ .../xgboost/prediction/assets/.gitkeep | 0 .../xgboost/training/assets/.gitkeep | 0 .../training/assets/train_xgb_model.py | 142 --------- 5 files changed, 441 deletions(-) delete mode 100644 pipelines/src/pipelines/tensorflow/prediction/assets/.gitkeep delete mode 100644 pipelines/src/pipelines/tensorflow/training/assets/train_tf_model.py delete mode 100644 pipelines/src/pipelines/xgboost/prediction/assets/.gitkeep delete mode 100644 pipelines/src/pipelines/xgboost/training/assets/.gitkeep delete mode 100644 pipelines/src/pipelines/xgboost/training/assets/train_xgb_model.py diff --git a/pipelines/src/pipelines/tensorflow/prediction/assets/.gitkeep b/pipelines/src/pipelines/tensorflow/prediction/assets/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/pipelines/src/pipelines/tensorflow/training/assets/train_tf_model.py b/pipelines/src/pipelines/tensorflow/training/assets/train_tf_model.py deleted file mode 100644 index 5d164ae7..00000000 --- a/pipelines/src/pipelines/tensorflow/training/assets/train_tf_model.py +++ /dev/null @@ -1,299 +0,0 @@ -import argparse -import os -import json -import logging -import sys - -import tensorflow as tf -from pathlib import Path -from tensorflow.data import Dataset -from tensorflow.keras import Input, Model, optimizers -from tensorflow.keras.layers import Dense, Normalization, StringLookup, Concatenate - -# used for monitoring during prediction time -TRAINING_DATASET_INFO = "training_dataset.json" -# numeric/categorical features in Chicago trips dataset to be preprocessed -NUM_COLS = ["dayofweek", "hourofday", "trip_distance", "trip_miles", "trip_seconds"] -ORD_COLS = ["company"] -OHE_COLS = ["payment_type"] -DEFAULT_HPARAMS = dict( - batch_size=100, - epochs=1, - loss_fn="MeanSquaredError", - optimizer="Adam", - learning_rate=0.001, - metrics=[ - "RootMeanSquaredError", - "MeanAbsoluteError", - "MeanAbsolutePercentageError", - "MeanSquaredLogarithmicError", - ], - hidden_units=[(10, "relu")], - distribute_strategy="single", - early_stopping_epochs=5, - label="total_fare", -) - -logging.getLogger().setLevel(logging.INFO) - - -def create_dataset(input_data: Path, label_name: str, model_params: dict) -> Dataset: - """Create a TF Dataset from input csv files. - Args: - input_data (Input[Dataset]): Train/Valid data in CSV format - label_name (str): Name of column containing the labels - model_params (dict): model parameters - file_pattern (str): Read data from one or more files. If empty, then - training and validation data is read from single file respectively. - For multiple files, use a pattern e.g. "files-*.csv". - Returns: - dataset (TF Dataset): TF dataset where each element is a (features, labels) - tuple that corresponds to a batch of CSV rows - """ - - # shuffle & shuffle_buffer_size added to rearrange input data - # passed into model training - # num_rows_for_inference is for auto detection of datatypes - # while creating the dataset. - # If a float column has a high proportion of integer values (0/1 etc), - # the method wrongly detects it as a tf.int32 which fails during training time, - # hence the high hardcoded value (default is 100) - - # Apply data sharding: Sharded elements are produced by the dataset - # Each worker will process the whole dataset and discard the portion that is - # not for itself. Note that for this mode to correctly partitions the dataset - # elements, the dataset needs to produce elements in a deterministic order. - data_options = tf.data.Options() - data_options.experimental_distribute.auto_shard_policy = ( - tf.data.experimental.AutoShardPolicy.DATA - ) - - logging.info(f"Creating dataset from CSV file(s) at {input_data}...") - created_dataset = tf.data.experimental.make_csv_dataset( - file_pattern=str(input_data), - batch_size=model_params["batch_size"], - label_name=label_name, - num_epochs=model_params["epochs"], - shuffle=True, - shuffle_buffer_size=1000, - num_rows_for_inference=20000, - ) - return created_dataset.with_options(data_options) - - -def get_distribution_strategy(distribute_strategy: str) -> tf.distribute.Strategy: - """Set distribute strategy based on input string. - Args: - distribute_strategy (str): single, mirror or multi - Returns: - strategy (tf.distribute.Strategy): distribution strategy - """ - logging.info(f"Distribution strategy: {distribute_strategy}") - - # Single machine, single compute device - if distribute_strategy == "single": - if len(tf.config.list_physical_devices("GPU")): - strategy = tf.distribute.OneDeviceStrategy(device="/gpu:0") - else: - strategy = tf.distribute.OneDeviceStrategy(device="/cpu:0") - # Single machine, multiple compute device - elif distribute_strategy == "mirror": - strategy = tf.distribute.MirroredStrategy() - # Multiple machine, multiple compute device - elif distribute_strategy == "multi": - strategy = tf.distribute.MultiWorkerMirroredStrategy() - else: - raise RuntimeError(f"Distribute strategy: {distribute_strategy} not supported") - return strategy - - -def normalization(name: str, dataset: Dataset) -> Normalization: - """Create a Normalization layer for a feature. - Args: - name (str): name of feature to be normalized - dataset (Dataset): dataset to adapt layer - Returns: - normalization layer (Normalization): adapted normalization layer - of shape (?,1) - """ - logging.info(f"Normalizing numerical input '{name}'...") - x = Normalization(axis=None, name=f"normalize_{name}") - x.adapt(dataset.map(lambda y, _: y[name])) - return x - - -def str_lookup(name: str, dataset: Dataset, output_mode: str) -> StringLookup: - """Create a StringLookup layer for a feature. - Args: - name (str): name of feature to be encoded - dataset (Dataset): dataset to adapt layer - output_mode (str): argument for StringLookup layer (e.g. 'one_hot', 'int') - Returns: - StringLookup layer (StringLookup): adapted StringLookup layer of shape (?,X) - """ - logging.info(f"Encoding categorical input '{name}' ({output_mode})...") - x = StringLookup(output_mode=output_mode, name=f"str_lookup_{output_mode}_{name}") - x.adapt(dataset.map(lambda y, _: y[name])) - logging.info(f"Vocabulary: {x.get_vocabulary()}") - return x - - -def build_and_compile_model(dataset: Dataset, model_params: dict) -> Model: - """Build and compile model. - Args: - dataset (Dataset): training dataset - model_params (dict): model parameters - Returns: - model (Model): built and compiled model - """ - - # create inputs (scalars with shape `()`) - num_ins = {name: Input(shape=(), name=name, dtype=tf.float32) for name in NUM_COLS} - ord_ins = {name: Input(shape=(), name=name, dtype=tf.string) for name in ORD_COLS} - cat_ins = {name: Input(shape=(), name=name, dtype=tf.string) for name in OHE_COLS} - - # join all inputs and expand by 1 dimension. NOTE: this is useful when passing - # in scalar inputs to a model in Vertex AI batch predictions or endpoints e.g. - # `{"instances": {"input1": 1.0, "input2": "str"}}` instead of - # `{"instances": {"input1": [1.0], "input2": ["str"]}` - all_ins = {**num_ins, **ord_ins, **cat_ins} - exp_ins = {n: tf.expand_dims(i, axis=-1) for n, i in all_ins.items()} - - # preprocess expanded inputs - num_encoded = [normalization(n, dataset)(exp_ins[n]) for n in NUM_COLS] - ord_encoded = [str_lookup(n, dataset, "int")(exp_ins[n]) for n in ORD_COLS] - ohe_encoded = [str_lookup(n, dataset, "one_hot")(exp_ins[n]) for n in OHE_COLS] - - # ensure ordinal encoded layers is of type float32 (like the other layers) - ord_encoded = [tf.cast(x, tf.float32) for x in ord_encoded] - - # concat encoded inputs and add dense layers including output layer - x = num_encoded + ord_encoded + ohe_encoded - x = Concatenate()(x) - for units, activation in model_params["hidden_units"]: - x = Dense(units, activation=activation)(x) - x = Dense(1, name="output", activation="linear")(x) - - model = Model(inputs=all_ins, outputs=x, name="nn_model") - model.summary() - - logging.info(f"Use optimizer {model_params['optimizer']}") - optimizer = optimizers.get(model_params["optimizer"]) - optimizer.learning_rate = model_params["learning_rate"] - - model.compile( - loss=model_params["loss_fn"], - optimizer=optimizer, - metrics=model_params["metrics"], - ) - - return model - - -def _is_chief(strategy: tf.distribute.Strategy) -> bool: - """Determine whether current worker is the chief (master). See more info: - - https://www.tensorflow.org/tutorials/distribute/multi_worker_with_keras - - https://www.tensorflow.org/api_docs/python/tf/distribute/cluster_resolver/ClusterResolver # noqa: E501 - Args: - strategy (tf.distribute.Strategy): strategy - Returns: - is_chief (bool): True if worker is chief, otherwise False - """ - cr = strategy.cluster_resolver - return (cr is None) or (cr.task_type == "chief" and cr.task_id == 0) - - -def _get_temp_dir(dirpath, task_id): - base_dirpath = "workertemp_" + str(task_id) - temp_dir = os.path.join(dirpath, base_dirpath) - tf.io.gfile.makedirs(temp_dir) - return temp_dir - - -parser = argparse.ArgumentParser() -parser.add_argument("--train_data", type=str, required=True) -parser.add_argument("--valid_data", type=str, required=True) -parser.add_argument("--test_data", type=str, required=True) -parser.add_argument("--model", default=os.getenv("AIP_MODEL_DIR"), type=str, help="") -parser.add_argument("--metrics", type=str, required=True) -parser.add_argument("--hparams", default={}, type=json.loads) -args = parser.parse_args() - -if args.model.startswith("gs://"): - args.model = Path("/gcs/" + args.model[5:]) - -# merge dictionaries by overwriting default_model_params if provided in model_params -hparams = {**DEFAULT_HPARAMS, **args.hparams} -logging.info(f"Using model hyper-parameters: {hparams}") -label = hparams["label"] - -# Set distribute strategy before any TF operations -strategy = get_distribution_strategy(hparams["distribute_strategy"]) - -train_ds = create_dataset(Path(args.train_data), label, hparams) -valid_ds = create_dataset(Path(args.valid_data), label, hparams) -test_ds = create_dataset(Path(args.test_data), label, hparams) - -train_features = list(train_ds.element_spec[0].keys()) -valid_features = list(valid_ds.element_spec[0].keys()) -logging.info(f"Training feature names: {train_features}") -logging.info(f"Validation feature names: {valid_features}") - -if len(train_features) != len(valid_features): - raise RuntimeError(f"No. of training features != # validation features") - -with strategy.scope(): - tf_model = build_and_compile_model(train_ds, hparams) - -logging.info("Use early stopping") -callback = tf.keras.callbacks.EarlyStopping( - monitor="loss", mode="min", patience=hparams["early_stopping_epochs"] -) - -logging.info("Fit model...") -history = tf_model.fit( - train_ds, - batch_size=hparams["batch_size"], - epochs=hparams["epochs"], - validation_data=valid_ds, - callbacks=[callback], -) - -# only persist output files if current worker is chief -if not _is_chief(strategy): - logging.info("not chief node, exiting now") - sys.exit() - -logging.info(f"Save model to: {args.model}") -args.model.mkdir(parents=True) -tf_model.save(str(args.model), save_format="tf") - -logging.info(f"Save metrics to: {args.metrics}") -eval_metrics = dict(zip(tf_model.metrics_names, tf_model.evaluate(test_ds))) - -metrics = { - "problemType": "regression", - "rootMeanSquaredError": eval_metrics["root_mean_squared_error"], - "meanAbsoluteError": eval_metrics["mean_absolute_error"], - "meanAbsolutePercentageError": eval_metrics["mean_absolute_percentage_error"], - "rSquared": None, - "rootMeanSquaredLogError": eval_metrics["mean_squared_logarithmic_error"], -} - -with open(args.metrics, "w") as fp: - json.dump(metrics, fp) - -# Persist URIs of training file(s) for model monitoring in batch predictions -# See https://cloud.google.com/python/docs/reference/aiplatform/latest/google.cloud.aiplatform_v1beta1.types.ModelMonitoringObjectiveConfig.TrainingDataset # noqa: E501 -# for the expected schema. -path = args.model / TRAINING_DATASET_INFO -training_dataset_for_monitoring = { - "gcsSource": {"uris": [args.train_data]}, - "dataFormat": "csv", - "targetField": label, -} -logging.info(f"Save training dataset info for model monitoring: {path}") -logging.info(f"Training dataset: {training_dataset_for_monitoring}") - -with open(path, "w") as fp: - json.dump(training_dataset_for_monitoring, fp) diff --git a/pipelines/src/pipelines/xgboost/prediction/assets/.gitkeep b/pipelines/src/pipelines/xgboost/prediction/assets/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/pipelines/src/pipelines/xgboost/training/assets/.gitkeep b/pipelines/src/pipelines/xgboost/training/assets/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/pipelines/src/pipelines/xgboost/training/assets/train_xgb_model.py b/pipelines/src/pipelines/xgboost/training/assets/train_xgb_model.py deleted file mode 100644 index 71cc65b5..00000000 --- a/pipelines/src/pipelines/xgboost/training/assets/train_xgb_model.py +++ /dev/null @@ -1,142 +0,0 @@ -import argparse -from pathlib import Path - -import joblib -import json -import os -import logging - -import numpy as np -import pandas as pd -from sklearn import metrics -from sklearn.compose import ColumnTransformer -from sklearn.pipeline import Pipeline -from sklearn.preprocessing import StandardScaler, OrdinalEncoder, OneHotEncoder -from xgboost import XGBRegressor - -logging.basicConfig(level=logging.DEBUG) - -# used for monitoring during prediction time -TRAINING_DATASET_INFO = "training_dataset.json" -# numeric/categorical features in Chicago trips dataset to be preprocessed -NUM_COLS = ["dayofweek", "hourofday", "trip_distance", "trip_miles", "trip_seconds"] -ORD_COLS = ["company"] -OHE_COLS = ["payment_type"] - - -def split_xy(df: pd.DataFrame, label: str) -> (pd.DataFrame, pd.Series): - """Split dataframe into X and y.""" - return df.drop(columns=[label]), df[label] - - -def indices_in_list(elements: list, base_list: list) -> list: - """Get indices of specific elements in a base list""" - return [idx for idx, elem in enumerate(base_list) if elem in elements] - - -parser = argparse.ArgumentParser() -parser.add_argument("--train_data", type=str, required=True) -parser.add_argument("--valid_data", type=str, required=True) -parser.add_argument("--test_data", type=str, required=True) -parser.add_argument("--model", default=os.getenv("AIP_MODEL_DIR"), type=str, help="") -parser.add_argument("--metrics", type=str, required=True) -parser.add_argument("--hparams", default={}, type=json.loads) -args = parser.parse_args() - -if args.model.startswith("gs://"): - args.model = Path("/gcs/" + args.model[5:]) - -logging.info("Read csv files into dataframes") -df_train = pd.read_csv(args.train_data) -df_valid = pd.read_csv(args.valid_data) -df_test = pd.read_csv(args.test_data) - -logging.info("Split dataframes") -label = args.hparams["label"] -X_train, y_train = split_xy(df_train, label) -X_valid, y_valid = split_xy(df_valid, label) -X_test, y_test = split_xy(df_test, label) - -logging.info("Get the number of unique categories for ordinal encoded columns") -ordinal_columns = X_train[ORD_COLS] -n_unique_cat = ordinal_columns.nunique() - -logging.info("Get indices of columns in base data") -col_list = X_train.columns.tolist() -num_indices = indices_in_list(NUM_COLS, col_list) -cat_indices_onehot = indices_in_list(OHE_COLS, col_list) -cat_indices_ordinal = indices_in_list(ORD_COLS, col_list) - -ordinal_transformers = [ - ( - f"ordinal encoding for {ord_col}", - OrdinalEncoder( - handle_unknown="use_encoded_value", unknown_value=n_unique_cat[ord_col] - ), - [ord_index], - ) - for ord_col in ORD_COLS - for ord_index in cat_indices_ordinal -] -all_transformers = [ - ("numeric_scaling", StandardScaler(), num_indices), - ( - "one_hot_encoding", - OneHotEncoder(handle_unknown="ignore"), - cat_indices_onehot, - ), -] + ordinal_transformers - -logging.info("Build sklearn preprocessing steps") -preprocesser = ColumnTransformer(transformers=all_transformers) -logging.info("Build sklearn pipeline with XGBoost model") -xgb_model = XGBRegressor(**args.hparams) - -pipeline = Pipeline( - steps=[("feature_engineering", preprocesser), ("train_model", xgb_model)] -) - -logging.info("Transform validation data") -valid_preprocesser = preprocesser.fit(X_train) -X_valid_transformed = valid_preprocesser.transform(X_valid) - -logging.info("Fit model") -pipeline.fit(X_train, y_train, train_model__eval_set=[(X_valid_transformed, y_valid)]) - -logging.info("Predict test data") -y_pred = pipeline.predict(X_test) -y_pred = y_pred.clip(0) - -metrics = { - "problemType": "regression", - "rootMeanSquaredError": np.sqrt(metrics.mean_squared_error(y_test, y_pred)), - "meanAbsoluteError": metrics.mean_absolute_error(y_test, y_pred), - "meanAbsolutePercentageError": metrics.mean_absolute_percentage_error( - y_test, y_pred - ), - "rSquared": metrics.r2_score(y_test, y_pred), - "rootMeanSquaredLogError": np.sqrt(metrics.mean_squared_log_error(y_test, y_pred)), -} - -logging.info(f"Save model to: {args.model}") -args.model.mkdir(parents=True) -joblib.dump(pipeline, str(args.model / "model.joblib")) - -logging.info(f"Metrics: {metrics}") -with open(args.metrics, "w") as fp: - json.dump(metrics, fp) - -# Persist URIs of training file(s) for model monitoring in batch predictions -# See https://cloud.google.com/python/docs/reference/aiplatform/latest/google.cloud.aiplatform_v1beta1.types.ModelMonitoringObjectiveConfig.TrainingDataset # noqa: E501 -# for the expected schema. -path = args.model / TRAINING_DATASET_INFO -training_dataset_for_monitoring = { - "gcsSource": {"uris": [args.train_data]}, - "dataFormat": "csv", - "targetField": label, -} -logging.info(f"Training dataset info: {training_dataset_for_monitoring}") - -with open(path, "w") as fp: - logging.info(f"Save training dataset info for model monitoring: {path}") - json.dump(training_dataset_for_monitoring, fp) From ee301889d7aeb2662c2b2b841a373294b62bc60e Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 23 May 2023 17:34:33 +0100 Subject: [PATCH 043/238] feat: assets files --- pipelines/assets/train_tf_model.py | 13 +++++++---- pipelines/assets/train_xgb_model.py | 35 +++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/pipelines/assets/train_tf_model.py b/pipelines/assets/train_tf_model.py index c89a7880..5d164ae7 100644 --- a/pipelines/assets/train_tf_model.py +++ b/pipelines/assets/train_tf_model.py @@ -219,6 +219,9 @@ def _get_temp_dir(dirpath, task_id): parser.add_argument("--hparams", default={}, type=json.loads) args = parser.parse_args() +if args.model.startswith("gs://"): + args.model = Path("/gcs/" + args.model[5:]) + # merge dictionaries by overwriting default_model_params if provided in model_params hparams = {**DEFAULT_HPARAMS, **args.hparams} logging.info(f"Using model hyper-parameters: {hparams}") @@ -261,9 +264,9 @@ def _get_temp_dir(dirpath, task_id): logging.info("not chief node, exiting now") sys.exit() -os.makedirs(args.model, exist_ok=True) logging.info(f"Save model to: {args.model}") -tf_model.save(args.model, save_format="tf") +args.model.mkdir(parents=True) +tf_model.save(str(args.model), save_format="tf") logging.info(f"Save metrics to: {args.metrics}") eval_metrics = dict(zip(tf_model.metrics_names, tf_model.evaluate(test_ds))) @@ -281,11 +284,13 @@ def _get_temp_dir(dirpath, task_id): json.dump(metrics, fp) # Persist URIs of training file(s) for model monitoring in batch predictions -path = Path(args.model) / TRAINING_DATASET_INFO +# See https://cloud.google.com/python/docs/reference/aiplatform/latest/google.cloud.aiplatform_v1beta1.types.ModelMonitoringObjectiveConfig.TrainingDataset # noqa: E501 +# for the expected schema. +path = args.model / TRAINING_DATASET_INFO training_dataset_for_monitoring = { "gcsSource": {"uris": [args.train_data]}, "dataFormat": "csv", - "targetField": hparams["label"], + "targetField": label, } logging.info(f"Save training dataset info for model monitoring: {path}") logging.info(f"Training dataset: {training_dataset_for_monitoring}") diff --git a/pipelines/assets/train_xgb_model.py b/pipelines/assets/train_xgb_model.py index 31d95247..71cc65b5 100644 --- a/pipelines/assets/train_xgb_model.py +++ b/pipelines/assets/train_xgb_model.py @@ -1,4 +1,6 @@ import argparse +from pathlib import Path + import joblib import json import os @@ -14,7 +16,9 @@ logging.basicConfig(level=logging.DEBUG) - +# used for monitoring during prediction time +TRAINING_DATASET_INFO = "training_dataset.json" +# numeric/categorical features in Chicago trips dataset to be preprocessed NUM_COLS = ["dayofweek", "hourofday", "trip_distance", "trip_miles", "trip_seconds"] ORD_COLS = ["company"] OHE_COLS = ["payment_type"] @@ -39,6 +43,9 @@ def indices_in_list(elements: list, base_list: list) -> list: parser.add_argument("--hparams", default={}, type=json.loads) args = parser.parse_args() +if args.model.startswith("gs://"): + args.model = Path("/gcs/" + args.model[5:]) + logging.info("Read csv files into dataframes") df_train = pd.read_csv(args.train_data) df_valid = pd.read_csv(args.valid_data) @@ -111,15 +118,25 @@ def indices_in_list(elements: list, base_list: list) -> list: "rootMeanSquaredLogError": np.sqrt(metrics.mean_squared_log_error(y_test, y_pred)), } -try: - model_path = args.model.replace("gs://", "/gcs/") - logging.info(f"Save model to: {model_path}") - os.makedirs(model_path, exist_ok=True) - joblib.dump(pipeline, model_path + "model.joblib") -except Exception as e: - print(e) - raise e +logging.info(f"Save model to: {args.model}") +args.model.mkdir(parents=True) +joblib.dump(pipeline, str(args.model / "model.joblib")) logging.info(f"Metrics: {metrics}") with open(args.metrics, "w") as fp: json.dump(metrics, fp) + +# Persist URIs of training file(s) for model monitoring in batch predictions +# See https://cloud.google.com/python/docs/reference/aiplatform/latest/google.cloud.aiplatform_v1beta1.types.ModelMonitoringObjectiveConfig.TrainingDataset # noqa: E501 +# for the expected schema. +path = args.model / TRAINING_DATASET_INFO +training_dataset_for_monitoring = { + "gcsSource": {"uris": [args.train_data]}, + "dataFormat": "csv", + "targetField": label, +} +logging.info(f"Training dataset info: {training_dataset_for_monitoring}") + +with open(path, "w") as fp: + logging.info(f"Save training dataset info for model monitoring: {path}") + json.dump(training_dataset_for_monitoring, fp) From 95d44f827188f26191f694c0da98726eb87e4e94 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 23 May 2023 19:55:35 +0100 Subject: [PATCH 044/238] style(pyproject.toml): change author description to string format --- components/bigquery-components/pyproject.toml | 4 +- components/vertex-components/pyproject.toml | 5 +- pipelines/poetry.lock | 55 ++++--------------- pipelines/pyproject.toml | 2 + 4 files changed, 14 insertions(+), 52 deletions(-) diff --git a/components/bigquery-components/pyproject.toml b/components/bigquery-components/pyproject.toml index 3a51450a..efeaa9f6 100644 --- a/components/bigquery-components/pyproject.toml +++ b/components/bigquery-components/pyproject.toml @@ -1,9 +1,7 @@ [tool.poetry] name = "bigquery-components" version = "0.1.0" -authors = [ - {name = "Example User", email = "user@example.com"}, -] +authors = ["Example User "] description = "Bigquery components for vertex pipelines" readme = "README.md" classifiers = [ diff --git a/components/vertex-components/pyproject.toml b/components/vertex-components/pyproject.toml index e6dc1afd..37374cff 100644 --- a/components/vertex-components/pyproject.toml +++ b/components/vertex-components/pyproject.toml @@ -1,9 +1,7 @@ [tool.poetry] name = "vertex-components" version = "0.1.0" -authors = [ - {name = "Example User", email = "user@example.com"}, -] +authors = ["Example User "] description = "KFP components for interacting with Vertex AI" readme = "README.md" classifiers = [ @@ -43,4 +41,3 @@ pythonpath = [ ] testpaths = "tests" junit_family = "xunit2" - diff --git a/pipelines/poetry.lock b/pipelines/poetry.lock index 3c0c989a..62a8d667 100644 --- a/pipelines/poetry.lock +++ b/pipelines/poetry.lock @@ -237,7 +237,7 @@ files = [ name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -281,14 +281,14 @@ termcolor = "*" [[package]] name = "google-api-core" -version = "2.10.2" +version = "1.34.0" description = "Google API client core library" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.10.2.tar.gz", hash = "sha256:10c06f7739fe57781f87523375e8e1a3a4674bf6392cd6131a3222182b971320"}, - {file = "google_api_core-2.10.2-py3-none-any.whl", hash = "sha256:34f24bd1d5f72a8c4519773d99ca6bf080a6c4e041b4e9f024fe230191dda62e"}, + {file = "google-api-core-1.34.0.tar.gz", hash = "sha256:6fb380f49d19ee1d09a9722d0379042b7edb06c0112e4796c7a395078a043e71"}, + {file = "google_api_core-1.34.0-py3-none-any.whl", hash = "sha256:7421474c39d396a74dfa317dddbc69188f2336835f526087c7648f91105e32ff"}, ] [package.dependencies] @@ -296,7 +296,7 @@ google-auth = ">=1.25.0,<3.0dev" googleapis-common-protos = ">=1.56.2,<2.0dev" grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.0.0dev" requests = ">=2.18.0,<3.0.0dev" [package.extras] @@ -488,23 +488,6 @@ google-auth = ">=1.25.0,<3.0dev" [package.extras] grpc = ["grpcio (>=1.38.0,<2.0dev)"] -[[package]] -name = "google-cloud-notebooks" -version = "1.4.4" -description = "" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-notebooks-1.4.4.tar.gz", hash = "sha256:76c1c21f2328c0777c4b0333d1a9adbeeea94cfc9af25fe589bef00a9dae41cd"}, - {file = "google_cloud_notebooks-1.4.4-py2.py3-none-any.whl", hash = "sha256:b0204d26663df0e460cd4158d6d0747e9e16d70c607e4da99572a77b51cf2e8d"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} -proto-plus = ">=1.22.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - [[package]] name = "google-cloud-notebooks" version = "1.7.0" @@ -550,24 +533,6 @@ protobuf = ">=3.19.0,<4.0.0dev" [package.extras] tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev)", "google-cloud-aiplatform (>=1.14.0,<2)", "google-cloud-notebooks (>=0.4.0)", "google-cloud-storage (>=2.2.1,<3)", "googleapis-common-protos (>=1.56.2,<2.0dev)", "grpcio-status (<=1.47.0)", "kfp (>=1.8.9,<2.0.0)", "mock (>=4.0.0)", "protobuf (>=3.19.0,<4.0.0dev)", "pytest (>=6.0.0)"] -[[package]] -name = "google-cloud-resource-manager" -version = "1.6.3" -description = "Google Cloud Resource Manager API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-resource-manager-1.6.3.tar.gz", hash = "sha256:6cf8a9a74e65a03857896967c79307b75805c752ade3bc41224e6167774bc9c9"}, - {file = "google_cloud_resource_manager-1.6.3-py2.py3-none-any.whl", hash = "sha256:e330883c53c5e3e38a651da85ae0bce201a736b5cd5f9df10941160c6a66ce6e"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = ">=1.22.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - [[package]] name = "google-cloud-resource-manager" version = "1.10.0" @@ -868,7 +833,7 @@ files = [ name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1135,7 +1100,7 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest- name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1352,7 +1317,7 @@ files = [ name = "pytest" version = "7.3.1" description = "pytest: simple powerful testing with Python" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1630,7 +1595,7 @@ tests = ["pytest", "pytest-cov"] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1842,4 +1807,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "61dd42065ded5aa173586b15285aee373d40d89710fff18ae49e546999a29156" +content-hash = "3f173b0e8ccc458b480858963ca07ed592426d564e0fa62378a51e325d15f802" diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index 0c5c74d6..1fdc5156 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -15,6 +15,8 @@ python = "^3.9" Jinja2 = ">=3.0.1,<4.0.0" google-cloud-aiplatform = "1.24.1" google-cloud-pipeline-components = "1.0.42" +pytest = "^7.3.1" + [tool.poetry.group.dev.dependencies] pytest = ">=7.3.1,<8.0.0" From 96aff8b0b5fd70cf8f2109ccbba256a3931575f0 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Wed, 24 May 2023 10:47:22 +0100 Subject: [PATCH 045/238] Update Makefile Co-authored-by: Felix Schaumann <89205956+felix-datatonic@users.noreply.github.com> --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 337fe89c..df8ed419 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ test-all-components: ## Run unit tests for all pipeline components done sync-assets: ## Sync assets folder to GCS. - @if [ -d "./pipelines/src/pipelines/assets/" ] ; then \ + @if [ -d "./pipelines/assets/" ] ; then \ echo "Syncing assets to GCS" && \ gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}/assets ; \ else \ From 9f6cbc4b6f992761abfb67174c77f30ab4df2b50 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Wed, 24 May 2023 10:48:21 +0100 Subject: [PATCH 046/238] Update README.md Co-authored-by: Felix Schaumann <89205956+felix-datatonic@users.noreply.github.com> --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 927ea440..38615ae0 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,8 @@ When triggering ad hoc runs in your dev/sandbox environment, or when running the ### Assets -Pipeline folder, contains `assets` directory (`pipelines/assets/`). This can be used for any additional files that may be needed during execution of the pipelines. +The folder `pipelines/assets/` can be used for any additional files that may be needed during execution of the pipelines. +Most importantly this can include your training scripts. This directory is rsync'd to Google Cloud Storage when running a pipeline in the sandbox environment or as part of the CD pipeline (see [CI/CD setup](cloudbuild/README.md)). ## Testing From 352a4ed50929e9150133c640baff44d66dd54a34 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Thu, 25 May 2023 16:51:18 +0100 Subject: [PATCH 047/238] build(pyproject.toml): update packages --- pipelines/poetry.lock | 240 ++++++++++++++++++++------------------- pipelines/pyproject.toml | 9 +- 2 files changed, 129 insertions(+), 120 deletions(-) diff --git a/pipelines/poetry.lock b/pipelines/poetry.lock index 62a8d667..b04293b3 100644 --- a/pipelines/poetry.lock +++ b/pipelines/poetry.lock @@ -31,6 +31,27 @@ docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib- tests = ["attrs[tests-no-zope]", "zope-interface"] tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +[[package]] +name = "bigquery-components" +version = "0.1.0" +description = "Bigquery components for vertex pipelines" +category = "main" +optional = false +python-versions = ">=3.9,<3.11" +files = [] +develop = true + +[package.dependencies] +google-cloud-aiplatform = "1.24.1" +google-cloud-bigquery = "2.30.0" +google-cloud-pipeline-components = "1.0.42" +Jinja2 = ">=3.0.1,<4.0.0" +kfp = "==1.8.21" + +[package.source] +type = "directory" +url = "../components/bigquery-components" + [[package]] name = "cachetools" version = "5.3.0" @@ -279,31 +300,6 @@ files = [ six = "*" termcolor = "*" -[[package]] -name = "google-api-core" -version = "1.34.0" -description = "Google API client core library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-api-core-1.34.0.tar.gz", hash = "sha256:6fb380f49d19ee1d09a9722d0379042b7edb06c0112e4796c7a395078a043e71"}, - {file = "google_api_core-1.34.0-py3-none-any.whl", hash = "sha256:7421474c39d396a74dfa317dddbc69188f2336835f526087c7648f91105e32ff"}, -] - -[package.dependencies] -google-auth = ">=1.25.0,<3.0dev" -googleapis-common-protos = ">=1.56.2,<2.0dev" -grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} -grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.0.0dev" -requests = ">=2.18.0,<3.0.0dev" - -[package.extras] -grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)"] -grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] -grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] - [[package]] name = "google-api-core" version = "2.11.0" @@ -319,10 +315,7 @@ files = [ [package.dependencies] google-auth = ">=2.14.1,<3.0dev" googleapis-common-protos = ">=1.56.2,<2.0dev" -grpcio = [ - {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, - {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, -] +grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" requests = ">=2.18.0,<3.0.0dev" @@ -354,14 +347,14 @@ uritemplate = ">=3.0.0,<4dev" [[package]] name = "google-auth" -version = "2.18.0" +version = "2.18.1" description = "Google Authentication Library" category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" files = [ - {file = "google-auth-2.18.0.tar.gz", hash = "sha256:c66b488a8b005b23ccb97b1198b6cece516c91869091ac5b7c267422db2733c7"}, - {file = "google_auth-2.18.0-py2.py3-none-any.whl", hash = "sha256:ef3f3a67fa54d421a1c155864570f9a8de9179cedc937bda496b7a8ca338e936"}, + {file = "google-auth-2.18.1.tar.gz", hash = "sha256:d7a3249027e7f464fbbfd7ee8319a08ad09d2eea51578575c4bd360ffa049ccb"}, + {file = "google_auth-2.18.1-py2.py3-none-any.whl", hash = "sha256:55a395cdfd3f3dd3f649131d41f97c17b4ed8a2aac1be3502090c716314e8a37"}, ] [package.dependencies] @@ -435,38 +428,34 @@ xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] [[package]] name = "google-cloud-bigquery" -version = "3.10.0" +version = "2.30.0" description = "Google BigQuery API client library" category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.6, <3.11" files = [ - {file = "google-cloud-bigquery-3.10.0.tar.gz", hash = "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df"}, - {file = "google_cloud_bigquery-3.10.0-py2.py3-none-any.whl", hash = "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a"}, + {file = "google-cloud-bigquery-2.30.0.tar.gz", hash = "sha256:fdb57aa5e8af7d692eb5835739d9339dc1b5e89836430fe88dd1ddc1b0047639"}, + {file = "google_cloud_bigquery-2.30.0-py2.py3-none-any.whl", hash = "sha256:f6546ee96d57f45eaac8c4b24b52228a7f21a33669730efc9e69569bc88a04ad"}, ] [package.dependencies] -google-api-core = {version = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} -google-cloud-core = ">=1.6.0,<3.0.0dev" +google-api-core = {version = ">=1.29.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-core = ">=1.4.1,<3.0.0dev" google-resumable-media = ">=0.6.0,<3.0dev" -grpcio = [ - {version = ">=1.47.0,<2.0dev", markers = "python_version < \"3.11\""}, - {version = ">=1.49.1,<2.0dev", markers = "python_version >= \"3.11\""}, -] -packaging = ">=20.0.0" -proto-plus = ">=1.15.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +grpcio = ">=1.38.1,<2.0dev" +packaging = ">=14.3" +proto-plus = ">=1.10.0" +protobuf = ">=3.12.0" python-dateutil = ">=2.7.2,<3.0dev" -requests = ">=2.21.0,<3.0.0dev" +requests = ">=2.18.0,<3.0.0dev" [package.extras] -all = ["Shapely (>=1.8.4,<2.0dev)", "db-dtypes (>=0.3.0,<2.0.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)", "ipywidgets (>=7.7.0)", "opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)", "tqdm (>=4.7.4,<5.0.0dev)"] -bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "pyarrow (>=3.0.0)"] -geopandas = ["Shapely (>=1.8.4,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)"] -ipython = ["ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)"] -ipywidgets = ["ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)"] -opentelemetry = ["opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)"] -pandas = ["db-dtypes (>=0.3.0,<2.0.0dev)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)"] +all = ["Shapely (>=1.6.0,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.0.0,<3.0.0dev)", "grpcio (>=1.38.1,<2.0dev)", "opentelemetry-api (>=0.11b0)", "opentelemetry-instrumentation (>=0.11b0)", "opentelemetry-sdk (>=0.11b0)", "pandas (>=0.24.2)", "pyarrow (>=3.0.0,<7.0dev)", "tqdm (>=4.7.4,<5.0.0dev)"] +bignumeric-type = ["pyarrow (>=3.0.0,<7.0dev)"] +bqstorage = ["google-cloud-bigquery-storage (>=2.0.0,<3.0.0dev)", "grpcio (>=1.38.1,<2.0dev)", "pyarrow (>=3.0.0,<7.0dev)"] +geopandas = ["Shapely (>=1.6.0,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)"] +opentelemetry = ["opentelemetry-api (>=0.11b0)", "opentelemetry-instrumentation (>=0.11b0)", "opentelemetry-sdk (>=0.11b0)"] +pandas = ["pandas (>=0.24.2)", "pyarrow (>=3.0.0,<7.0dev)"] tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] [[package]] @@ -503,10 +492,7 @@ files = [ [package.dependencies] google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = [ - {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, - {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, -] +proto-plus = ">=1.22.0,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" [[package]] @@ -535,23 +521,20 @@ tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0 [[package]] name = "google-cloud-resource-manager" -version = "1.10.0" +version = "1.10.1" description = "Google Cloud Resource Manager API client library" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-resource-manager-1.10.0.tar.gz", hash = "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287"}, - {file = "google_cloud_resource_manager-1.10.0-py2.py3-none-any.whl", hash = "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a"}, + {file = "google-cloud-resource-manager-1.10.1.tar.gz", hash = "sha256:c974fb6f9810476cf7b63ea89394c1a8df47f7f2dc2303e728bb74b500bcde67"}, + {file = "google_cloud_resource_manager-1.10.1-py2.py3-none-any.whl", hash = "sha256:41a2204532f084c707fde0bc1a9bc95c7e0b739d7072dd0b8a25106667a56184"}, ] [package.dependencies] google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = [ - {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, - {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, -] +proto-plus = ">=1.22.0,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" [[package]] @@ -1210,48 +1193,48 @@ pyasn1 = ">=0.4.6,<0.6.0" [[package]] name = "pydantic" -version = "1.10.7" +version = "1.10.8" description = "Data validation and settings management using python type hints" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, - {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, - {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, - {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, - {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, - {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, - {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, - {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, - {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, - {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, - {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, - {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, - {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, - {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, - {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, - {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, - {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, - {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, - {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, - {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, - {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, - {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, - {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, - {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, - {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, - {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, - {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, - {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, - {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, - {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, - {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, - {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, - {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, - {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, - {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, - {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, + {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, + {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, + {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, + {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, + {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, + {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, + {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, + {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, + {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, + {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, ] [package.dependencies] @@ -1392,14 +1375,14 @@ files = [ [[package]] name = "requests" -version = "2.30.0" +version = "2.31.0" description = "Python HTTP for Humans." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "requests-2.30.0-py3-none-any.whl", hash = "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294"}, - {file = "requests-2.30.0.tar.gz", hash = "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4"}, + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] @@ -1463,19 +1446,19 @@ pyasn1 = ">=0.1.3" [[package]] name = "setuptools" -version = "67.7.2" +version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, + {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, + {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -1627,14 +1610,14 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.5.0" +version = "4.6.2" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, + {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, + {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, ] [[package]] @@ -1651,14 +1634,14 @@ files = [ [[package]] name = "urllib3" -version = "1.26.15" +version = "1.26.16" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, + {file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"}, + {file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"}, ] [package.extras] @@ -1666,6 +1649,25 @@ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +[[package]] +name = "vertex-components" +version = "0.1.0" +description = "KFP components for interacting with Vertex AI" +category = "main" +optional = false +python-versions = ">=3.9,<3.11" +files = [] +develop = true + +[package.dependencies] +google-cloud-aiplatform = "1.24.1" +google-cloud-pipeline-components = "1.0.42" +kfp = "==1.8.21" + +[package.source] +type = "directory" +url = "../components/vertex-components" + [[package]] name = "virtualenv" version = "20.23.0" @@ -1689,14 +1691,14 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess [[package]] name = "websocket-client" -version = "1.5.1" +version = "1.5.2" description = "WebSocket client for Python with low level API options" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "websocket-client-1.5.1.tar.gz", hash = "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40"}, - {file = "websocket_client-1.5.1-py3-none-any.whl", hash = "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"}, + {file = "websocket-client-1.5.2.tar.gz", hash = "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b"}, + {file = "websocket_client-1.5.2-py3-none-any.whl", hash = "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1"}, ] [package.extras] @@ -1806,5 +1808,5 @@ files = [ [metadata] lock-version = "2.0" -python-versions = "^3.9" -content-hash = "3f173b0e8ccc458b480858963ca07ed592426d564e0fa62378a51e325d15f802" +python-versions = ">=3.9,<3.11" +content-hash = "c464813a2e2cb67c3d2e0ae229ca383b847a62fca4486fdf2f8ab90f335c624b" diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index 1fdc5156..5e714f01 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -9,13 +9,20 @@ classifiers = [ "Intended Audience :: Developers", "Programming Language :: Python :: 3.9", ] +packages = [ + { include = "pipelines", from = "src" }, +] + [tool.poetry.dependencies] -python = "^3.9" +python = ">=3.9,<3.11" Jinja2 = ">=3.0.1,<4.0.0" google-cloud-aiplatform = "1.24.1" google-cloud-pipeline-components = "1.0.42" pytest = "^7.3.1" +bigquery-components = { path = "../components/bigquery-components", develop = true } +vertex-components = { path = "../components/vertex-components", develop = true } + [tool.poetry.group.dev.dependencies] From 68a290d7acb55b914a737773c5db02aef9140bfc Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Thu, 25 May 2023 16:58:07 +0100 Subject: [PATCH 048/238] build(makefile): update makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f342554d..b2138216 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ compile-pipeline: ## Compile the pipeline to training.json or prediction.json. M setup-components: ## Run unit tests for a component group @cd "components/${GROUP}" && \ - poetry install --dev + poetry install --with dev setup-all-components: ## Run unit tests for all pipeline components @set -e && \ From cec9f3e462e0aeb4388f0df917e1fcf5e8cca41c Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 11:05:32 +0100 Subject: [PATCH 049/238] build(pyproject.toml): update poetry dependencies --- components/bigquery-components/pyproject.toml | 6 +----- components/vertex-components/pyproject.toml | 5 ++--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/components/bigquery-components/pyproject.toml b/components/bigquery-components/pyproject.toml index efeaa9f6..ae6dc103 100644 --- a/components/bigquery-components/pyproject.toml +++ b/components/bigquery-components/pyproject.toml @@ -12,15 +12,11 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.9,<3.11" -Jinja2 = ">=3.0.1,<4.0.0" -google-cloud-aiplatform = "1.24.1" -google-cloud-pipeline-components = "1.0.42" -google-cloud-bigquery = "2.30.0" kfp = "==1.8.21" [tool.poetry.group.dev.dependencies] +google-cloud-bigquery = "2.30.0" pytest = ">=7.3.1,<8.0.0" -pre-commit = ">=2.14.1,<3.0.0" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/components/vertex-components/pyproject.toml b/components/vertex-components/pyproject.toml index 37374cff..62fe0693 100644 --- a/components/vertex-components/pyproject.toml +++ b/components/vertex-components/pyproject.toml @@ -12,13 +12,12 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.9,<3.11" -google-cloud-aiplatform = "1.24.1" -google-cloud-pipeline-components = "1.0.42" kfp = "==1.8.21" [tool.poetry.group.dev.dependencies] +google-cloud-aiplatform = "1.24.1" +google-cloud-pipeline-components = "1.0.42" pytest = ">=7.3.1,<8.0.0" -pre-commit = ">=2.14.1,<3.0.0" [build-system] requires = ["poetry-core>=1.0.0"] From 149ab44fed68b64e604d090500aa9a2d89eb862f Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 11:08:35 +0100 Subject: [PATCH 050/238] build(pyproject.toml): remove pytest from dependecies --- pipelines/pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index 5e714f01..cf42b576 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -19,7 +19,6 @@ python = ">=3.9,<3.11" Jinja2 = ">=3.0.1,<4.0.0" google-cloud-aiplatform = "1.24.1" google-cloud-pipeline-components = "1.0.42" -pytest = "^7.3.1" bigquery-components = { path = "../components/bigquery-components", develop = true } vertex-components = { path = "../components/vertex-components", develop = true } From 341392a6111176c6a20cab9e2719f8445d28c261 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 14:01:09 +0100 Subject: [PATCH 051/238] docs(CONTRIBUTING.md): update docs with poetry requirements --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9e7f8f89..0bbcb994 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,7 @@ This guide is chiefly for users wishing to contribute to the opensource version. ## Links to Important Resources - [pytest](https://docs.pytest.org) - [unittest.mock](https://docs.python.org/3/library/unittest.mock.html) -- [pipenv](https://pipenv-fork.readthedocs.io/en/latest/index.html) +- [poetry](https://python-poetry.org/docs/#installation) - [Kubeflow Pipelines](https://www.kubeflow.org/docs/components/pipelines/overview) - [Vertex AI](https://cloud.google.com/vertex-ai/docs) - [AI Platform SDK](https://googleapis.dev/python/aiplatform/latest/index.html) From 1258220877e228da777e964d34d3d7c23e227ca3 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 14:22:53 +0100 Subject: [PATCH 052/238] docs(README.md): update poetry dependencies --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 09940d13..3c7504a5 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ In a production MLOps solution, your ML pipelines need to be repeatable. So, we 1. Clone the repository locally 1. Install Python: `pyenv install` -1. Install pipenv and pipenv dependencies: `make setup` -1. Install pre-commit hooks: `cd pipelines && pipenv run pre-commit install` +1. Install poetry and dependencies: `make setup` +1. Install pre-commit hooks: `cd pipelines` && run `pre-commit install` 1. Copy `env.sh.example` to `env.sh`, and update the environment variables in `env.sh` 1. Load the environment variables in `env.sh` by running `source env.sh` From 08c3796512d997ee93a42d1b4060b02bb16a06d3 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 14:26:12 +0100 Subject: [PATCH 053/238] style(pr-checks.yaml): update comment with poetry dependency --- cloudbuild/pr-checks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudbuild/pr-checks.yaml b/cloudbuild/pr-checks.yaml index 4a8f92cb..d0747890 100644 --- a/cloudbuild/pr-checks.yaml +++ b/cloudbuild/pr-checks.yaml @@ -14,7 +14,7 @@ --- steps: - # Install pipenv and deps, run pre-commit and unit tests + # Install poetry and deps, run pre-commit and unit tests # Then compile pipelines (to make sure they can compile) # need to run "git init" for pre-commit checks to work - name: python:3.9 From 7185dc66d049821c519f3de472cc4538751f29bf Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 14:27:47 +0100 Subject: [PATCH 054/238] style(pr-checks.yaml): update comments with poetry dependency --- cloudbuild/pr-checks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudbuild/pr-checks.yaml b/cloudbuild/pr-checks.yaml index d0747890..969e7bbf 100644 --- a/cloudbuild/pr-checks.yaml +++ b/cloudbuild/pr-checks.yaml @@ -37,5 +37,5 @@ steps: options: logging: CLOUD_LOGGING_ONLY -# Increase timeout to allow pipenv to resolve dependencies +# Increase timeout to allow poetry to resolve dependencies timeout: 3600s From 7aa2efcddb9ed80a76b8338457fdb61216dabd11 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 14:31:50 +0100 Subject: [PATCH 055/238] style(release.yaml): update comments with poetry dependency --- cloudbuild/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index d09f451d..9d0f4226 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -14,7 +14,7 @@ --- steps: - # Install pipenv, install deps, compile pipelines + # Install poetry, install deps, compile pipelines - name: python:3.9 entrypoint: /bin/sh args: From 86fc9217269da4d274ea934b0e7f0dc46a8ccc09 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 14:41:46 +0100 Subject: [PATCH 056/238] style(README.md): update comments with poetry dependency --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c7504a5..6932dd99 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ In a production MLOps solution, your ML pipelines need to be repeatable. So, we 1. Clone the repository locally 1. Install Python: `pyenv install` 1. Install poetry and dependencies: `make setup` -1. Install pre-commit hooks: `cd pipelines` && run `pre-commit install` +1. Install pre-commit hooks: `cd pipelines` && `poetry run pre-commit install` 1. Copy `env.sh.example` to `env.sh`, and update the environment variables in `env.sh` 1. Load the environment variables in `env.sh` by running `source env.sh` From bcea327f9098c8811d65ba588c365db5c026c241 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 14:44:29 +0100 Subject: [PATCH 057/238] style(README.md): update comments with poetry dependency --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6932dd99..bce38968 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ In a production MLOps solution, your ML pipelines need to be repeatable. So, we 1. Clone the repository locally 1. Install Python: `pyenv install` 1. Install poetry and dependencies: `make setup` -1. Install pre-commit hooks: `cd pipelines` && `poetry run pre-commit install` +1. Install pre-commit hooks: `cd pipelines && poetry run pre-commit install` 1. Copy `env.sh.example` to `env.sh`, and update the environment variables in `env.sh` 1. Load the environment variables in `env.sh` by running `source env.sh` From 9bf426c119d2642b2abe47957b252d8f2c606dba Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 15:07:31 +0100 Subject: [PATCH 058/238] style(README.md): update comments with how to install new component package into pipeline package --- components/README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/components/README.md b/components/README.md index e1b5fac9..5c724aac 100644 --- a/components/README.md +++ b/components/README.md @@ -6,20 +6,21 @@ This directory contains multiple Python packages that are used to define pipelin To create a new set of components (with different Python dependencies), copy one of the existing subdirectories and rename the different files and directories as appropriate (e.g. `bigquery-components` -> `my-new-components`). You will also need to update any references in the Python files themselves, as well as the `Pipfile` and `pyproject.toml`. -Your Python dependencies should be defined in `Pipfile`, `pyproject.toml`, and in `packages_to_install` (in the `@component` decorator): +Your Python dependencies should be defined in `poetry.lock file`, `pyproject.toml`, and in `packages_to_install` (in the `@component` decorator): -- In `Pipfile`, add `kfp` to the `[packages]` section (pinned to a specific version), and add any dependencies that your component uses under `[dev-packages]` (each pinned to a specific version) +- In `pyproject.toml`, add `kfp` to the `[packages]` section (pinned to a specific version), and add any dependencies that your component uses under `[dev-packages]` (each pinned to a specific version) - In `pyproject.toml`, add `kfp` to the `[dependencies]` section (pinned to a specific version), and add any dependencies that your component uses under `[[project.optional-dependencies]` -> `tests` (each pinned to a specific version) - In `packages_to_install` (in the `@component` decorator used to define your component), add any dependencies that your component uses (each pinned to a specific version) Define your pipeline components using the `@component` decorator in Python files under `my-new-components/src/my-new-components`. You will need to update the `__init__.py` file to provide tests - see the [Kubeflow Pipelines documentation](https://www.kubeflow.org/docs/components/pipelines/v1/sdk-v2/python-function-components/#building-python-function-based-components) for more information about writing pipeline components. -Finally, you will need to install this new components package into the [`pipelines`](../pipelines) package. In [`pipelines/Pipfile`](../pipelines/Pipfile), add the following line to the `packages` section: -```ini -my-new-components = {editable = true, path = "./../components/my-new-components"} -``` +Finally, you will need to install this new components package into the [`pipelines`](../pipelines) package. In [`pipelines/pyproject.toml`](../pipelines/pyproject.toml), add the following line to the `packages` section: -Once you have added this line to [`pipelines/Pipfile`](../pipelines/Pipfile), run `make setup` from the root of the repository to install the new components package into the `pipelines` package. +``` +[tool.poetry.dependencies] +my-new-components = { path = "../components/my-new-components", develop = true } +``` +Once you have added this line to [`pipelines/pyproject.toml`](../pipelines/pyproject.toml), run `make setup` from the root of the repository to install the new components package into the `pipelines` package. ## Testing components From 67abb5e2d409bad78e29109feaaad27253add87e Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 15:10:04 +0100 Subject: [PATCH 059/238] style(CONTRIBUTING.md): update docs with poetry requirements --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0bbcb994..da7e6ba7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -211,12 +211,12 @@ def test_vertex_endpoint_uri(output_uri: str): ``` ## Adding or changing python dependencies -We use [pipenv](https://pipenv-fork.readthedocs.io/en/latest/index.html) to handle our packages and their dependencies. Each group of pipeline components (e.g. [aiplatform](./pipeline_components/aiplatform/)) containers its own pipenv environment, and there is a [separate pipenv environment](./pipelines/) for the ML pipelines themselves and the pipeline trigger code. +We use [poetry](https://python-poetry.org/docs/#installation) to handle our packages and their dependencies. Each group of pipeline components (e.g. [aiplatform](./pipeline_components/aiplatform/)) containers its own poetry environment, and there is a [separate poetry environment](./pipelines/) for the ML pipelines themselves and the pipeline trigger code. ### Adding python dependencies You may need to add new packages for your own use cases. To do this, run the following from the relevant directory ([pipelines](./pipelines) for the main ML pipeline dependencies or the directory of the relevant component group e.g. [aiplatform](./pipeline_components/aiplatform/)): ``` -pipenv install +poetry install ``` ## Committing Changes @@ -260,7 +260,7 @@ make pre-commit - **Checks fail and displays an error message**. Some errors cannot be automatically fixed by pre-commit hooks, and instead they will display the error number and the file and line which failed. For more details beyond the error message, you can look up the error number online. The most common errors are caused by lines which exceed the character limit. Once you identify the cause of the error, you will need to fix this in your code, add the edited file to the staging area, and then commit again. ### Commit changes to Python packages and dependencies -If you have changes to `Pipfile` and `Pipfile.lock`, please make sure you commit these files! +If you have changes to `pyproject.toml` and `poetry.lock`, please make sure you commit these files! ## Makefile This project contains a [Makefile](Makefile) which contains "rules" describing the commands to be executed by the system. These allow you to quickly and easily run commands for specific purposes, for example running all of the unit-tests, or compiling a pipeline. You can find the full set of available `make` rules by running: From 6a7b6d9afc2eda80effa24595999bc899f9d3360 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 15:13:02 +0100 Subject: [PATCH 060/238] style(README.md): update comments with how to install new component package into pipeline package --- components/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/README.md b/components/README.md index 5c724aac..1512b7e4 100644 --- a/components/README.md +++ b/components/README.md @@ -14,10 +14,9 @@ Your Python dependencies should be defined in `poetry.lock file`, `pyproject.tom Define your pipeline components using the `@component` decorator in Python files under `my-new-components/src/my-new-components`. You will need to update the `__init__.py` file to provide tests - see the [Kubeflow Pipelines documentation](https://www.kubeflow.org/docs/components/pipelines/v1/sdk-v2/python-function-components/#building-python-function-based-components) for more information about writing pipeline components. -Finally, you will need to install this new components package into the [`pipelines`](../pipelines) package. In [`pipelines/pyproject.toml`](../pipelines/pyproject.toml), add the following line to the `packages` section: +Finally, you will need to install this new components package into the [`pipelines`](../pipelines) package. In [`pipelines/pyproject.toml`](../pipelines/pyproject.toml), add the following line to the `tool.poetry.dependencies` section: ``` -[tool.poetry.dependencies] my-new-components = { path = "../components/my-new-components", develop = true } ``` Once you have added this line to [`pipelines/pyproject.toml`](../pipelines/pyproject.toml), run `make setup` from the root of the repository to install the new components package into the `pipelines` package. From 94cd885bf8395f9fe99e143faf3cf4900d1d54b5 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 15:18:26 +0100 Subject: [PATCH 061/238] style(README.md): update comments to reflect poetry package installation --- components/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/README.md b/components/README.md index 1512b7e4..2a41ac1f 100644 --- a/components/README.md +++ b/components/README.md @@ -8,8 +8,7 @@ To create a new set of components (with different Python dependencies), copy one Your Python dependencies should be defined in `poetry.lock file`, `pyproject.toml`, and in `packages_to_install` (in the `@component` decorator): -- In `pyproject.toml`, add `kfp` to the `[packages]` section (pinned to a specific version), and add any dependencies that your component uses under `[dev-packages]` (each pinned to a specific version) -- In `pyproject.toml`, add `kfp` to the `[dependencies]` section (pinned to a specific version), and add any dependencies that your component uses under `[[project.optional-dependencies]` -> `tests` (each pinned to a specific version) +- In `pyproject.toml`, add `kfp` to the `[dependencies]` section (pinned to a specific version), and add any dependencies that your component uses under `[tool.poetry.dependencies]`(each pinned to a specific version) - In `packages_to_install` (in the `@component` decorator used to define your component), add any dependencies that your component uses (each pinned to a specific version) Define your pipeline components using the `@component` decorator in Python files under `my-new-components/src/my-new-components`. You will need to update the `__init__.py` file to provide tests - see the [Kubeflow Pipelines documentation](https://www.kubeflow.org/docs/components/pipelines/v1/sdk-v2/python-function-components/#building-python-function-based-components) for more information about writing pipeline components. From 05378d948a0b6bde03525d5e0908f687170b3233 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 15:21:24 +0100 Subject: [PATCH 062/238] style(README.md): update with poetry requirements --- components/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/README.md b/components/README.md index 2a41ac1f..6f58f74b 100644 --- a/components/README.md +++ b/components/README.md @@ -4,7 +4,7 @@ This directory contains multiple Python packages that are used to define pipelin ## Creating a new pipeline components package -To create a new set of components (with different Python dependencies), copy one of the existing subdirectories and rename the different files and directories as appropriate (e.g. `bigquery-components` -> `my-new-components`). You will also need to update any references in the Python files themselves, as well as the `Pipfile` and `pyproject.toml`. +To create a new set of components (with different Python dependencies), copy one of the existing subdirectories and rename the different files and directories as appropriate (e.g. `bigquery-components` -> `my-new-components`). You will also need to update any references in the Python files themselves, as well as the `poetry.lock file` and `pyproject.toml`. Your Python dependencies should be defined in `poetry.lock file`, `pyproject.toml`, and in `packages_to_install` (in the `@component` decorator): From 736392cb4f630d00d2af63ff400b084199c540fb Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 16:12:39 +0100 Subject: [PATCH 063/238] build(poetry.lock): update poetry.lock file --- components/bigquery-components/poetry.lock | 513 +++------------------ components/vertex-components/poetry.lock | 272 +++-------- 2 files changed, 141 insertions(+), 644 deletions(-) diff --git a/components/bigquery-components/poetry.lock b/components/bigquery-components/poetry.lock index 9a846c08..8a60aaaf 100644 --- a/components/bigquery-components/poetry.lock +++ b/components/bigquery-components/poetry.lock @@ -55,18 +55,6 @@ files = [ {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, ] -[[package]] -name = "cfgv" -version = "3.3.1" -description = "Validate configuration and produce human readable error messages." -category = "dev" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] - [[package]] name = "charset-normalizer" version = "3.1.0" @@ -209,18 +197,6 @@ wrapt = ">=1.10,<2" [package.extras] dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] -[[package]] -name = "distlib" -version = "0.3.6" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] - [[package]] name = "docstring-parser" version = "0.15" @@ -248,22 +224,6 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "filelock" -version = "3.12.0" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, - {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, -] - -[package.extras] -docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] - [[package]] name = "fire" version = "0.5.0" @@ -326,20 +286,20 @@ uritemplate = ">=3.0.0,<4dev" [[package]] name = "google-auth" -version = "2.18.0" +version = "2.19.0" description = "Google Authentication Library" category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +python-versions = ">=3.6" files = [ - {file = "google-auth-2.18.0.tar.gz", hash = "sha256:c66b488a8b005b23ccb97b1198b6cece516c91869091ac5b7c267422db2733c7"}, - {file = "google_auth-2.18.0-py2.py3-none-any.whl", hash = "sha256:ef3f3a67fa54d421a1c155864570f9a8de9179cedc937bda496b7a8ca338e936"}, + {file = "google-auth-2.19.0.tar.gz", hash = "sha256:f39d528077ac540793dd3c22a8706178f157642a67d874db25c640b7fead277e"}, + {file = "google_auth-2.19.0-py2.py3-none-any.whl", hash = "sha256:be617bfaf77774008e9d177573f782e109188c8a64ae6e744285df5cea3e7df6"}, ] [package.dependencies] cachetools = ">=2.0.0,<6.0" pyasn1-modules = ">=0.2.1" -rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} +rsa = ">=3.1.4,<5" six = ">=1.9.0" urllib3 = "<2.0" @@ -367,49 +327,11 @@ google-auth = "*" httplib2 = ">=0.15.0" six = "*" -[[package]] -name = "google-cloud-aiplatform" -version = "1.24.1" -description = "Vertex AI API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-aiplatform-1.24.1.tar.gz", hash = "sha256:0ce9e97bf5c977397e52b3b7c4dc78610c135fbde11a60a6c0b77a4fdf776400"}, - {file = "google_cloud_aiplatform-1.24.1-py2.py3-none-any.whl", hash = "sha256:942765a6bad97e46e262dd6599dc5f171663ce952130e0b0b2eb97e0b1b98bfd"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} -google-cloud-bigquery = ">=1.15.0,<4.0.0dev" -google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" -google-cloud-storage = ">=1.32.0,<3.0.0dev" -packaging = ">=14.3" -proto-plus = ">=1.22.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" -shapely = "<2.0.0" - -[package.extras] -autologging = ["mlflow (>=1.27.0,<=2.1.1)"] -cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] -datasets = ["pyarrow (>=3.0.0,<8.0dev)"] -endpoint = ["requests (>=2.28.1)"] -full = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] -lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"] -metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"] -pipelines = ["pyyaml (>=5.3,<6)"] -prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<0.76.0)", "starlette (>=0.17.1)", "uvicorn[standard] (>=0.16.0)"] -private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] -tensorboard = ["tensorflow (>=2.3.0,<3.0.0dev)"] -testing = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "grpcio-testing", "ipython", "kfp", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3,<6)", "requests (>=2.28.1)", "scikit-learn", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] -vizier = ["google-vizier (==0.0.4)"] -xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] - [[package]] name = "google-cloud-bigquery" version = "2.30.0" description = "Google BigQuery API client library" -category = "main" +category = "dev" optional = false python-versions = ">=3.6, <3.11" files = [ @@ -456,66 +378,6 @@ google-auth = ">=1.25.0,<3.0dev" [package.extras] grpc = ["grpcio (>=1.38.0,<2.0dev)"] -[[package]] -name = "google-cloud-notebooks" -version = "1.7.0" -description = "Google Cloud Notebooks API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-notebooks-1.7.0.tar.gz", hash = "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e"}, - {file = "google_cloud_notebooks-1.7.0-py2.py3-none-any.whl", hash = "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = ">=1.22.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - -[[package]] -name = "google-cloud-pipeline-components" -version = "1.0.42" -description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "google_cloud_pipeline_components-1.0.42-py3-none-any.whl", hash = "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623"}, -] - -[package.dependencies] -google-api-core = ">=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev" -google-cloud-aiplatform = ">=1.14.0,<2" -google-cloud-notebooks = ">=0.4.0" -google-cloud-storage = ">=2.2.1,<3" -googleapis-common-protos = ">=1.56.2,<2.0dev" -grpcio-status = "<=1.47.0" -kfp = ">=1.8.9,<2.0.0" -protobuf = ">=3.19.0,<4.0.0dev" - -[package.extras] -tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev)", "google-cloud-aiplatform (>=1.14.0,<2)", "google-cloud-notebooks (>=0.4.0)", "google-cloud-storage (>=2.2.1,<3)", "googleapis-common-protos (>=1.56.2,<2.0dev)", "grpcio-status (<=1.47.0)", "kfp (>=1.8.9,<2.0.0)", "mock (>=4.0.0)", "protobuf (>=3.19.0,<4.0.0dev)", "pytest (>=6.0.0)"] - -[[package]] -name = "google-cloud-resource-manager" -version = "1.10.0" -description = "Google Cloud Resource Manager API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-resource-manager-1.10.0.tar.gz", hash = "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287"}, - {file = "google_cloud_resource_manager-1.10.0-py2.py3-none-any.whl", hash = "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = ">=1.22.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - [[package]] name = "google-cloud-storage" version = "2.9.0" @@ -651,34 +513,16 @@ files = [ ] [package.dependencies] -grpcio = {version = ">=1.44.0,<2.0.0dev", optional = true, markers = "extra == \"grpc\""} protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" [package.extras] grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] -[[package]] -name = "grpc-google-iam-v1" -version = "0.12.6" -description = "IAM API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "grpc-google-iam-v1-0.12.6.tar.gz", hash = "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f"}, - {file = "grpc_google_iam_v1-0.12.6-py2.py3-none-any.whl", hash = "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c"}, -] - -[package.dependencies] -googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} -grpcio = ">=1.44.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - [[package]] name = "grpcio" version = "1.54.2" description = "HTTP/2-based RPC framework" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -734,19 +578,19 @@ protobuf = ["grpcio-tools (>=1.54.2)"] [[package]] name = "grpcio-status" -version = "1.47.0" +version = "1.48.2" description = "Status proto mapping for gRPC" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.47.0.tar.gz", hash = "sha256:c9ce3213e84c6fd8801c31aca3ea4a6b3453eaa40b93a6c0a23ea8999808fa00"}, - {file = "grpcio_status-1.47.0-py3-none-any.whl", hash = "sha256:2154fdb8aad20452488712be6879657b508115ca06139fde8897ea8e9bc79367"}, + {file = "grpcio-status-1.48.2.tar.gz", hash = "sha256:53695f45da07437b7c344ee4ef60d370fd2850179f5a28bb26d8e2aa1102ec11"}, + {file = "grpcio_status-1.48.2-py3-none-any.whl", hash = "sha256:2c33bbdbe20188b2953f46f31af669263b6ee2a9b2d38fa0d36ee091532e21bf"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.47.0" +grpcio = ">=1.48.2" protobuf = ">=3.12.0" [[package]] @@ -764,21 +608,6 @@ files = [ [package.dependencies] pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} -[[package]] -name = "identify" -version = "2.5.24" -description = "File identification library for Python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, - {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, -] - -[package.extras] -license = ["ukkonen"] - [[package]] name = "idna" version = "3.4" @@ -803,24 +632,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - [[package]] name = "jsonschema" version = "4.17.3" @@ -938,81 +749,6 @@ websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" [package.extras] adal = ["adal (>=1.0.2)"] -[[package]] -name = "markupsafe" -version = "2.1.2" -description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, - {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, -] - -[[package]] -name = "nodeenv" -version = "1.8.0" -description = "Node.js virtual environment builder" -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" -files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, -] - -[package.dependencies] -setuptools = "*" - [[package]] name = "oauthlib" version = "3.2.2" @@ -1034,7 +770,7 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1042,22 +778,6 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] -[[package]] -name = "platformdirs" -version = "3.5.1" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, - {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, -] - -[package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] - [[package]] name = "pluggy" version = "1.0.0" @@ -1074,30 +794,11 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[[package]] -name = "pre-commit" -version = "2.21.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, - {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - [[package]] name = "proto-plus" version = "1.22.2" description = "Beautiful, Pythonic protocol buffers." -category = "main" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1172,48 +873,48 @@ pyasn1 = ">=0.4.6,<0.6.0" [[package]] name = "pydantic" -version = "1.10.7" +version = "1.10.8" description = "Data validation and settings management using python type hints" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, - {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, - {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, - {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, - {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, - {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, - {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, - {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, - {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, - {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, - {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, - {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, - {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, - {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, - {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, - {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, - {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, - {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, - {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, - {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, - {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, - {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, - {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, - {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, - {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, - {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, - {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, - {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, - {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, - {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, - {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, - {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, - {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, - {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, - {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, - {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, + {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, + {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, + {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, + {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, + {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, + {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, + {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, + {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, + {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, + {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, ] [package.dependencies] @@ -1354,14 +1055,14 @@ files = [ [[package]] name = "requests" -version = "2.30.0" +version = "2.31.0" description = "Python HTTP for Humans." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "requests-2.30.0-py3-none-any.whl", hash = "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294"}, - {file = "requests-2.30.0.tar.gz", hash = "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4"}, + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] @@ -1425,78 +1126,21 @@ pyasn1 = ">=0.1.3" [[package]] name = "setuptools" -version = "67.7.2" +version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, + {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, + {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] -[[package]] -name = "shapely" -version = "1.8.5.post1" -description = "Geometric objects, predicates, and operations" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d048f93e42ba578b82758c15d8ae037d08e69d91d9872bca5a1895b118f4e2b0"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99ab0ddc05e44acabdbe657c599fdb9b2d82e86c5493bdae216c0c4018a82dee"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a2f0da0109e81e0c101a2b4cd8412f73f5f299e7b5b2deaf64cd2a100ac118"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6fe855e7d45685926b6ba00aaeb5eba5862611f7465775dacd527e081a8ced6d"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec14ceca36f67cb48b34d02d7f65a9acae15cd72b48e303531893ba4a960f3ea"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a2b2a65fa7f97115c1cd989fe9d6f39281ca2a8a014f1d4904c1a6e34d7f25"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-win32.whl", hash = "sha256:21776184516a16bf82a0c3d6d6a312b3cd15a4cabafc61ee01cf2714a82e8396"}, - {file = "Shapely-1.8.5.post1-cp310-cp310-win_amd64.whl", hash = "sha256:a354199219c8d836f280b88f2c5102c81bb044ccea45bd361dc38a79f3873714"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:783bad5f48e2708a0e2f695a34ed382e4162c795cb2f0368b39528ac1d6db7ed"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a23ef3882d6aa203dd3623a3d55d698f59bfbd9f8a3bfed52c2da05a7f0f8640"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab38f7b5196ace05725e407cb8cab9ff66edb8e6f7bb36a398e8f73f52a7aaa2"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d086591f744be483b34628b391d741e46f2645fe37594319e0a673cc2c26bcf"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4728666fff8cccc65a07448cae72c75a8773fea061c3f4f139c44adc429b18c3"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-win32.whl", hash = "sha256:84010db15eb364a52b74ea8804ef92a6a930dfc1981d17a369444b6ddec66efd"}, - {file = "Shapely-1.8.5.post1-cp311-cp311-win_amd64.whl", hash = "sha256:48dcfffb9e225c0481120f4bdf622131c8c95f342b00b158cdbe220edbbe20b6"}, - {file = "Shapely-1.8.5.post1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2fd15397638df291c427a53d641d3e6fd60458128029c8c4f487190473a69a91"}, - {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a74631e511153366c6dbe3229fa93f877e3c87ea8369cd00f1d38c76b0ed9ace"}, - {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:66bdac74fbd1d3458fa787191a90fa0ae610f09e2a5ec398c36f968cc0ed743f"}, - {file = "Shapely-1.8.5.post1-cp36-cp36m-win32.whl", hash = "sha256:6d388c0c1bd878ed1af4583695690aa52234b02ed35f93a1c8486ff52a555838"}, - {file = "Shapely-1.8.5.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:be9423d5a3577ac2e92c7e758bd8a2b205f5e51a012177a590bc46fc51eb4834"}, - {file = "Shapely-1.8.5.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5d7f85c2d35d39ff53c9216bc76b7641c52326f7e09aaad1789a3611a0f812f2"}, - {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:adcf8a11b98af9375e32bff91de184f33a68dc48b9cb9becad4f132fa25cfa3c"}, - {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:753ed0e21ab108bd4282405b9b659f2e985e8502b1a72b978eaa51d3496dee19"}, - {file = "Shapely-1.8.5.post1-cp37-cp37m-win32.whl", hash = "sha256:65b21243d8f6bcd421210daf1fabb9de84de2c04353c5b026173b88d17c1a581"}, - {file = "Shapely-1.8.5.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:370b574c78dc5af3a198a6da5d9b3d7c04654bd2ef7e80e80a3a0992dfb2d9cd"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:532a55ee2a6c52d23d6f7d1567c8f0473635f3b270262c44e1b0c88096827e22"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3480657460e939f45a7d359ef0e172a081f249312557fe9aa78c4fd3a362d993"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b65f5d530ba91e49ffc7c589255e878d2506a8b96ffce69d3b7c4500a9a9eaf8"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:147066da0be41b147a61f8eb805dea3b13709dbc873a431ccd7306e24d712bc0"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2822111ddc5bcfb116e6c663e403579d0fe3f147d2a97426011a191c43a7458"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b47bb6f9369e8bf3e6dbd33e6a25a47ee02b2874792a529fe04a49bf8bc0df6"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-win32.whl", hash = "sha256:2e0a8c2e55f1be1312b51c92b06462ea89e6bb703fab4b114e7a846d941cfc40"}, - {file = "Shapely-1.8.5.post1-cp38-cp38-win_amd64.whl", hash = "sha256:0d885cb0cf670c1c834df3f371de8726efdf711f18e2a75da5cfa82843a7ab65"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0b4ee3132ee90f07d63db3aea316c4c065ed7a26231458dda0874414a09d6ba3"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:02dd5d7dc6e46515d88874134dc8fcdc65826bca93c3eecee59d1910c42c1b17"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6a9a4a31cd6e86d0fbe8473ceed83d4fe760b19d949fb557ef668defafea0f6"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:38f0fbbcb8ca20c16451c966c1f527cc43968e121c8a048af19ed3e339a921cd"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78fb9d929b8ee15cfd424b6c10879ce1907f24e05fb83310fc47d2cd27088e40"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89164e7a9776a19e29f01369a98529321994e2e4d852b92b7e01d4d9804c55bf"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-win32.whl", hash = "sha256:8e59817b0fe63d34baedaabba8c393c0090f061917d18fc0bcc2f621937a8f73"}, - {file = "Shapely-1.8.5.post1-cp39-cp39-win_amd64.whl", hash = "sha256:e9c30b311de2513555ab02464ebb76115d242842b29c412f5a9aa0cac57be9f6"}, - {file = "Shapely-1.8.5.post1.tar.gz", hash = "sha256:ef3be705c3eac282a28058e6c6e5503419b250f482320df2172abcbea642c831"}, -] - -[package.extras] -all = ["numpy", "pytest", "pytest-cov"] -test = ["pytest", "pytest-cov"] -vectorized = ["numpy"] - [[package]] name = "six" version = "1.16.0" @@ -1589,14 +1233,14 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.5.0" +version = "4.6.2" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, + {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, + {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, ] [[package]] @@ -1613,14 +1257,14 @@ files = [ [[package]] name = "urllib3" -version = "1.26.15" +version = "1.26.16" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, + {file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"}, + {file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"}, ] [package.extras] @@ -1628,37 +1272,16 @@ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -[[package]] -name = "virtualenv" -version = "20.23.0" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, - {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, -] - -[package.dependencies] -distlib = ">=0.3.6,<1" -filelock = ">=3.11,<4" -platformdirs = ">=3.2,<4" - -[package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] - [[package]] name = "websocket-client" -version = "1.5.1" +version = "1.5.2" description = "WebSocket client for Python with low level API options" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "websocket-client-1.5.1.tar.gz", hash = "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40"}, - {file = "websocket_client-1.5.1-py3-none-any.whl", hash = "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"}, + {file = "websocket-client-1.5.2.tar.gz", hash = "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b"}, + {file = "websocket_client-1.5.2-py3-none-any.whl", hash = "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1"}, ] [package.extras] @@ -1769,4 +1392,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.11" -content-hash = "1f75fb78ca2fd965417d30dd7715e4c282abcd1b53703654e5c022f4d2e79328" +content-hash = "ec83532f673f942df8b19c490393de3a75bd4d1841ba3dc58958e39c311536de" diff --git a/components/vertex-components/poetry.lock b/components/vertex-components/poetry.lock index 45fcd24d..8a328b03 100644 --- a/components/vertex-components/poetry.lock +++ b/components/vertex-components/poetry.lock @@ -55,18 +55,6 @@ files = [ {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, ] -[[package]] -name = "cfgv" -version = "3.3.1" -description = "Validate configuration and produce human readable error messages." -category = "dev" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] - [[package]] name = "charset-normalizer" version = "3.1.0" @@ -209,18 +197,6 @@ wrapt = ">=1.10,<2" [package.extras] dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] -[[package]] -name = "distlib" -version = "0.3.6" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] - [[package]] name = "docstring-parser" version = "0.15" @@ -248,22 +224,6 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "filelock" -version = "3.12.0" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, - {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, -] - -[package.extras] -docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] - [[package]] name = "fire" version = "0.5.0" @@ -326,20 +286,20 @@ uritemplate = ">=3.0.0,<4dev" [[package]] name = "google-auth" -version = "2.18.0" +version = "2.19.0" description = "Google Authentication Library" category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +python-versions = ">=3.6" files = [ - {file = "google-auth-2.18.0.tar.gz", hash = "sha256:c66b488a8b005b23ccb97b1198b6cece516c91869091ac5b7c267422db2733c7"}, - {file = "google_auth-2.18.0-py2.py3-none-any.whl", hash = "sha256:ef3f3a67fa54d421a1c155864570f9a8de9179cedc937bda496b7a8ca338e936"}, + {file = "google-auth-2.19.0.tar.gz", hash = "sha256:f39d528077ac540793dd3c22a8706178f157642a67d874db25c640b7fead277e"}, + {file = "google_auth-2.19.0-py2.py3-none-any.whl", hash = "sha256:be617bfaf77774008e9d177573f782e109188c8a64ae6e744285df5cea3e7df6"}, ] [package.dependencies] cachetools = ">=2.0.0,<6.0" pyasn1-modules = ">=0.2.1" -rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} +rsa = ">=3.1.4,<5" six = ">=1.9.0" urllib3 = "<2.0" @@ -371,7 +331,7 @@ six = "*" name = "google-cloud-aiplatform" version = "1.24.1" description = "Vertex AI API client library" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -409,7 +369,7 @@ xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] name = "google-cloud-bigquery" version = "3.10.0" description = "Google BigQuery API client library" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -461,7 +421,7 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)"] name = "google-cloud-notebooks" version = "1.7.0" description = "Google Cloud Notebooks API client library" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -479,7 +439,7 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4 name = "google-cloud-pipeline-components" version = "1.0.42" description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." -category = "main" +category = "dev" optional = false python-versions = "*" files = [ @@ -501,14 +461,14 @@ tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0 [[package]] name = "google-cloud-resource-manager" -version = "1.10.0" +version = "1.10.1" description = "Google Cloud Resource Manager API client library" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-resource-manager-1.10.0.tar.gz", hash = "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287"}, - {file = "google_cloud_resource_manager-1.10.0-py2.py3-none-any.whl", hash = "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a"}, + {file = "google-cloud-resource-manager-1.10.1.tar.gz", hash = "sha256:c974fb6f9810476cf7b63ea89394c1a8df47f7f2dc2303e728bb74b500bcde67"}, + {file = "google_cloud_resource_manager-1.10.1-py2.py3-none-any.whl", hash = "sha256:41a2204532f084c707fde0bc1a9bc95c7e0b739d7072dd0b8a25106667a56184"}, ] [package.dependencies] @@ -662,7 +622,7 @@ grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] name = "grpc-google-iam-v1" version = "0.12.6" description = "IAM API client library" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -679,7 +639,7 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 name = "grpcio" version = "1.54.2" description = "HTTP/2-based RPC framework" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -737,7 +697,7 @@ protobuf = ["grpcio-tools (>=1.54.2)"] name = "grpcio-status" version = "1.47.0" description = "Status proto mapping for gRPC" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -765,21 +725,6 @@ files = [ [package.dependencies] pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} -[[package]] -name = "identify" -version = "2.5.24" -description = "File identification library for Python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, - {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, -] - -[package.extras] -license = ["ukkonen"] - [[package]] name = "idna" version = "3.4" @@ -921,21 +866,6 @@ websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" [package.extras] adal = ["adal (>=1.0.2)"] -[[package]] -name = "nodeenv" -version = "1.8.0" -description = "Node.js virtual environment builder" -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" -files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, -] - -[package.dependencies] -setuptools = "*" - [[package]] name = "oauthlib" version = "3.2.2" @@ -957,7 +887,7 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -965,22 +895,6 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] -[[package]] -name = "platformdirs" -version = "3.5.1" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, - {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, -] - -[package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] - [[package]] name = "pluggy" version = "1.0.0" @@ -997,30 +911,11 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[[package]] -name = "pre-commit" -version = "2.21.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, - {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - [[package]] name = "proto-plus" version = "1.22.2" description = "Beautiful, Pythonic protocol buffers." -category = "main" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1095,48 +990,48 @@ pyasn1 = ">=0.4.6,<0.6.0" [[package]] name = "pydantic" -version = "1.10.7" +version = "1.10.8" description = "Data validation and settings management using python type hints" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, - {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, - {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, - {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, - {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, - {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, - {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, - {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, - {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, - {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, - {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, - {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, - {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, - {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, - {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, - {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, - {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, - {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, - {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, - {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, - {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, - {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, - {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, - {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, - {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, - {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, - {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, - {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, - {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, - {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, - {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, - {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, - {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, - {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, - {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, - {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, + {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, + {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, + {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, + {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, + {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, + {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, + {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, + {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, + {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, + {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, ] [package.dependencies] @@ -1277,14 +1172,14 @@ files = [ [[package]] name = "requests" -version = "2.30.0" +version = "2.31.0" description = "Python HTTP for Humans." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "requests-2.30.0-py3-none-any.whl", hash = "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294"}, - {file = "requests-2.30.0.tar.gz", hash = "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4"}, + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] @@ -1348,26 +1243,26 @@ pyasn1 = ">=0.1.3" [[package]] name = "setuptools" -version = "67.7.2" +version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, + {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, + {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shapely" version = "1.8.5.post1" description = "Geometric objects, predicates, and operations" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1512,14 +1407,14 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.5.0" +version = "4.6.2" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, + {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, + {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, ] [[package]] @@ -1536,14 +1431,14 @@ files = [ [[package]] name = "urllib3" -version = "1.26.15" +version = "1.26.16" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, + {file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"}, + {file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"}, ] [package.extras] @@ -1551,37 +1446,16 @@ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -[[package]] -name = "virtualenv" -version = "20.23.0" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, - {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, -] - -[package.dependencies] -distlib = ">=0.3.6,<1" -filelock = ">=3.11,<4" -platformdirs = ">=3.2,<4" - -[package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] - [[package]] name = "websocket-client" -version = "1.5.1" +version = "1.5.2" description = "WebSocket client for Python with low level API options" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "websocket-client-1.5.1.tar.gz", hash = "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40"}, - {file = "websocket_client-1.5.1-py3-none-any.whl", hash = "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e"}, + {file = "websocket-client-1.5.2.tar.gz", hash = "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b"}, + {file = "websocket_client-1.5.2-py3-none-any.whl", hash = "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1"}, ] [package.extras] @@ -1692,4 +1566,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.11" -content-hash = "a33ea27bf21c7344b28dfc284c9da679f3dbd302759f7eb5e929058ebef11572" +content-hash = "1f97617d2b92c562860cb6db2c44356f2a71e8c3444e15edc0a798edc8f5f564" From 369d99d2802cd5fe15f5a9fefff437f8b995ada7 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 26 May 2023 16:56:10 +0100 Subject: [PATCH 064/238] style(README.md): update with poetry requirements --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bce38968..0414b7c6 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ In a production MLOps solution, your ML pipelines need to be repeatable. So, we 1. Clone the repository locally 1. Install Python: `pyenv install` -1. Install poetry and dependencies: `make setup` +1. Install poetry and poetry dependencies: `make setup` 1. Install pre-commit hooks: `cd pipelines && poetry run pre-commit install` 1. Copy `env.sh.example` to `env.sh`, and update the environment variables in `env.sh` 1. Load the environment variables in `env.sh` by running `source env.sh` From d6afa104acca5e9460648aef8d965dbf8ba0c71e Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 09:19:35 +0100 Subject: [PATCH 065/238] build(src): update the base images of components to python 3.9 --- .../src/bigquery_components/extract_bq_to_dataset.py | 2 +- .../src/vertex_components/update_best_model.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py b/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py index 3effe09a..10cd5bf4 100644 --- a/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py +++ b/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py @@ -16,7 +16,7 @@ @component( - base_image="python:3.7", + base_image="python:3.9", packages_to_install=["google-cloud-bigquery==2.30.0"], ) def extract_bq_to_dataset( diff --git a/components/vertex-components/src/vertex_components/update_best_model.py b/components/vertex-components/src/vertex_components/update_best_model.py index eeb45461..14762906 100644 --- a/components/vertex-components/src/vertex_components/update_best_model.py +++ b/components/vertex-components/src/vertex_components/update_best_model.py @@ -17,7 +17,7 @@ @component( - base_image="python:3.7", + base_image="python:3.9", packages_to_install=["google-cloud-aiplatform==1.24.1"], ) def update_best_model( From 28251c6e29d66f3af69ea16d5cf2be4fbbbf89a0 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 10:22:22 +0100 Subject: [PATCH 066/238] build(.python-version): change python version from 3.9 to 3.7 --- components/bigquery-components/.python-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bigquery-components/.python-version b/components/bigquery-components/.python-version index 9f3d4c17..475ba515 100644 --- a/components/bigquery-components/.python-version +++ b/components/bigquery-components/.python-version @@ -1 +1 @@ -3.9.16 +3.7 From 358abc2b9d4ad4989bce6a31a91211caef735471 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 10:24:40 +0100 Subject: [PATCH 067/238] build(.python-vesrion): python 3.7 to 3.9 --- components/bigquery-components/.python-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bigquery-components/.python-version b/components/bigquery-components/.python-version index 475ba515..9f3d4c17 100644 --- a/components/bigquery-components/.python-version +++ b/components/bigquery-components/.python-version @@ -1 +1 @@ -3.7 +3.9.16 From b58e20bf5299aac885f34eaa580f09a8ffdfeaf4 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 30 May 2023 10:26:18 +0100 Subject: [PATCH 068/238] feat: merge conflict --- Makefile | 17 ++ .../tests/test_extract_bq_to_dataset.py | 64 ++++++ .../tests/test_custom_training_job.py | 93 +++++++++ .../tests/test_import_model_evaluation.py | 57 ++++++ .../tests/test_lookup_model.py | 126 +++++------- .../tests/test_model_batch_predict.py | 59 +++--- .../tests/test_update_best_model.py | 43 ++-- pipelines/Pipfile | 1 + pipelines/Pipfile.lock | 189 ++++++++++++------ pipelines/pyproject.toml | 1 + 10 files changed, 463 insertions(+), 187 deletions(-) create mode 100644 components/bigquery-components/tests/test_extract_bq_to_dataset.py create mode 100644 components/vertex-components/tests/test_custom_training_job.py create mode 100644 components/vertex-components/tests/test_import_model_evaluation.py diff --git a/Makefile b/Makefile index df8ed419..88b75c84 100644 --- a/Makefile +++ b/Makefile @@ -57,8 +57,25 @@ test-all-components: ## Run unit tests for all pipeline components $(MAKE) test-components GROUP=$$(basename $$component_group) ; \ done +<<<<<<< HEAD sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ] ; then \ +======= +test-components-coverage: ## Run tests with coverage + @cd "components/${GROUP}" && \ + pipenv run coverage run -m pytest && \ + pipenv run coverage report -m + +test-all-components-coverage: ## Run tests with coverage + @set -e && \ + for component_group in components/*/ ; do \ + echo "Test components under $$component_group" && \ + $(MAKE) test-components-coverage GROUP=$$(basename $$component_group) ; \ + done + +sync-assets: ## Sync assets folder to GCS. Must specify pipeline= + @if [ -d "./pipelines/src/pipelines/${PIPELINE_TEMPLATE}/$(pipeline)/assets/" ] ; then \ +>>>>>>> develop echo "Syncing assets to GCS" && \ gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}/assets ; \ else \ diff --git a/components/bigquery-components/tests/test_extract_bq_to_dataset.py b/components/bigquery-components/tests/test_extract_bq_to_dataset.py new file mode 100644 index 00000000..be3084e6 --- /dev/null +++ b/components/bigquery-components/tests/test_extract_bq_to_dataset.py @@ -0,0 +1,64 @@ +import google.cloud.bigquery # noqa +from kfp.v2.dsl import Dataset +from unittest import mock + +import bigquery_components + +extract_bq_to_dataset = bigquery_components.extract_bq_to_dataset.python_func + + +@mock.patch("google.cloud.bigquery.client.Client") +@mock.patch("google.cloud.bigquery.table.Table") +@mock.patch("google.cloud.bigquery.job.ExtractJobConfig") +def test_extract_bq_to_dataset(mock_job_config, mock_table, mock_client, tmpdir): + """ + Checks that the extract_bq_to_dataset is called correctly + """ + mock_path = tmpdir + mock_client.extract_table.return_value = "my-job" + mock_table.return_value.table_ref = "my-table" + mock_job_config.return_value = "mock-job-config" + + extract_bq_to_dataset( + bq_client_project_id="my-project-id", + source_project_id="source-project-id", + dataset_id="dataset-id", + table_name="table-name", + dataset=Dataset(uri=mock_path), + destination_gcs_uri="gs://mock_bucket", + dataset_location="EU", + extract_job_config=None, + skip_if_exists=False, + ) + + mock_client.return_value.extract_table.assert_called_once_with( + mock_table.return_value, "gs://mock_bucket", job_config="mock-job-config" + ) + + +@mock.patch("google.cloud.bigquery.client.Client") +@mock.patch("google.cloud.bigquery.table.Table") +@mock.patch("google.cloud.bigquery.job.ExtractJobConfig") +@mock.patch("pathlib.Path.exists") +def test_extract_bq_to_dataset_skip_existing( + mock_path_exists, mock_job_config, mock_table, mock_client, tmpdir +): + """ + Checks that when the dataset exists the method is not called + """ + mock_path = tmpdir + mock_path_exists.return_value = True + + extract_bq_to_dataset( + bq_client_project_id="my-project-id", + source_project_id="source-project-id", + dataset_id="dataset-id", + table_name="table-name", + dataset=Dataset(uri=mock_path), + destination_gcs_uri="gs://mock_bucket", + dataset_location="EU", + extract_job_config=None, + skip_if_exists=True, + ) + + assert not mock_client.return_value.extract_table.called diff --git a/components/vertex-components/tests/test_custom_training_job.py b/components/vertex-components/tests/test_custom_training_job.py new file mode 100644 index 00000000..46ec46ca --- /dev/null +++ b/components/vertex-components/tests/test_custom_training_job.py @@ -0,0 +1,93 @@ +import google.cloud.aiplatform as aip # noqa +from kfp.v2.dsl import Dataset, Metrics, Artifact +from unittest import mock +import pytest + + +import vertex_components + +custom_train_job = vertex_components.custom_train_job.python_func + + +@mock.patch("google.cloud.aiplatform.CustomTrainingJob") +@mock.patch("os.path.exists") +@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="{}") +def test_custom_train_job(mock_open, mock_exists, mock_job, tmpdir): + """ + Checks that the custom job method is called + """ + mock_exists.return_value = True + + mock_train_data = Dataset(uri=tmpdir) + mock_valid_data = Dataset(uri=tmpdir) + mock_test_data = Dataset(uri=tmpdir) + mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) + mock_metrics = Metrics(uri=tmpdir) + + custom_train_job( + train_script_uri="gs://my-bucket/train_script.py", + train_data=mock_train_data, + valid_data=mock_valid_data, + test_data=mock_test_data, + project_id="my-project-id", + project_location="europe-west4", + model_display_name="my-model", + train_container_uri="gcr.io/my-project/my-image:latest", + serving_container_uri="gcr.io/my-project/my-serving-image:latest", + model=mock_model, + metrics=mock_metrics, + staging_bucket="gs://my-bucket", + job_name="my-job", + ) + + mock_job.assert_called_once_with( + project="my-project-id", + location="europe-west4", + staging_bucket="gs://my-bucket", + display_name="my-job", + script_path="/gcs/my-bucket/train_script.py", + container_uri="gcr.io/my-project/my-image:latest", + requirements=None, + model_serving_container_image_uri="gcr.io/my-project/my-serving-image:latest", # noqa: E501 + ) + + # Assert metrics loading + mock_open.assert_called_once_with(tmpdir, "r") + + +@mock.patch("google.cloud.aiplatform.CustomTrainingJob") +@mock.patch("os.path.exists") +@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="{}") +def test_custom_train_script_not_found(mock_open, mock_exists, mock_job, tmpdir): + """ + Checks that when the training script is not found + the method fails + """ + mock_exists.return_value = False + + mock_train_data = Dataset(uri=tmpdir) + mock_valid_data = Dataset(uri=tmpdir) + mock_test_data = Dataset(uri=tmpdir) + mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) + mock_metrics = Metrics(uri=tmpdir) + + with pytest.raises(ValueError): + custom_train_job( + train_script_uri="gs://my-bucket/train_script.py", + train_data=mock_train_data, + valid_data=mock_valid_data, + test_data=mock_test_data, + project_id="my-project-id", + project_location="europe-west4", + model_display_name="my-model", + train_container_uri="gcr.io/my-project/my-image:latest", + serving_container_uri="gcr.io/my-project/my-serving-image:latest", + model=mock_model, + metrics=mock_metrics, + staging_bucket="gs://my-bucket", + job_name="my-job", + ) + + # Assert the custom training job is not executed + mock_job.assert_not_called() + mock_open.assert_not_called() diff --git a/components/vertex-components/tests/test_import_model_evaluation.py b/components/vertex-components/tests/test_import_model_evaluation.py new file mode 100644 index 00000000..56cdf8bd --- /dev/null +++ b/components/vertex-components/tests/test_import_model_evaluation.py @@ -0,0 +1,57 @@ +from unittest import mock +from kfp.v2.dsl import Model, Metrics, Dataset + +import vertex_components +from google.cloud.aiplatform_v1 import ModelEvaluation + + +import_model_evaluation = vertex_components.import_model_evaluation.python_func + + +@mock.patch("google.cloud.aiplatform_v1.ModelServiceClient") +@mock.patch( + "builtins.open", + new_callable=mock.mock_open, + read_data='{"accuracy": 0.85, "problemType": "classification"}', +) +@mock.patch("google.protobuf.json_format.ParseDict") +def test_import_model_evaluation( + mock_parse_dict, mock_open, mock_service_client, tmpdir +): + """ + Checks that when the model evaluation is running and it is writing the metrics + """ + mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) + mock_metrics = Metrics(uri=tmpdir) + mock_dataset = Dataset(uri=tmpdir) + + # Create an instance of the mocked ModelServiceClient. + service_client_instance = mock.MagicMock() + mock_service_client.return_value = service_client_instance + # When import_model_evaluation is called during the test, + # it will return a new ModelEvaluation with the specified name. + service_client_instance.import_model_evaluation.return_value = ModelEvaluation( + name="model_evaluation_name" + ) + + # Set the return value for ParseDict to be a mock ModelEvaluation + mock_parse_dict.return_value = mock.MagicMock(spec=ModelEvaluation) + + model_evaluation_name = import_model_evaluation( + model=mock_model, + metrics=mock_metrics, + test_dataset=mock_dataset, + pipeline_job_id="1234", + project_location="my-location", + evaluation_name="Imported evaluation", + ) + + service_client_instance.import_model_evaluation.assert_called_once_with( + parent=mock_model.metadata["resourceName"], + model_evaluation=mock_parse_dict.return_value, + ) + + # Check that open was called with the correct path + mock_open.assert_called_once_with(mock_metrics.uri) + + assert model_evaluation_name[0] == "model_evaluation_name" diff --git a/components/vertex-components/tests/test_lookup_model.py b/components/vertex-components/tests/test_lookup_model.py index 1ba31f81..6950dd1b 100644 --- a/components/vertex-components/tests/test_lookup_model.py +++ b/components/vertex-components/tests/test_lookup_model.py @@ -22,93 +22,75 @@ lookup_model = vertex_components.lookup_model.python_func -def test_lookup_model(tmpdir): +@mock.patch("google.cloud.aiplatform.Model") +def test_lookup_model(mock_model, tmpdir): """ Assert lookup_model produces expected resource name, and that list method is called with the correct arguemnts - - Args: - tmpdir: built-in pytest tmpdir fixture - - Returns: - None """ - with mock.patch("google.cloud.aiplatform.Model") as mock_model: - - # Mock attribute and method - - mock_path = tmpdir - mock_model.resource_name = "my-model-resource-name" - mock_model.uri = mock_path - mock_model.list.return_value = [mock_model] - - # Invoke the model look up - found_model_resource_name, _ = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - model=Model(uri=mock_path), - ) - - assert found_model_resource_name == "my-model-resource-name" - - # Check the list method was called once with the correct arguments - mock_model.list.assert_called_once_with( - filter='display_name="my-model"', - order_by="create_time desc", - location="europe-west4", - project="my-project-id", - ) - -def test_lookup_model_when_no_models(tmpdir): + # Mock attribute and method + mock_path = tmpdir + mock_model.resource_name = "my-model-resource-name" + mock_model.uri = mock_path + mock_model.list.return_value = [mock_model] + + # Invoke the model look up + found_model_resource_name, _ = lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=False, + model=Model(uri=mock_path), + ) + + assert found_model_resource_name == "my-model-resource-name" + + # Check the list method was called once with the correct arguments + mock_model.list.assert_called_once_with( + filter='display_name="my-model"', + order_by="create_time desc", + location="europe-west4", + project="my-project-id", + ) + + +@mock.patch("google.cloud.aiplatform.Model") +def test_lookup_model_when_no_models(mock_model, tmpdir): """ Checks that when there are no models and fail_on_model_found = False, lookup_model returns an empty string. - - Args: - tmpdir: built-in pytest tmpdir fixture - - Returns: - None """ - with mock.patch("google.cloud.aiplatform.Model") as mock_model: - mock_model.list.return_value = [] - exported_model_resource_name, _ = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - model=Model(uri=str(tmpdir)), - ) + mock_model.list.return_value = [] + exported_model_resource_name, _ = lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=False, + model=Model(uri=str(tmpdir)), + ) + print(exported_model_resource_name) assert exported_model_resource_name == "" -def test_lookup_model_when_no_models_fail(tmpdir): +@mock.patch("google.cloud.aiplatform.Model") +def test_lookup_model_when_no_models_fail(mock_model, tmpdir): """ Checks that when there are no models and fail_on_model_found = True, lookup_model raises a RuntimeError. - - Args: - tmpdir: built-in pytest tmpdir fixture - - Returns: - None """ - with mock.patch("google.cloud.aiplatform.Model") as mock_model: - mock_model.list.return_value = [] + mock_model.list.return_value = [] - # Verify that a ValueError is raised - with pytest.raises(RuntimeError): - lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=True, - model=Model(uri=str(tmpdir)), - ) + # Verify that a ValueError is raised + with pytest.raises(RuntimeError): + lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=True, + model=Model(uri=str(tmpdir)), + ) diff --git a/components/vertex-components/tests/test_model_batch_predict.py b/components/vertex-components/tests/test_model_batch_predict.py index 10d9e808..76bca67d 100644 --- a/components/vertex-components/tests/test_model_batch_predict.py +++ b/components/vertex-components/tests/test_model_batch_predict.py @@ -13,7 +13,7 @@ # limitations under the License. import json import pytest -from unittest.mock import Mock, patch +from unittest import mock from kfp.v2.dsl import Model from google.cloud.aiplatform_v1beta1.types.job_state import JobState @@ -30,7 +30,19 @@ "targetField": "col", } +mock_job1 = mock.Mock() +mock_job1.name = "mock-batch-job" +mock_job1.state = JobState.JOB_STATE_SUCCEEDED + +@mock.patch( + "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.create_batch_prediction_job", # noqa : E501 + return_value=mock_job1, +) +@mock.patch( + "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.get_batch_prediction_job", # noqa : E501 + return_value=mock_job1, +) @pytest.mark.parametrize( ( "source_format,destination_format,source_uri,monitoring_training_dataset," @@ -44,6 +56,8 @@ ], ) def test_model_batch_predict( + create_job, + get_job, tmpdir, source_format, destination_format, @@ -55,37 +69,22 @@ def test_model_batch_predict( """ Asserts model_batch_predict successfully creates requests given different arguments. """ - mock_resource_name = "mock-batch-job" - - mock_job1 = Mock() - mock_job1.name = mock_resource_name - mock_job1.state = JobState.JOB_STATE_SUCCEEDED - mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) - with patch( - "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.create_batch_prediction_job", # noqa: E501 - return_value=mock_job1, - ) as create_job, patch( - "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.get_batch_prediction_job", # noqa: E501 - return_value=mock_job1, - ) as get_job: - (gcp_resources,) = model_batch_predict( - model=mock_model, - job_display_name="", - project_location="", - project_id="", - source_uri=source_uri, - destination_uri=destination_format, - source_format=source_format, - destination_format=destination_format, - monitoring_training_dataset=monitoring_training_dataset, - monitoring_alert_email_addresses=monitoring_alert_email_addresses, - monitoring_skew_config=monitoring_skew_config, - ) + (gcp_resources,) = model_batch_predict( + model=mock_model, + job_display_name="", + project_location="", + project_id="", + source_uri=source_uri, + destination_uri=destination_format, + source_format=source_format, + destination_format=destination_format, + monitoring_training_dataset=monitoring_training_dataset, + monitoring_alert_email_addresses=monitoring_alert_email_addresses, + monitoring_skew_config=monitoring_skew_config, + ) create_job.assert_called_once() get_job.assert_called_once() - assert ( - json.loads(gcp_resources)["resources"][0]["resourceUri"] == mock_resource_name - ) + assert json.loads(gcp_resources)["resources"][0]["resourceUri"] == mock_job1.name diff --git a/components/vertex-components/tests/test_update_best_model.py b/components/vertex-components/tests/test_update_best_model.py index bce9a9df..4ef6ba21 100644 --- a/components/vertex-components/tests/test_update_best_model.py +++ b/components/vertex-components/tests/test_update_best_model.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from unittest.mock import patch +from unittest import mock from kfp.v2.dsl import Model @@ -21,26 +21,31 @@ update_best_model = vertex_components.update_best_model.python_func -def test_model_batch_predict(tmpdir): +@mock.patch("google.cloud.aiplatform.Model") +@mock.patch("google.cloud.aiplatform.model_evaluation.ModelEvaluation") +@mock.patch("google.cloud.aiplatform.models.ModelRegistry") +@mock.patch("google.protobuf.json_format.MessageToDict") +def test_model_batch_predict( + mock_message_to_dict, + mock_model_registry, + mock_model_evaluation, + mock_model_class, + tmpdir, +): """ Asserts model_batch_predict successfully creates requests given different arguments. """ mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) mock_message = {"metrics": {"rmse": 0.01}} - - with patch("google.cloud.aiplatform.Model",), patch( - "google.cloud.aiplatform.model_evaluation.ModelEvaluation", - ), patch("google.cloud.aiplatform.models.ModelRegistry",), patch( - "google.protobuf.json_format.MessageToDict", return_value=mock_message - ): - - (challenger_wins,) = update_best_model( - challenger=mock_model, - challenger_evaluation="", - parent_model="", - project_id="", - project_location="", - eval_metric="rmse", - eval_lower_is_better=True, - ) - assert not challenger_wins + mock_message_to_dict.return_value = mock_message + + (challenger_wins,) = update_best_model( + challenger=mock_model, + challenger_evaluation="", + parent_model="", + project_id="", + project_location="", + eval_metric="rmse", + eval_lower_is_better=True, + ) + assert not challenger_wins diff --git a/pipelines/Pipfile b/pipelines/Pipfile index 507b4e6d..9538e7fc 100644 --- a/pipelines/Pipfile +++ b/pipelines/Pipfile @@ -14,6 +14,7 @@ bigquery-components = {editable = true, path = "./../components/bigquery-compone [dev-packages] pytest = ">=7.3.1,<8.0.0" pre-commit = ">=2.14.1,<3.0.0" +coverage = "==7.2.5" [requires] python_version = "3.7" diff --git a/pipelines/Pipfile.lock b/pipelines/Pipfile.lock index 92d8d064..42ce01df 100644 --- a/pipelines/Pipfile.lock +++ b/pipelines/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c889b828537547fe3e76c0ca6f812f331bffdf13bfd8c72104145db94eb555e0" + "sha256": "2350f00a78fbfb02ef816a1ba3f5316c810e711a7cc685895cb135cb293d38cd" }, "pipfile-spec": 6, "requires": { @@ -192,11 +192,11 @@ }, "google-auth": { "hashes": [ - "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc", - "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f" + "sha256:55a395cdfd3f3dd3f649131d41f97c17b4ed8a2aac1be3502090c716314e8a37", + "sha256:d7a3249027e7f464fbbfd7ee8319a08ad09d2eea51578575c4bd360ffa049ccb" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.17.3" + "version": "==2.18.1" }, "google-auth-httplib2": { "hashes": [ @@ -246,11 +246,11 @@ }, "google-cloud-resource-manager": { "hashes": [ - "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a", - "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287" + "sha256:41a2204532f084c707fde0bc1a9bc95c7e0b739d7072dd0b8a25106667a56184", + "sha256:c974fb6f9810476cf7b63ea89394c1a8df47f7f2dc2303e728bb74b500bcde67" ], "markers": "python_version >= '3.7'", - "version": "==1.10.0" + "version": "==1.10.1" }, "google-cloud-storage": { "hashes": [ @@ -360,53 +360,53 @@ }, "grpcio": { "hashes": [ - "sha256:02000b005bc8b72ff50c477b6431e8886b29961159e8b8d03c00b3dd9139baed", - "sha256:031bbd26656e0739e4b2c81c172155fb26e274b8d0312d67aefc730bcba915b6", - "sha256:1209d6b002b26e939e4c8ea37a3d5b4028eb9555394ea69fb1adbd4b61a10bb8", - "sha256:125ed35aa3868efa82eabffece6264bf638cfdc9f0cd58ddb17936684aafd0f8", - "sha256:1382bc499af92901c2240c4d540c74eae8a671e4fe9839bfeefdfcc3a106b5e2", - "sha256:16bca8092dd994f2864fdab278ae052fad4913f36f35238b2dd11af2d55a87db", - "sha256:1c59d899ee7160638613a452f9a4931de22623e7ba17897d8e3e348c2e9d8d0b", - "sha256:1d109df30641d050e009105f9c9ca5a35d01e34d2ee2a4e9c0984d392fd6d704", - "sha256:1fa7d6ddd33abbd3c8b3d7d07c56c40ea3d1891ce3cd2aa9fa73105ed5331866", - "sha256:21c4a1aae861748d6393a3ff7867473996c139a77f90326d9f4104bebb22d8b8", - "sha256:224166f06ccdaf884bf35690bf4272997c1405de3035d61384ccb5b25a4c1ca8", - "sha256:2262bd3512ba9e9f0e91d287393df6f33c18999317de45629b7bd46c40f16ba9", - "sha256:2585b3c294631a39b33f9f967a59b0fad23b1a71a212eba6bc1e3ca6e6eec9ee", - "sha256:27fb030a4589d2536daec5ff5ba2a128f4f155149efab578fe2de2cb21596d3d", - "sha256:30fbbce11ffeb4f9f91c13fe04899aaf3e9a81708bedf267bf447596b95df26b", - "sha256:3930669c9e6f08a2eed824738c3d5699d11cd47a0ecc13b68ed11595710b1133", - "sha256:3b170e441e91e4f321e46d3cc95a01cb307a4596da54aca59eb78ab0fc03754d", - "sha256:3db71c6f1ab688d8dfc102271cedc9828beac335a3a4372ec54b8bf11b43fd29", - "sha256:48cb7af77238ba16c77879009003f6b22c23425e5ee59cb2c4c103ec040638a5", - "sha256:49eace8ea55fbc42c733defbda1e4feb6d3844ecd875b01bb8b923709e0f5ec8", - "sha256:533eaf5b2a79a3c6f35cbd6a095ae99cac7f4f9c0e08bdcf86c130efd3c32adf", - "sha256:5942a3e05630e1ef5b7b5752e5da6582460a2e4431dae603de89fc45f9ec5aa9", - "sha256:62117486460c83acd3b5d85c12edd5fe20a374630475388cfc89829831d3eb79", - "sha256:650f5f2c9ab1275b4006707411bb6d6bc927886874a287661c3c6f332d4c068b", - "sha256:6dc1e2c9ac292c9a484ef900c568ccb2d6b4dfe26dfa0163d5bc815bb836c78d", - "sha256:73c238ef6e4b64272df7eec976bb016c73d3ab5a6c7e9cd906ab700523d312f3", - "sha256:775a2f70501370e5ba54e1ee3464413bff9bd85bd9a0b25c989698c44a6fb52f", - "sha256:860fcd6db7dce80d0a673a1cc898ce6bc3d4783d195bbe0e911bf8a62c93ff3f", - "sha256:87f47bf9520bba4083d65ab911f8f4c0ac3efa8241993edd74c8dd08ae87552f", - "sha256:960b176e0bb2b4afeaa1cd2002db1e82ae54c9b6e27ea93570a42316524e77cf", - "sha256:a7caf553ccaf715ec05b28c9b2ab2ee3fdb4036626d779aa09cf7cbf54b71445", - "sha256:a947d5298a0bbdd4d15671024bf33e2b7da79a70de600ed29ba7e0fef0539ebb", - "sha256:a97b0d01ae595c997c1d9d8249e2d2da829c2d8a4bdc29bb8f76c11a94915c9a", - "sha256:b7655f809e3420f80ce3bf89737169a9dce73238af594049754a1128132c0da4", - "sha256:c33744d0d1a7322da445c0fe726ea6d4e3ef2dfb0539eadf23dce366f52f546c", - "sha256:c55a9cf5cba80fb88c850915c865b8ed78d5e46e1f2ec1b27692f3eaaf0dca7e", - "sha256:d2f62fb1c914a038921677cfa536d645cb80e3dd07dc4859a3c92d75407b90a5", - "sha256:d8ae6e0df3a608e99ee1acafaafd7db0830106394d54571c1ece57f650124ce9", - "sha256:e355ee9da9c1c03f174efea59292b17a95e0b7b4d7d2a389265f731a9887d5a9", - "sha256:e3e526062c690517b42bba66ffe38aaf8bc99a180a78212e7b22baa86902f690", - "sha256:eb0807323572642ab73fd86fe53d88d843ce617dd1ddf430351ad0759809a0ae", - "sha256:ebff0738be0499d7db74d20dca9f22a7b27deae31e1bf92ea44924fd69eb6251", - "sha256:ed36e854449ff6c2f8ee145f94851fe171298e1e793f44d4f672c4a0d78064e7", - "sha256:ed3d458ded32ff3a58f157b60cc140c88f7ac8c506a1c567b2a9ee8a2fd2ce54", - "sha256:f4a7dca8ccd8023d916b900aa3c626f1bd181bd5b70159479b142f957ff420e4" - ], - "version": "==1.54.0" + "sha256:0212e2f7fdf7592e4b9d365087da30cb4d71e16a6f213120c89b4f8fb35a3ab3", + "sha256:09d4bfd84686cd36fd11fd45a0732c7628308d094b14d28ea74a81db0bce2ed3", + "sha256:1e623e0cf99a0ac114f091b3083a1848dbc64b0b99e181473b5a4a68d4f6f821", + "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea", + "sha256:2296356b5c9605b73ed6a52660b538787094dae13786ba53080595d52df13a98", + "sha256:2a1e601ee31ef30a9e2c601d0867e236ac54c922d32ed9f727b70dd5d82600d5", + "sha256:2be88c081e33f20630ac3343d8ad9f1125f32987968e9c8c75c051c9800896e8", + "sha256:33d40954199bddbb6a78f8f6f2b2082660f381cd2583ec860a6c2fa7c8400c08", + "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c", + "sha256:46a057329938b08e5f0e12ea3d7aed3ecb20a0c34c4a324ef34e00cecdb88a12", + "sha256:4864f99aac207e3e45c5e26c6cbb0ad82917869abc2f156283be86c05286485c", + "sha256:4c44e1a765b31e175c391f22e8fc73b2a2ece0e5e6ff042743d8109b5d2eff9f", + "sha256:4cb283f630624ebb16c834e5ac3d7880831b07cbe76cb08ab7a271eeaeb8943e", + "sha256:5008964885e8d23313c8e5ea0d44433be9bfd7e24482574e8cc43c02c02fc796", + "sha256:50a9f075eeda5097aa9a182bb3877fe1272875e45370368ac0ee16ab9e22d019", + "sha256:51630c92591d6d3fe488a7c706bd30a61594d144bac7dee20c8e1ce78294f474", + "sha256:5cc928cfe6c360c1df636cf7991ab96f059666ac7b40b75a769410cc6217df9c", + "sha256:61f7203e2767800edee7a1e1040aaaf124a35ce0c7fe0883965c6b762defe598", + "sha256:66233ccd2a9371158d96e05d082043d47dadb18cbb294dc5accfdafc2e6b02a7", + "sha256:70fcac7b94f4c904152809a050164650ac81c08e62c27aa9f156ac518029ebbe", + "sha256:714242ad0afa63a2e6dabd522ae22e1d76e07060b5af2ddda5474ba4f14c2c94", + "sha256:782f4f8662a2157c4190d0f99eaaebc602899e84fb1e562a944e5025929e351c", + "sha256:7fc2b4edb938c8faa4b3c3ea90ca0dd89b7565a049e8e4e11b77e60e4ed2cc05", + "sha256:881d058c5ccbea7cc2c92085a11947b572498a27ef37d3eef4887f499054dca8", + "sha256:89dde0ac72a858a44a2feb8e43dc68c0c66f7857a23f806e81e1b7cc7044c9cf", + "sha256:8cdbcbd687e576d48f7886157c95052825ca9948c0ed2afdc0134305067be88b", + "sha256:8d6192c37a30a115f4663592861f50e130caed33efc4eec24d92ec881c92d771", + "sha256:96a41817d2c763b1d0b32675abeb9179aa2371c72aefdf74b2d2b99a1b92417b", + "sha256:9bdbb7624d65dc0ed2ed8e954e79ab1724526f09b1efa88dcd9a1815bf28be5f", + "sha256:9bf88004fe086c786dc56ef8dd6cb49c026833fdd6f42cb853008bce3f907148", + "sha256:a08920fa1a97d4b8ee5db2f31195de4a9def1a91bc003544eb3c9e6b8977960a", + "sha256:a2f5a1f1080ccdc7cbaf1171b2cf384d852496fe81ddedeb882d42b85727f610", + "sha256:b04202453941a63b36876a7172b45366dc0cde10d5fd7855c0f4a4e673c0357a", + "sha256:b38b3de8cff5bc70f8f9c615f51b48eff7313fc9aca354f09f81b73036e7ddfa", + "sha256:b52d00d1793d290c81ad6a27058f5224a7d5f527867e5b580742e1bd211afeee", + "sha256:b74ae837368cfffeb3f6b498688a123e6b960951be4dec0e869de77e7fa0439e", + "sha256:be48496b0e00460717225e7680de57c38be1d8629dc09dadcd1b3389d70d942b", + "sha256:c0e3155fc5335ec7b3b70f15230234e529ca3607b20a562b6c75fb1b1218874c", + "sha256:c2392f5b5d84b71d853918687d806c1aa4308109e5ca158a16e16a6be71041eb", + "sha256:c72956972e4b508dd39fdc7646637a791a9665b478e768ffa5f4fe42123d5de1", + "sha256:dc80c9c6b608bf98066a038e0172013a49cfa9a08d53335aefefda2c64fc68f4", + "sha256:e416c8baf925b5a1aff31f7f5aecc0060b25d50cce3a5a7255dc5cf2f1d4e5eb", + "sha256:f8da84bbc61a4e92af54dc96344f328e5822d574f767e9b08e1602bb5ddc254a", + "sha256:f900ed4ad7a0f1f05d35f955e0943944d5a75f607a836958c6b8ab2a81730ef2", + "sha256:fd6c6c29717724acf9fc1847c4515d57e4dc12762452457b9cb37461f30a81bb" + ], + "version": "==1.54.2" }, "grpcio-status": { "hashes": [ @@ -784,11 +784,11 @@ }, "setuptools": { "hashes": [ - "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", - "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" + "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", + "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" ], "markers": "python_version >= '3.7'", - "version": "==67.7.2" + "version": "==67.8.0" }, "shapely": { "hashes": [ @@ -1019,6 +1019,63 @@ "markers": "python_full_version >= '3.6.1'", "version": "==3.3.1" }, + "coverage": { + "hashes": [ + "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3", + "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a", + "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813", + "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0", + "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a", + "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd", + "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139", + "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b", + "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252", + "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790", + "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045", + "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce", + "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200", + "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718", + "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b", + "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f", + "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5", + "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade", + "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5", + "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a", + "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8", + "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33", + "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e", + "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c", + "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3", + "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969", + "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068", + "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2", + "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771", + "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed", + "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212", + "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614", + "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88", + "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3", + "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c", + "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84", + "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11", + "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1", + "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1", + "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e", + "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1", + "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd", + "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47", + "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a", + "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c", + "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31", + "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5", + "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6", + "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303", + "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5", + "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47" + ], + "index": "pypi", + "version": "==7.2.5" + }, "distlib": { "hashes": [ "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46", @@ -1068,11 +1125,11 @@ }, "nodeenv": { "hashes": [ - "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e", - "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b" + "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2", + "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==1.7.0" + "version": "==1.8.0" }, "packaging": { "hashes": [ @@ -1084,11 +1141,11 @@ }, "platformdirs": { "hashes": [ - "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4", - "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335" + "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f", + "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5" ], "markers": "python_version >= '3.7'", - "version": "==3.5.0" + "version": "==3.5.1" }, "pluggy": { "hashes": [ @@ -1151,11 +1208,11 @@ }, "setuptools": { "hashes": [ - "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", - "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" + "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", + "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" ], "markers": "python_version >= '3.7'", - "version": "==67.7.2" + "version": "==67.8.0" }, "tomli": { "hashes": [ diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index a5e2cbb7..693a3bd8 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -20,6 +20,7 @@ dependencies = [ tests = [ "google-cloud-bigquery == 2.30.0", "pytest >= 7.3.1,<8.0.0", + "coverage = ==7.2.5" ] [build-system] From d856cd3bd738a83b96a65e74917bd8715ac55234 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Tue, 30 May 2023 10:41:54 +0100 Subject: [PATCH 069/238] Revert "feat: merge conflict" This reverts commit b58e20bf5299aac885f34eaa580f09a8ffdfeaf4. --- Makefile | 17 -- .../tests/test_extract_bq_to_dataset.py | 64 ------ .../tests/test_custom_training_job.py | 93 --------- .../tests/test_import_model_evaluation.py | 57 ------ .../tests/test_lookup_model.py | 126 +++++++----- .../tests/test_model_batch_predict.py | 59 +++--- .../tests/test_update_best_model.py | 43 ++-- pipelines/Pipfile | 1 - pipelines/Pipfile.lock | 189 ++++++------------ pipelines/pyproject.toml | 1 - 10 files changed, 187 insertions(+), 463 deletions(-) delete mode 100644 components/bigquery-components/tests/test_extract_bq_to_dataset.py delete mode 100644 components/vertex-components/tests/test_custom_training_job.py delete mode 100644 components/vertex-components/tests/test_import_model_evaluation.py diff --git a/Makefile b/Makefile index 88b75c84..df8ed419 100644 --- a/Makefile +++ b/Makefile @@ -57,25 +57,8 @@ test-all-components: ## Run unit tests for all pipeline components $(MAKE) test-components GROUP=$$(basename $$component_group) ; \ done -<<<<<<< HEAD sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ] ; then \ -======= -test-components-coverage: ## Run tests with coverage - @cd "components/${GROUP}" && \ - pipenv run coverage run -m pytest && \ - pipenv run coverage report -m - -test-all-components-coverage: ## Run tests with coverage - @set -e && \ - for component_group in components/*/ ; do \ - echo "Test components under $$component_group" && \ - $(MAKE) test-components-coverage GROUP=$$(basename $$component_group) ; \ - done - -sync-assets: ## Sync assets folder to GCS. Must specify pipeline= - @if [ -d "./pipelines/src/pipelines/${PIPELINE_TEMPLATE}/$(pipeline)/assets/" ] ; then \ ->>>>>>> develop echo "Syncing assets to GCS" && \ gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}/assets ; \ else \ diff --git a/components/bigquery-components/tests/test_extract_bq_to_dataset.py b/components/bigquery-components/tests/test_extract_bq_to_dataset.py deleted file mode 100644 index be3084e6..00000000 --- a/components/bigquery-components/tests/test_extract_bq_to_dataset.py +++ /dev/null @@ -1,64 +0,0 @@ -import google.cloud.bigquery # noqa -from kfp.v2.dsl import Dataset -from unittest import mock - -import bigquery_components - -extract_bq_to_dataset = bigquery_components.extract_bq_to_dataset.python_func - - -@mock.patch("google.cloud.bigquery.client.Client") -@mock.patch("google.cloud.bigquery.table.Table") -@mock.patch("google.cloud.bigquery.job.ExtractJobConfig") -def test_extract_bq_to_dataset(mock_job_config, mock_table, mock_client, tmpdir): - """ - Checks that the extract_bq_to_dataset is called correctly - """ - mock_path = tmpdir - mock_client.extract_table.return_value = "my-job" - mock_table.return_value.table_ref = "my-table" - mock_job_config.return_value = "mock-job-config" - - extract_bq_to_dataset( - bq_client_project_id="my-project-id", - source_project_id="source-project-id", - dataset_id="dataset-id", - table_name="table-name", - dataset=Dataset(uri=mock_path), - destination_gcs_uri="gs://mock_bucket", - dataset_location="EU", - extract_job_config=None, - skip_if_exists=False, - ) - - mock_client.return_value.extract_table.assert_called_once_with( - mock_table.return_value, "gs://mock_bucket", job_config="mock-job-config" - ) - - -@mock.patch("google.cloud.bigquery.client.Client") -@mock.patch("google.cloud.bigquery.table.Table") -@mock.patch("google.cloud.bigquery.job.ExtractJobConfig") -@mock.patch("pathlib.Path.exists") -def test_extract_bq_to_dataset_skip_existing( - mock_path_exists, mock_job_config, mock_table, mock_client, tmpdir -): - """ - Checks that when the dataset exists the method is not called - """ - mock_path = tmpdir - mock_path_exists.return_value = True - - extract_bq_to_dataset( - bq_client_project_id="my-project-id", - source_project_id="source-project-id", - dataset_id="dataset-id", - table_name="table-name", - dataset=Dataset(uri=mock_path), - destination_gcs_uri="gs://mock_bucket", - dataset_location="EU", - extract_job_config=None, - skip_if_exists=True, - ) - - assert not mock_client.return_value.extract_table.called diff --git a/components/vertex-components/tests/test_custom_training_job.py b/components/vertex-components/tests/test_custom_training_job.py deleted file mode 100644 index 46ec46ca..00000000 --- a/components/vertex-components/tests/test_custom_training_job.py +++ /dev/null @@ -1,93 +0,0 @@ -import google.cloud.aiplatform as aip # noqa -from kfp.v2.dsl import Dataset, Metrics, Artifact -from unittest import mock -import pytest - - -import vertex_components - -custom_train_job = vertex_components.custom_train_job.python_func - - -@mock.patch("google.cloud.aiplatform.CustomTrainingJob") -@mock.patch("os.path.exists") -@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="{}") -def test_custom_train_job(mock_open, mock_exists, mock_job, tmpdir): - """ - Checks that the custom job method is called - """ - mock_exists.return_value = True - - mock_train_data = Dataset(uri=tmpdir) - mock_valid_data = Dataset(uri=tmpdir) - mock_test_data = Dataset(uri=tmpdir) - mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) - mock_metrics = Metrics(uri=tmpdir) - - custom_train_job( - train_script_uri="gs://my-bucket/train_script.py", - train_data=mock_train_data, - valid_data=mock_valid_data, - test_data=mock_test_data, - project_id="my-project-id", - project_location="europe-west4", - model_display_name="my-model", - train_container_uri="gcr.io/my-project/my-image:latest", - serving_container_uri="gcr.io/my-project/my-serving-image:latest", - model=mock_model, - metrics=mock_metrics, - staging_bucket="gs://my-bucket", - job_name="my-job", - ) - - mock_job.assert_called_once_with( - project="my-project-id", - location="europe-west4", - staging_bucket="gs://my-bucket", - display_name="my-job", - script_path="/gcs/my-bucket/train_script.py", - container_uri="gcr.io/my-project/my-image:latest", - requirements=None, - model_serving_container_image_uri="gcr.io/my-project/my-serving-image:latest", # noqa: E501 - ) - - # Assert metrics loading - mock_open.assert_called_once_with(tmpdir, "r") - - -@mock.patch("google.cloud.aiplatform.CustomTrainingJob") -@mock.patch("os.path.exists") -@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="{}") -def test_custom_train_script_not_found(mock_open, mock_exists, mock_job, tmpdir): - """ - Checks that when the training script is not found - the method fails - """ - mock_exists.return_value = False - - mock_train_data = Dataset(uri=tmpdir) - mock_valid_data = Dataset(uri=tmpdir) - mock_test_data = Dataset(uri=tmpdir) - mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) - mock_metrics = Metrics(uri=tmpdir) - - with pytest.raises(ValueError): - custom_train_job( - train_script_uri="gs://my-bucket/train_script.py", - train_data=mock_train_data, - valid_data=mock_valid_data, - test_data=mock_test_data, - project_id="my-project-id", - project_location="europe-west4", - model_display_name="my-model", - train_container_uri="gcr.io/my-project/my-image:latest", - serving_container_uri="gcr.io/my-project/my-serving-image:latest", - model=mock_model, - metrics=mock_metrics, - staging_bucket="gs://my-bucket", - job_name="my-job", - ) - - # Assert the custom training job is not executed - mock_job.assert_not_called() - mock_open.assert_not_called() diff --git a/components/vertex-components/tests/test_import_model_evaluation.py b/components/vertex-components/tests/test_import_model_evaluation.py deleted file mode 100644 index 56cdf8bd..00000000 --- a/components/vertex-components/tests/test_import_model_evaluation.py +++ /dev/null @@ -1,57 +0,0 @@ -from unittest import mock -from kfp.v2.dsl import Model, Metrics, Dataset - -import vertex_components -from google.cloud.aiplatform_v1 import ModelEvaluation - - -import_model_evaluation = vertex_components.import_model_evaluation.python_func - - -@mock.patch("google.cloud.aiplatform_v1.ModelServiceClient") -@mock.patch( - "builtins.open", - new_callable=mock.mock_open, - read_data='{"accuracy": 0.85, "problemType": "classification"}', -) -@mock.patch("google.protobuf.json_format.ParseDict") -def test_import_model_evaluation( - mock_parse_dict, mock_open, mock_service_client, tmpdir -): - """ - Checks that when the model evaluation is running and it is writing the metrics - """ - mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) - mock_metrics = Metrics(uri=tmpdir) - mock_dataset = Dataset(uri=tmpdir) - - # Create an instance of the mocked ModelServiceClient. - service_client_instance = mock.MagicMock() - mock_service_client.return_value = service_client_instance - # When import_model_evaluation is called during the test, - # it will return a new ModelEvaluation with the specified name. - service_client_instance.import_model_evaluation.return_value = ModelEvaluation( - name="model_evaluation_name" - ) - - # Set the return value for ParseDict to be a mock ModelEvaluation - mock_parse_dict.return_value = mock.MagicMock(spec=ModelEvaluation) - - model_evaluation_name = import_model_evaluation( - model=mock_model, - metrics=mock_metrics, - test_dataset=mock_dataset, - pipeline_job_id="1234", - project_location="my-location", - evaluation_name="Imported evaluation", - ) - - service_client_instance.import_model_evaluation.assert_called_once_with( - parent=mock_model.metadata["resourceName"], - model_evaluation=mock_parse_dict.return_value, - ) - - # Check that open was called with the correct path - mock_open.assert_called_once_with(mock_metrics.uri) - - assert model_evaluation_name[0] == "model_evaluation_name" diff --git a/components/vertex-components/tests/test_lookup_model.py b/components/vertex-components/tests/test_lookup_model.py index 6950dd1b..1ba31f81 100644 --- a/components/vertex-components/tests/test_lookup_model.py +++ b/components/vertex-components/tests/test_lookup_model.py @@ -22,75 +22,93 @@ lookup_model = vertex_components.lookup_model.python_func -@mock.patch("google.cloud.aiplatform.Model") -def test_lookup_model(mock_model, tmpdir): +def test_lookup_model(tmpdir): """ Assert lookup_model produces expected resource name, and that list method is called with the correct arguemnts + + Args: + tmpdir: built-in pytest tmpdir fixture + + Returns: + None """ + with mock.patch("google.cloud.aiplatform.Model") as mock_model: + + # Mock attribute and method + + mock_path = tmpdir + mock_model.resource_name = "my-model-resource-name" + mock_model.uri = mock_path + mock_model.list.return_value = [mock_model] + + # Invoke the model look up + found_model_resource_name, _ = lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=False, + model=Model(uri=mock_path), + ) + + assert found_model_resource_name == "my-model-resource-name" + + # Check the list method was called once with the correct arguments + mock_model.list.assert_called_once_with( + filter='display_name="my-model"', + order_by="create_time desc", + location="europe-west4", + project="my-project-id", + ) + - # Mock attribute and method - mock_path = tmpdir - mock_model.resource_name = "my-model-resource-name" - mock_model.uri = mock_path - mock_model.list.return_value = [mock_model] - - # Invoke the model look up - found_model_resource_name, _ = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - model=Model(uri=mock_path), - ) - - assert found_model_resource_name == "my-model-resource-name" - - # Check the list method was called once with the correct arguments - mock_model.list.assert_called_once_with( - filter='display_name="my-model"', - order_by="create_time desc", - location="europe-west4", - project="my-project-id", - ) - - -@mock.patch("google.cloud.aiplatform.Model") -def test_lookup_model_when_no_models(mock_model, tmpdir): +def test_lookup_model_when_no_models(tmpdir): """ Checks that when there are no models and fail_on_model_found = False, lookup_model returns an empty string. - """ - mock_model.list.return_value = [] - exported_model_resource_name, _ = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - model=Model(uri=str(tmpdir)), - ) + Args: + tmpdir: built-in pytest tmpdir fixture + + Returns: + None + """ + with mock.patch("google.cloud.aiplatform.Model") as mock_model: + mock_model.list.return_value = [] + exported_model_resource_name, _ = lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=False, + model=Model(uri=str(tmpdir)), + ) print(exported_model_resource_name) assert exported_model_resource_name == "" -@mock.patch("google.cloud.aiplatform.Model") -def test_lookup_model_when_no_models_fail(mock_model, tmpdir): +def test_lookup_model_when_no_models_fail(tmpdir): """ Checks that when there are no models and fail_on_model_found = True, lookup_model raises a RuntimeError. + + Args: + tmpdir: built-in pytest tmpdir fixture + + Returns: + None """ - mock_model.list.return_value = [] + with mock.patch("google.cloud.aiplatform.Model") as mock_model: + mock_model.list.return_value = [] - # Verify that a ValueError is raised - with pytest.raises(RuntimeError): - lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=True, - model=Model(uri=str(tmpdir)), - ) + # Verify that a ValueError is raised + with pytest.raises(RuntimeError): + lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=True, + model=Model(uri=str(tmpdir)), + ) diff --git a/components/vertex-components/tests/test_model_batch_predict.py b/components/vertex-components/tests/test_model_batch_predict.py index 76bca67d..10d9e808 100644 --- a/components/vertex-components/tests/test_model_batch_predict.py +++ b/components/vertex-components/tests/test_model_batch_predict.py @@ -13,7 +13,7 @@ # limitations under the License. import json import pytest -from unittest import mock +from unittest.mock import Mock, patch from kfp.v2.dsl import Model from google.cloud.aiplatform_v1beta1.types.job_state import JobState @@ -30,19 +30,7 @@ "targetField": "col", } -mock_job1 = mock.Mock() -mock_job1.name = "mock-batch-job" -mock_job1.state = JobState.JOB_STATE_SUCCEEDED - -@mock.patch( - "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.create_batch_prediction_job", # noqa : E501 - return_value=mock_job1, -) -@mock.patch( - "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.get_batch_prediction_job", # noqa : E501 - return_value=mock_job1, -) @pytest.mark.parametrize( ( "source_format,destination_format,source_uri,monitoring_training_dataset," @@ -56,8 +44,6 @@ ], ) def test_model_batch_predict( - create_job, - get_job, tmpdir, source_format, destination_format, @@ -69,22 +55,37 @@ def test_model_batch_predict( """ Asserts model_batch_predict successfully creates requests given different arguments. """ + mock_resource_name = "mock-batch-job" + + mock_job1 = Mock() + mock_job1.name = mock_resource_name + mock_job1.state = JobState.JOB_STATE_SUCCEEDED + mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) - (gcp_resources,) = model_batch_predict( - model=mock_model, - job_display_name="", - project_location="", - project_id="", - source_uri=source_uri, - destination_uri=destination_format, - source_format=source_format, - destination_format=destination_format, - monitoring_training_dataset=monitoring_training_dataset, - monitoring_alert_email_addresses=monitoring_alert_email_addresses, - monitoring_skew_config=monitoring_skew_config, - ) + with patch( + "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.create_batch_prediction_job", # noqa: E501 + return_value=mock_job1, + ) as create_job, patch( + "google.cloud.aiplatform_v1beta1.services.job_service.JobServiceClient.get_batch_prediction_job", # noqa: E501 + return_value=mock_job1, + ) as get_job: + (gcp_resources,) = model_batch_predict( + model=mock_model, + job_display_name="", + project_location="", + project_id="", + source_uri=source_uri, + destination_uri=destination_format, + source_format=source_format, + destination_format=destination_format, + monitoring_training_dataset=monitoring_training_dataset, + monitoring_alert_email_addresses=monitoring_alert_email_addresses, + monitoring_skew_config=monitoring_skew_config, + ) create_job.assert_called_once() get_job.assert_called_once() - assert json.loads(gcp_resources)["resources"][0]["resourceUri"] == mock_job1.name + assert ( + json.loads(gcp_resources)["resources"][0]["resourceUri"] == mock_resource_name + ) diff --git a/components/vertex-components/tests/test_update_best_model.py b/components/vertex-components/tests/test_update_best_model.py index 4ef6ba21..bce9a9df 100644 --- a/components/vertex-components/tests/test_update_best_model.py +++ b/components/vertex-components/tests/test_update_best_model.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from unittest import mock +from unittest.mock import patch from kfp.v2.dsl import Model @@ -21,31 +21,26 @@ update_best_model = vertex_components.update_best_model.python_func -@mock.patch("google.cloud.aiplatform.Model") -@mock.patch("google.cloud.aiplatform.model_evaluation.ModelEvaluation") -@mock.patch("google.cloud.aiplatform.models.ModelRegistry") -@mock.patch("google.protobuf.json_format.MessageToDict") -def test_model_batch_predict( - mock_message_to_dict, - mock_model_registry, - mock_model_evaluation, - mock_model_class, - tmpdir, -): +def test_model_batch_predict(tmpdir): """ Asserts model_batch_predict successfully creates requests given different arguments. """ mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) mock_message = {"metrics": {"rmse": 0.01}} - mock_message_to_dict.return_value = mock_message - - (challenger_wins,) = update_best_model( - challenger=mock_model, - challenger_evaluation="", - parent_model="", - project_id="", - project_location="", - eval_metric="rmse", - eval_lower_is_better=True, - ) - assert not challenger_wins + + with patch("google.cloud.aiplatform.Model",), patch( + "google.cloud.aiplatform.model_evaluation.ModelEvaluation", + ), patch("google.cloud.aiplatform.models.ModelRegistry",), patch( + "google.protobuf.json_format.MessageToDict", return_value=mock_message + ): + + (challenger_wins,) = update_best_model( + challenger=mock_model, + challenger_evaluation="", + parent_model="", + project_id="", + project_location="", + eval_metric="rmse", + eval_lower_is_better=True, + ) + assert not challenger_wins diff --git a/pipelines/Pipfile b/pipelines/Pipfile index 9538e7fc..507b4e6d 100644 --- a/pipelines/Pipfile +++ b/pipelines/Pipfile @@ -14,7 +14,6 @@ bigquery-components = {editable = true, path = "./../components/bigquery-compone [dev-packages] pytest = ">=7.3.1,<8.0.0" pre-commit = ">=2.14.1,<3.0.0" -coverage = "==7.2.5" [requires] python_version = "3.7" diff --git a/pipelines/Pipfile.lock b/pipelines/Pipfile.lock index 42ce01df..92d8d064 100644 --- a/pipelines/Pipfile.lock +++ b/pipelines/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "2350f00a78fbfb02ef816a1ba3f5316c810e711a7cc685895cb135cb293d38cd" + "sha256": "c889b828537547fe3e76c0ca6f812f331bffdf13bfd8c72104145db94eb555e0" }, "pipfile-spec": 6, "requires": { @@ -192,11 +192,11 @@ }, "google-auth": { "hashes": [ - "sha256:55a395cdfd3f3dd3f649131d41f97c17b4ed8a2aac1be3502090c716314e8a37", - "sha256:d7a3249027e7f464fbbfd7ee8319a08ad09d2eea51578575c4bd360ffa049ccb" + "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc", + "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.18.1" + "version": "==2.17.3" }, "google-auth-httplib2": { "hashes": [ @@ -246,11 +246,11 @@ }, "google-cloud-resource-manager": { "hashes": [ - "sha256:41a2204532f084c707fde0bc1a9bc95c7e0b739d7072dd0b8a25106667a56184", - "sha256:c974fb6f9810476cf7b63ea89394c1a8df47f7f2dc2303e728bb74b500bcde67" + "sha256:26beb595b957972df50173f1d0fd51c00d280551eac73566017ebdda62b1616a", + "sha256:bfc3e60eb92e25ac562a9248bb8fc17e9bef04c3dc9f031ffbe0dfe28d919287" ], "markers": "python_version >= '3.7'", - "version": "==1.10.1" + "version": "==1.10.0" }, "google-cloud-storage": { "hashes": [ @@ -360,53 +360,53 @@ }, "grpcio": { "hashes": [ - "sha256:0212e2f7fdf7592e4b9d365087da30cb4d71e16a6f213120c89b4f8fb35a3ab3", - "sha256:09d4bfd84686cd36fd11fd45a0732c7628308d094b14d28ea74a81db0bce2ed3", - "sha256:1e623e0cf99a0ac114f091b3083a1848dbc64b0b99e181473b5a4a68d4f6f821", - "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea", - "sha256:2296356b5c9605b73ed6a52660b538787094dae13786ba53080595d52df13a98", - "sha256:2a1e601ee31ef30a9e2c601d0867e236ac54c922d32ed9f727b70dd5d82600d5", - "sha256:2be88c081e33f20630ac3343d8ad9f1125f32987968e9c8c75c051c9800896e8", - "sha256:33d40954199bddbb6a78f8f6f2b2082660f381cd2583ec860a6c2fa7c8400c08", - "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c", - "sha256:46a057329938b08e5f0e12ea3d7aed3ecb20a0c34c4a324ef34e00cecdb88a12", - "sha256:4864f99aac207e3e45c5e26c6cbb0ad82917869abc2f156283be86c05286485c", - "sha256:4c44e1a765b31e175c391f22e8fc73b2a2ece0e5e6ff042743d8109b5d2eff9f", - "sha256:4cb283f630624ebb16c834e5ac3d7880831b07cbe76cb08ab7a271eeaeb8943e", - "sha256:5008964885e8d23313c8e5ea0d44433be9bfd7e24482574e8cc43c02c02fc796", - "sha256:50a9f075eeda5097aa9a182bb3877fe1272875e45370368ac0ee16ab9e22d019", - "sha256:51630c92591d6d3fe488a7c706bd30a61594d144bac7dee20c8e1ce78294f474", - "sha256:5cc928cfe6c360c1df636cf7991ab96f059666ac7b40b75a769410cc6217df9c", - "sha256:61f7203e2767800edee7a1e1040aaaf124a35ce0c7fe0883965c6b762defe598", - "sha256:66233ccd2a9371158d96e05d082043d47dadb18cbb294dc5accfdafc2e6b02a7", - "sha256:70fcac7b94f4c904152809a050164650ac81c08e62c27aa9f156ac518029ebbe", - "sha256:714242ad0afa63a2e6dabd522ae22e1d76e07060b5af2ddda5474ba4f14c2c94", - "sha256:782f4f8662a2157c4190d0f99eaaebc602899e84fb1e562a944e5025929e351c", - "sha256:7fc2b4edb938c8faa4b3c3ea90ca0dd89b7565a049e8e4e11b77e60e4ed2cc05", - "sha256:881d058c5ccbea7cc2c92085a11947b572498a27ef37d3eef4887f499054dca8", - "sha256:89dde0ac72a858a44a2feb8e43dc68c0c66f7857a23f806e81e1b7cc7044c9cf", - "sha256:8cdbcbd687e576d48f7886157c95052825ca9948c0ed2afdc0134305067be88b", - "sha256:8d6192c37a30a115f4663592861f50e130caed33efc4eec24d92ec881c92d771", - "sha256:96a41817d2c763b1d0b32675abeb9179aa2371c72aefdf74b2d2b99a1b92417b", - "sha256:9bdbb7624d65dc0ed2ed8e954e79ab1724526f09b1efa88dcd9a1815bf28be5f", - "sha256:9bf88004fe086c786dc56ef8dd6cb49c026833fdd6f42cb853008bce3f907148", - "sha256:a08920fa1a97d4b8ee5db2f31195de4a9def1a91bc003544eb3c9e6b8977960a", - "sha256:a2f5a1f1080ccdc7cbaf1171b2cf384d852496fe81ddedeb882d42b85727f610", - "sha256:b04202453941a63b36876a7172b45366dc0cde10d5fd7855c0f4a4e673c0357a", - "sha256:b38b3de8cff5bc70f8f9c615f51b48eff7313fc9aca354f09f81b73036e7ddfa", - "sha256:b52d00d1793d290c81ad6a27058f5224a7d5f527867e5b580742e1bd211afeee", - "sha256:b74ae837368cfffeb3f6b498688a123e6b960951be4dec0e869de77e7fa0439e", - "sha256:be48496b0e00460717225e7680de57c38be1d8629dc09dadcd1b3389d70d942b", - "sha256:c0e3155fc5335ec7b3b70f15230234e529ca3607b20a562b6c75fb1b1218874c", - "sha256:c2392f5b5d84b71d853918687d806c1aa4308109e5ca158a16e16a6be71041eb", - "sha256:c72956972e4b508dd39fdc7646637a791a9665b478e768ffa5f4fe42123d5de1", - "sha256:dc80c9c6b608bf98066a038e0172013a49cfa9a08d53335aefefda2c64fc68f4", - "sha256:e416c8baf925b5a1aff31f7f5aecc0060b25d50cce3a5a7255dc5cf2f1d4e5eb", - "sha256:f8da84bbc61a4e92af54dc96344f328e5822d574f767e9b08e1602bb5ddc254a", - "sha256:f900ed4ad7a0f1f05d35f955e0943944d5a75f607a836958c6b8ab2a81730ef2", - "sha256:fd6c6c29717724acf9fc1847c4515d57e4dc12762452457b9cb37461f30a81bb" - ], - "version": "==1.54.2" + "sha256:02000b005bc8b72ff50c477b6431e8886b29961159e8b8d03c00b3dd9139baed", + "sha256:031bbd26656e0739e4b2c81c172155fb26e274b8d0312d67aefc730bcba915b6", + "sha256:1209d6b002b26e939e4c8ea37a3d5b4028eb9555394ea69fb1adbd4b61a10bb8", + "sha256:125ed35aa3868efa82eabffece6264bf638cfdc9f0cd58ddb17936684aafd0f8", + "sha256:1382bc499af92901c2240c4d540c74eae8a671e4fe9839bfeefdfcc3a106b5e2", + "sha256:16bca8092dd994f2864fdab278ae052fad4913f36f35238b2dd11af2d55a87db", + "sha256:1c59d899ee7160638613a452f9a4931de22623e7ba17897d8e3e348c2e9d8d0b", + "sha256:1d109df30641d050e009105f9c9ca5a35d01e34d2ee2a4e9c0984d392fd6d704", + "sha256:1fa7d6ddd33abbd3c8b3d7d07c56c40ea3d1891ce3cd2aa9fa73105ed5331866", + "sha256:21c4a1aae861748d6393a3ff7867473996c139a77f90326d9f4104bebb22d8b8", + "sha256:224166f06ccdaf884bf35690bf4272997c1405de3035d61384ccb5b25a4c1ca8", + "sha256:2262bd3512ba9e9f0e91d287393df6f33c18999317de45629b7bd46c40f16ba9", + "sha256:2585b3c294631a39b33f9f967a59b0fad23b1a71a212eba6bc1e3ca6e6eec9ee", + "sha256:27fb030a4589d2536daec5ff5ba2a128f4f155149efab578fe2de2cb21596d3d", + "sha256:30fbbce11ffeb4f9f91c13fe04899aaf3e9a81708bedf267bf447596b95df26b", + "sha256:3930669c9e6f08a2eed824738c3d5699d11cd47a0ecc13b68ed11595710b1133", + "sha256:3b170e441e91e4f321e46d3cc95a01cb307a4596da54aca59eb78ab0fc03754d", + "sha256:3db71c6f1ab688d8dfc102271cedc9828beac335a3a4372ec54b8bf11b43fd29", + "sha256:48cb7af77238ba16c77879009003f6b22c23425e5ee59cb2c4c103ec040638a5", + "sha256:49eace8ea55fbc42c733defbda1e4feb6d3844ecd875b01bb8b923709e0f5ec8", + "sha256:533eaf5b2a79a3c6f35cbd6a095ae99cac7f4f9c0e08bdcf86c130efd3c32adf", + "sha256:5942a3e05630e1ef5b7b5752e5da6582460a2e4431dae603de89fc45f9ec5aa9", + "sha256:62117486460c83acd3b5d85c12edd5fe20a374630475388cfc89829831d3eb79", + "sha256:650f5f2c9ab1275b4006707411bb6d6bc927886874a287661c3c6f332d4c068b", + "sha256:6dc1e2c9ac292c9a484ef900c568ccb2d6b4dfe26dfa0163d5bc815bb836c78d", + "sha256:73c238ef6e4b64272df7eec976bb016c73d3ab5a6c7e9cd906ab700523d312f3", + "sha256:775a2f70501370e5ba54e1ee3464413bff9bd85bd9a0b25c989698c44a6fb52f", + "sha256:860fcd6db7dce80d0a673a1cc898ce6bc3d4783d195bbe0e911bf8a62c93ff3f", + "sha256:87f47bf9520bba4083d65ab911f8f4c0ac3efa8241993edd74c8dd08ae87552f", + "sha256:960b176e0bb2b4afeaa1cd2002db1e82ae54c9b6e27ea93570a42316524e77cf", + "sha256:a7caf553ccaf715ec05b28c9b2ab2ee3fdb4036626d779aa09cf7cbf54b71445", + "sha256:a947d5298a0bbdd4d15671024bf33e2b7da79a70de600ed29ba7e0fef0539ebb", + "sha256:a97b0d01ae595c997c1d9d8249e2d2da829c2d8a4bdc29bb8f76c11a94915c9a", + "sha256:b7655f809e3420f80ce3bf89737169a9dce73238af594049754a1128132c0da4", + "sha256:c33744d0d1a7322da445c0fe726ea6d4e3ef2dfb0539eadf23dce366f52f546c", + "sha256:c55a9cf5cba80fb88c850915c865b8ed78d5e46e1f2ec1b27692f3eaaf0dca7e", + "sha256:d2f62fb1c914a038921677cfa536d645cb80e3dd07dc4859a3c92d75407b90a5", + "sha256:d8ae6e0df3a608e99ee1acafaafd7db0830106394d54571c1ece57f650124ce9", + "sha256:e355ee9da9c1c03f174efea59292b17a95e0b7b4d7d2a389265f731a9887d5a9", + "sha256:e3e526062c690517b42bba66ffe38aaf8bc99a180a78212e7b22baa86902f690", + "sha256:eb0807323572642ab73fd86fe53d88d843ce617dd1ddf430351ad0759809a0ae", + "sha256:ebff0738be0499d7db74d20dca9f22a7b27deae31e1bf92ea44924fd69eb6251", + "sha256:ed36e854449ff6c2f8ee145f94851fe171298e1e793f44d4f672c4a0d78064e7", + "sha256:ed3d458ded32ff3a58f157b60cc140c88f7ac8c506a1c567b2a9ee8a2fd2ce54", + "sha256:f4a7dca8ccd8023d916b900aa3c626f1bd181bd5b70159479b142f957ff420e4" + ], + "version": "==1.54.0" }, "grpcio-status": { "hashes": [ @@ -784,11 +784,11 @@ }, "setuptools": { "hashes": [ - "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", - "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" + "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", + "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" ], "markers": "python_version >= '3.7'", - "version": "==67.8.0" + "version": "==67.7.2" }, "shapely": { "hashes": [ @@ -1019,63 +1019,6 @@ "markers": "python_full_version >= '3.6.1'", "version": "==3.3.1" }, - "coverage": { - "hashes": [ - "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3", - "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a", - "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813", - "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0", - "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a", - "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd", - "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139", - "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b", - "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252", - "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790", - "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045", - "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce", - "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200", - "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718", - "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b", - "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f", - "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5", - "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade", - "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5", - "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a", - "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8", - "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33", - "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e", - "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c", - "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3", - "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969", - "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068", - "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2", - "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771", - "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed", - "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212", - "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614", - "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88", - "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3", - "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c", - "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84", - "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11", - "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1", - "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1", - "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e", - "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1", - "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd", - "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47", - "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a", - "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c", - "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31", - "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5", - "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6", - "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303", - "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5", - "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47" - ], - "index": "pypi", - "version": "==7.2.5" - }, "distlib": { "hashes": [ "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46", @@ -1125,11 +1068,11 @@ }, "nodeenv": { "hashes": [ - "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2", - "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec" + "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e", + "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==1.8.0" + "version": "==1.7.0" }, "packaging": { "hashes": [ @@ -1141,11 +1084,11 @@ }, "platformdirs": { "hashes": [ - "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f", - "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5" + "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4", + "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335" ], "markers": "python_version >= '3.7'", - "version": "==3.5.1" + "version": "==3.5.0" }, "pluggy": { "hashes": [ @@ -1208,11 +1151,11 @@ }, "setuptools": { "hashes": [ - "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", - "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" + "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", + "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" ], "markers": "python_version >= '3.7'", - "version": "==67.8.0" + "version": "==67.7.2" }, "tomli": { "hashes": [ diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index 693a3bd8..a5e2cbb7 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -20,7 +20,6 @@ dependencies = [ tests = [ "google-cloud-bigquery == 2.30.0", "pytest >= 7.3.1,<8.0.0", - "coverage = ==7.2.5" ] [build-system] From 386349daa990c925f50ed2a25cea8b5cdc95998b Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 13:50:15 +0100 Subject: [PATCH 070/238] style(README.md): add instructions for python version detection --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 0414b7c6..8c9651e1 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,13 @@ In a production MLOps solution, your ML pipelines need to be repeatable. So, we 1. Copy `env.sh.example` to `env.sh`, and update the environment variables in `env.sh` 1. Load the environment variables in `env.sh` by running `source env.sh` +please note: `poetry install` or `poetry add`, installs packages within the project's virtual environment. If you use pip directly, you might accidentally install packages globally or in the wrong environment, leading to conflicts or difficulties in managing dependencies. + +### Configuring poetry to detect python version using pyenv + +1. run in terminal `poetry config virtualenvs.prefer-active-python true` +1. Manage and install project's dependencies using `poetry install` + ### Deploying Cloud Infrastructure The cloud infrastructure is managed using Terraform and is defined in the [`terraform`](terraform) directory. There are three Terraform modules defined in [`terraform/modules`](terraform/modules): From ee462f024bf7d6aac6fdd8607b949ba7471f3935 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 13:51:56 +0100 Subject: [PATCH 071/238] style(Makefile): remove pip install poetry from setup command --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 09441314..a9438069 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ pre-commit: ## Runs the pre-commit checks over entire repo poetry run pre-commit run --all-files setup: ## Set up local environment for Python development on pipelines - @pip install poetry && \ + @poetry install && \ cd pipelines && \ poetry install --with dev From 7d18fa86e76f53406df413507e7463e969b159e5 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 15:39:06 +0100 Subject: [PATCH 072/238] build: revert python version from 3.9 to 3.7 --- .pre-commit-config.yaml | 2 +- .python-version | 2 +- cloudbuild/e2e-test.yaml | 2 +- cloudbuild/pr-checks.yaml | 2 +- cloudbuild/release.yaml | 2 +- cloudbuild/trigger-tests.yaml | 2 +- .../bigquery-components/.python-version | 2 +- components/bigquery-components/poetry.lock | 101 +- components/bigquery-components/pyproject.toml | 4 +- .../extract_bq_to_dataset.py | 2 +- components/vertex-components/.python-version | 2 +- components/vertex-components/poetry.lock | 101 +- components/vertex-components/pyproject.toml | 4 +- .../vertex_components/update_best_model.py | 2 +- pipelines/.python-version | 2 +- pipelines/Pipfile | 20 - pipelines/Pipfile.lock | 1250 ----------------- pipelines/poetry.lock | 171 ++- pipelines/pyproject.toml | 7 +- 19 files changed, 325 insertions(+), 1355 deletions(-) delete mode 100644 pipelines/Pipfile delete mode 100644 pipelines/Pipfile.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 66dc32d3..3bd0bc1e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ --- default_language_version: - python: python3.9 + python: python3.7 repos: - repo: https://github.com/gruntwork-io/pre-commit diff --git a/.python-version b/.python-version index 9f3d4c17..8a433dd7 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.9.16 +3.7.12 diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 82554d8e..3aa9a513 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -30,7 +30,7 @@ steps: # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines - - name: python:3.9 + - name: python:3.7 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/pr-checks.yaml b/cloudbuild/pr-checks.yaml index 969e7bbf..9f0f4347 100644 --- a/cloudbuild/pr-checks.yaml +++ b/cloudbuild/pr-checks.yaml @@ -17,7 +17,7 @@ steps: # Install poetry and deps, run pre-commit and unit tests # Then compile pipelines (to make sure they can compile) # need to run "git init" for pre-commit checks to work - - name: python:3.9 + - name: python:3.7 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index 9d0f4226..b5664ec0 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -15,7 +15,7 @@ steps: # Install poetry, install deps, compile pipelines - - name: python:3.9 + - name: python:3.7 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/trigger-tests.yaml b/cloudbuild/trigger-tests.yaml index 295e2c50..a3387438 100644 --- a/cloudbuild/trigger-tests.yaml +++ b/cloudbuild/trigger-tests.yaml @@ -13,7 +13,7 @@ # limitations under the License. --- steps: - - name: 'python:3.9' + - name: 'python:3.7' args: - '-c' - | diff --git a/components/bigquery-components/.python-version b/components/bigquery-components/.python-version index 9f3d4c17..8a433dd7 100644 --- a/components/bigquery-components/.python-version +++ b/components/bigquery-components/.python-version @@ -1 +1 @@ -3.9.16 +3.7.12 diff --git a/components/bigquery-components/poetry.lock b/components/bigquery-components/poetry.lock index 8a60aaaf..8f0f3273 100644 --- a/components/bigquery-components/poetry.lock +++ b/components/bigquery-components/poetry.lock @@ -24,6 +24,9 @@ files = [ {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, ] +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[docs,tests]", "pre-commit"] @@ -33,14 +36,14 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte [[package]] name = "cachetools" -version = "5.3.0" +version = "5.3.1" description = "Extensible memoizing collections and decorators" category = "main" optional = false -python-versions = "~=3.7" +python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, - {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, ] [[package]] @@ -154,6 +157,7 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "cloudpickle" @@ -181,21 +185,21 @@ files = [ [[package]] name = "deprecated" -version = "1.2.13" +version = "1.2.14" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ - {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, - {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, ] [package.dependencies] wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] [[package]] name = "docstring-parser" @@ -620,6 +624,46 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "importlib-metadata" +version = "6.6.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, + {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "importlib-resources" +version = "5.12.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, + {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -646,7 +690,11 @@ files = [ [package.dependencies] attrs = ">=17.4.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] @@ -685,6 +733,7 @@ requests-toolbelt = ">=0.8.0,<1" strip-hints = ">=0.1.8,<1" tabulate = ">=0.8.6,<1" typer = ">=0.3.2,<1.0" +typing-extensions = {version = ">=3.7.4,<5", markers = "python_version < \"3.9\""} uritemplate = ">=3.0.1,<4" urllib3 = "<2" @@ -778,6 +827,18 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] +[[package]] +name = "pkgutil-resolve-name" +version = "1.3.10" +description = "Resolve a name to an object." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, + {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, +] + [[package]] name = "pluggy" version = "1.0.0" @@ -790,6 +851,9 @@ files = [ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] @@ -991,6 +1055,7 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" @@ -1389,7 +1454,23 @@ files = [ {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, ] +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [metadata] lock-version = "2.0" -python-versions = ">=3.9,<3.11" -content-hash = "ec83532f673f942df8b19c490393de3a75bd4d1841ba3dc58958e39c311536de" +python-versions = ">=3.7,<3.11" +content-hash = "d7bc7cb1b7d234cc70d56b33a1253a6a52b0f6d9ab3114fd47ab47dbfcdadfa0" diff --git a/components/bigquery-components/pyproject.toml b/components/bigquery-components/pyproject.toml index ae6dc103..19db2207 100644 --- a/components/bigquery-components/pyproject.toml +++ b/components/bigquery-components/pyproject.toml @@ -7,11 +7,11 @@ readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.7", ] [tool.poetry.dependencies] -python = ">=3.9,<3.11" +python = ">=3.7,<3.11" kfp = "==1.8.21" [tool.poetry.group.dev.dependencies] diff --git a/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py b/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py index 10cd5bf4..3effe09a 100644 --- a/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py +++ b/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py @@ -16,7 +16,7 @@ @component( - base_image="python:3.9", + base_image="python:3.7", packages_to_install=["google-cloud-bigquery==2.30.0"], ) def extract_bq_to_dataset( diff --git a/components/vertex-components/.python-version b/components/vertex-components/.python-version index 9f3d4c17..8a433dd7 100644 --- a/components/vertex-components/.python-version +++ b/components/vertex-components/.python-version @@ -1 +1 @@ -3.9.16 +3.7.12 diff --git a/components/vertex-components/poetry.lock b/components/vertex-components/poetry.lock index 8a328b03..ca09c083 100644 --- a/components/vertex-components/poetry.lock +++ b/components/vertex-components/poetry.lock @@ -24,6 +24,9 @@ files = [ {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, ] +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[docs,tests]", "pre-commit"] @@ -33,14 +36,14 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte [[package]] name = "cachetools" -version = "5.3.0" +version = "5.3.1" description = "Extensible memoizing collections and decorators" category = "main" optional = false -python-versions = "~=3.7" +python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, - {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, ] [[package]] @@ -154,6 +157,7 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "cloudpickle" @@ -181,21 +185,21 @@ files = [ [[package]] name = "deprecated" -version = "1.2.13" +version = "1.2.14" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ - {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, - {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, ] [package.dependencies] wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] [[package]] name = "docstring-parser" @@ -737,6 +741,46 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "importlib-metadata" +version = "6.6.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, + {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "importlib-resources" +version = "5.12.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, + {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -763,7 +807,11 @@ files = [ [package.dependencies] attrs = ">=17.4.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] @@ -802,6 +850,7 @@ requests-toolbelt = ">=0.8.0,<1" strip-hints = ">=0.1.8,<1" tabulate = ">=0.8.6,<1" typer = ">=0.3.2,<1.0" +typing-extensions = {version = ">=3.7.4,<5", markers = "python_version < \"3.9\""} uritemplate = ">=3.0.1,<4" urllib3 = "<2" @@ -895,6 +944,18 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] +[[package]] +name = "pkgutil-resolve-name" +version = "1.3.10" +description = "Resolve a name to an object." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, + {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, +] + [[package]] name = "pluggy" version = "1.0.0" @@ -907,6 +968,9 @@ files = [ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] @@ -1108,6 +1172,7 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" @@ -1563,7 +1628,23 @@ files = [ {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, ] +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [metadata] lock-version = "2.0" -python-versions = ">=3.9,<3.11" -content-hash = "1f97617d2b92c562860cb6db2c44356f2a71e8c3444e15edc0a798edc8f5f564" +python-versions = ">=3.7,<3.11" +content-hash = "9ab64756b47c22efb2437659a3a7b8ca9a5b5c019f86217a03bdfbfb620d83ee" diff --git a/components/vertex-components/pyproject.toml b/components/vertex-components/pyproject.toml index 62fe0693..91f212a6 100644 --- a/components/vertex-components/pyproject.toml +++ b/components/vertex-components/pyproject.toml @@ -7,11 +7,11 @@ readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.7", ] [tool.poetry.dependencies] -python = ">=3.9,<3.11" +python = ">=3.7,<3.11" kfp = "==1.8.21" [tool.poetry.group.dev.dependencies] diff --git a/components/vertex-components/src/vertex_components/update_best_model.py b/components/vertex-components/src/vertex_components/update_best_model.py index 14762906..eeb45461 100644 --- a/components/vertex-components/src/vertex_components/update_best_model.py +++ b/components/vertex-components/src/vertex_components/update_best_model.py @@ -17,7 +17,7 @@ @component( - base_image="python:3.9", + base_image="python:3.7", packages_to_install=["google-cloud-aiplatform==1.24.1"], ) def update_best_model( diff --git a/pipelines/.python-version b/pipelines/.python-version index 9f3d4c17..8a433dd7 100644 --- a/pipelines/.python-version +++ b/pipelines/.python-version @@ -1 +1 @@ -3.9.16 +3.7.12 diff --git a/pipelines/Pipfile b/pipelines/Pipfile deleted file mode 100644 index 9538e7fc..00000000 --- a/pipelines/Pipfile +++ /dev/null @@ -1,20 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -kfp = "==1.8.21" -google-cloud-aiplatform = "==1.24.1" -google-cloud-pipeline-components = "==1.0.42" -Jinja2 = ">=3.0.1,<4.0.0" -vertex-components = {editable = true, path = "./../components/vertex-components"} -bigquery-components = {editable = true, path = "./../components/bigquery-components"} - -[dev-packages] -pytest = ">=7.3.1,<8.0.0" -pre-commit = ">=2.14.1,<3.0.0" -coverage = "==7.2.5" - -[requires] -python_version = "3.7" diff --git a/pipelines/Pipfile.lock b/pipelines/Pipfile.lock deleted file mode 100644 index 42ce01df..00000000 --- a/pipelines/Pipfile.lock +++ /dev/null @@ -1,1250 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "2350f00a78fbfb02ef816a1ba3f5316c810e711a7cc685895cb135cb293d38cd" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.7" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "absl-py": { - "hashes": [ - "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47", - "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d" - ], - "markers": "python_version >= '3.6'", - "version": "==1.4.0" - }, - "attrs": { - "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" - }, - "bigquery-components": { - "editable": true, - "path": "./../components/bigquery-components" - }, - "cachetools": { - "hashes": [ - "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14", - "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4" - ], - "markers": "python_version ~= '3.7'", - "version": "==5.3.0" - }, - "certifi": { - "hashes": [ - "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", - "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.5.7" - }, - "charset-normalizer": { - "hashes": [ - "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", - "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", - "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", - "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", - "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", - "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", - "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", - "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", - "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", - "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", - "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", - "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", - "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", - "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", - "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", - "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", - "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", - "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", - "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", - "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", - "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", - "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", - "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", - "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", - "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", - "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", - "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", - "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", - "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", - "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", - "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", - "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", - "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", - "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", - "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", - "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", - "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", - "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", - "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", - "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", - "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", - "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", - "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", - "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", - "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", - "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", - "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", - "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", - "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", - "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", - "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", - "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", - "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", - "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", - "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", - "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", - "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", - "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", - "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", - "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", - "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", - "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", - "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", - "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", - "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", - "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", - "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", - "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", - "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", - "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", - "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", - "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", - "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", - "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", - "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.1.0" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "cloudpickle": { - "hashes": [ - "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f", - "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5" - ], - "markers": "python_version >= '3.6'", - "version": "==2.2.1" - }, - "deprecated": { - "hashes": [ - "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d", - "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.13" - }, - "docstring-parser": { - "hashes": [ - "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682", - "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9" - ], - "markers": "python_version >= '3.6' and python_version < '4.0'", - "version": "==0.15" - }, - "fire": { - "hashes": [ - "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6" - ], - "version": "==0.5.0" - }, - "google-api-core": { - "extras": [ - "grpc" - ], - "hashes": [ - "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22", - "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.11.0" - }, - "google-api-python-client": { - "hashes": [ - "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9", - "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.12.11" - }, - "google-auth": { - "hashes": [ - "sha256:55a395cdfd3f3dd3f649131d41f97c17b4ed8a2aac1be3502090c716314e8a37", - "sha256:d7a3249027e7f464fbbfd7ee8319a08ad09d2eea51578575c4bd360ffa049ccb" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.18.1" - }, - "google-auth-httplib2": { - "hashes": [ - "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10", - "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac" - ], - "version": "==0.1.0" - }, - "google-cloud-aiplatform": { - "hashes": [ - "sha256:0ce9e97bf5c977397e52b3b7c4dc78610c135fbde11a60a6c0b77a4fdf776400", - "sha256:942765a6bad97e46e262dd6599dc5f171663ce952130e0b0b2eb97e0b1b98bfd" - ], - "index": "pypi", - "version": "==1.24.1" - }, - "google-cloud-bigquery": { - "hashes": [ - "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df", - "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a" - ], - "markers": "python_version >= '3.7'", - "version": "==3.10.0" - }, - "google-cloud-core": { - "hashes": [ - "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe", - "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.2" - }, - "google-cloud-notebooks": { - "hashes": [ - "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab", - "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e" - ], - "markers": "python_version >= '3.7'", - "version": "==1.7.0" - }, - "google-cloud-pipeline-components": { - "hashes": [ - "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623" - ], - "index": "pypi", - "version": "==1.0.42" - }, - "google-cloud-resource-manager": { - "hashes": [ - "sha256:41a2204532f084c707fde0bc1a9bc95c7e0b739d7072dd0b8a25106667a56184", - "sha256:c974fb6f9810476cf7b63ea89394c1a8df47f7f2dc2303e728bb74b500bcde67" - ], - "markers": "python_version >= '3.7'", - "version": "==1.10.1" - }, - "google-cloud-storage": { - "hashes": [ - "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94", - "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3" - ], - "markers": "python_version >= '3.7'", - "version": "==2.9.0" - }, - "google-crc32c": { - "hashes": [ - "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", - "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", - "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", - "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", - "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", - "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", - "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", - "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", - "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", - "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", - "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", - "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", - "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", - "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", - "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", - "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", - "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", - "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", - "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", - "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", - "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", - "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", - "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", - "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", - "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", - "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", - "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", - "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", - "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", - "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", - "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", - "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", - "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", - "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", - "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", - "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", - "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", - "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", - "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", - "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", - "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", - "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", - "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", - "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", - "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", - "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", - "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", - "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", - "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", - "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", - "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", - "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", - "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", - "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", - "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", - "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", - "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", - "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", - "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", - "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", - "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", - "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", - "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", - "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", - "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", - "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", - "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", - "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.0" - }, - "google-resumable-media": { - "hashes": [ - "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93", - "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.0" - }, - "googleapis-common-protos": { - "hashes": [ - "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44", - "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f" - ], - "markers": "python_version >= '3.7'", - "version": "==1.59.0" - }, - "grpc-google-iam-v1": { - "hashes": [ - "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f", - "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c" - ], - "markers": "python_version >= '3.7'", - "version": "==0.12.6" - }, - "grpcio": { - "hashes": [ - "sha256:0212e2f7fdf7592e4b9d365087da30cb4d71e16a6f213120c89b4f8fb35a3ab3", - "sha256:09d4bfd84686cd36fd11fd45a0732c7628308d094b14d28ea74a81db0bce2ed3", - "sha256:1e623e0cf99a0ac114f091b3083a1848dbc64b0b99e181473b5a4a68d4f6f821", - "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea", - "sha256:2296356b5c9605b73ed6a52660b538787094dae13786ba53080595d52df13a98", - "sha256:2a1e601ee31ef30a9e2c601d0867e236ac54c922d32ed9f727b70dd5d82600d5", - "sha256:2be88c081e33f20630ac3343d8ad9f1125f32987968e9c8c75c051c9800896e8", - "sha256:33d40954199bddbb6a78f8f6f2b2082660f381cd2583ec860a6c2fa7c8400c08", - "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c", - "sha256:46a057329938b08e5f0e12ea3d7aed3ecb20a0c34c4a324ef34e00cecdb88a12", - "sha256:4864f99aac207e3e45c5e26c6cbb0ad82917869abc2f156283be86c05286485c", - "sha256:4c44e1a765b31e175c391f22e8fc73b2a2ece0e5e6ff042743d8109b5d2eff9f", - "sha256:4cb283f630624ebb16c834e5ac3d7880831b07cbe76cb08ab7a271eeaeb8943e", - "sha256:5008964885e8d23313c8e5ea0d44433be9bfd7e24482574e8cc43c02c02fc796", - "sha256:50a9f075eeda5097aa9a182bb3877fe1272875e45370368ac0ee16ab9e22d019", - "sha256:51630c92591d6d3fe488a7c706bd30a61594d144bac7dee20c8e1ce78294f474", - "sha256:5cc928cfe6c360c1df636cf7991ab96f059666ac7b40b75a769410cc6217df9c", - "sha256:61f7203e2767800edee7a1e1040aaaf124a35ce0c7fe0883965c6b762defe598", - "sha256:66233ccd2a9371158d96e05d082043d47dadb18cbb294dc5accfdafc2e6b02a7", - "sha256:70fcac7b94f4c904152809a050164650ac81c08e62c27aa9f156ac518029ebbe", - "sha256:714242ad0afa63a2e6dabd522ae22e1d76e07060b5af2ddda5474ba4f14c2c94", - "sha256:782f4f8662a2157c4190d0f99eaaebc602899e84fb1e562a944e5025929e351c", - "sha256:7fc2b4edb938c8faa4b3c3ea90ca0dd89b7565a049e8e4e11b77e60e4ed2cc05", - "sha256:881d058c5ccbea7cc2c92085a11947b572498a27ef37d3eef4887f499054dca8", - "sha256:89dde0ac72a858a44a2feb8e43dc68c0c66f7857a23f806e81e1b7cc7044c9cf", - "sha256:8cdbcbd687e576d48f7886157c95052825ca9948c0ed2afdc0134305067be88b", - "sha256:8d6192c37a30a115f4663592861f50e130caed33efc4eec24d92ec881c92d771", - "sha256:96a41817d2c763b1d0b32675abeb9179aa2371c72aefdf74b2d2b99a1b92417b", - "sha256:9bdbb7624d65dc0ed2ed8e954e79ab1724526f09b1efa88dcd9a1815bf28be5f", - "sha256:9bf88004fe086c786dc56ef8dd6cb49c026833fdd6f42cb853008bce3f907148", - "sha256:a08920fa1a97d4b8ee5db2f31195de4a9def1a91bc003544eb3c9e6b8977960a", - "sha256:a2f5a1f1080ccdc7cbaf1171b2cf384d852496fe81ddedeb882d42b85727f610", - "sha256:b04202453941a63b36876a7172b45366dc0cde10d5fd7855c0f4a4e673c0357a", - "sha256:b38b3de8cff5bc70f8f9c615f51b48eff7313fc9aca354f09f81b73036e7ddfa", - "sha256:b52d00d1793d290c81ad6a27058f5224a7d5f527867e5b580742e1bd211afeee", - "sha256:b74ae837368cfffeb3f6b498688a123e6b960951be4dec0e869de77e7fa0439e", - "sha256:be48496b0e00460717225e7680de57c38be1d8629dc09dadcd1b3389d70d942b", - "sha256:c0e3155fc5335ec7b3b70f15230234e529ca3607b20a562b6c75fb1b1218874c", - "sha256:c2392f5b5d84b71d853918687d806c1aa4308109e5ca158a16e16a6be71041eb", - "sha256:c72956972e4b508dd39fdc7646637a791a9665b478e768ffa5f4fe42123d5de1", - "sha256:dc80c9c6b608bf98066a038e0172013a49cfa9a08d53335aefefda2c64fc68f4", - "sha256:e416c8baf925b5a1aff31f7f5aecc0060b25d50cce3a5a7255dc5cf2f1d4e5eb", - "sha256:f8da84bbc61a4e92af54dc96344f328e5822d574f767e9b08e1602bb5ddc254a", - "sha256:f900ed4ad7a0f1f05d35f955e0943944d5a75f607a836958c6b8ab2a81730ef2", - "sha256:fd6c6c29717724acf9fc1847c4515d57e4dc12762452457b9cb37461f30a81bb" - ], - "version": "==1.54.2" - }, - "grpcio-status": { - "hashes": [ - "sha256:2154fdb8aad20452488712be6879657b508115ca06139fde8897ea8e9bc79367", - "sha256:c9ce3213e84c6fd8801c31aca3ea4a6b3453eaa40b93a6c0a23ea8999808fa00" - ], - "markers": "python_version >= '3.6'", - "version": "==1.47.0" - }, - "httplib2": { - "hashes": [ - "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", - "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.22.0" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "importlib-metadata": { - "hashes": [ - "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed", - "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705" - ], - "markers": "python_version < '3.8'", - "version": "==6.6.0" - }, - "importlib-resources": { - "hashes": [ - "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6", - "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a" - ], - "markers": "python_version < '3.9'", - "version": "==5.12.0" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "index": "pypi", - "version": "==3.1.2" - }, - "jsonschema": { - "hashes": [ - "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6" - ], - "markers": "python_version >= '3.7'", - "version": "==4.17.3" - }, - "kfp": { - "hashes": [ - "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c" - ], - "index": "pypi", - "version": "==1.8.21" - }, - "kfp-pipeline-spec": { - "hashes": [ - "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==0.1.16" - }, - "kfp-server-api": { - "hashes": [ - "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de" - ], - "version": "==1.8.5" - }, - "kubernetes": { - "hashes": [ - "sha256:213befbb4e5aed95f94950c7eed0c2322fc5a2f8f40932e58d28fdd42d90836c", - "sha256:eb42333dad0bb5caf4e66460c6a4a1a36f0f057a040f35018f6c05a699baed86" - ], - "markers": "python_version >= '3.6'", - "version": "==25.3.0" - }, - "markupsafe": { - "hashes": [ - "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", - "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", - "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", - "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", - "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", - "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", - "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", - "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", - "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", - "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", - "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", - "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", - "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", - "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", - "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", - "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", - "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", - "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", - "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", - "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", - "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", - "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", - "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", - "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", - "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", - "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", - "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", - "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", - "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", - "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", - "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", - "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", - "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", - "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", - "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", - "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", - "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", - "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", - "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", - "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", - "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", - "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", - "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", - "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", - "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", - "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", - "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", - "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", - "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", - "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.2" - }, - "oauthlib": { - "hashes": [ - "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", - "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918" - ], - "markers": "python_version >= '3.6'", - "version": "==3.2.2" - }, - "packaging": { - "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1" - }, - "pkgutil-resolve-name": { - "hashes": [ - "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174", - "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e" - ], - "markers": "python_version < '3.9'", - "version": "==1.3.10" - }, - "proto-plus": { - "hashes": [ - "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165", - "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d" - ], - "markers": "python_version >= '3.6'", - "version": "==1.22.2" - }, - "protobuf": { - "hashes": [ - "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7", - "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c", - "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2", - "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b", - "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050", - "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9", - "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7", - "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454", - "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480", - "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469", - "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c", - "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e", - "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db", - "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905", - "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b", - "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86", - "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4", - "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402", - "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7", - "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4", - "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99", - "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee" - ], - "markers": "python_version >= '3.7'", - "version": "==3.20.3" - }, - "pyasn1": { - "hashes": [ - "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57", - "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.5.0" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", - "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.3.0" - }, - "pydantic": { - "hashes": [ - "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e", - "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6", - "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd", - "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca", - "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b", - "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a", - "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245", - "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d", - "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee", - "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1", - "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3", - "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d", - "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5", - "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914", - "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd", - "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1", - "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e", - "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e", - "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a", - "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd", - "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f", - "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209", - "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d", - "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a", - "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143", - "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918", - "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52", - "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e", - "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f", - "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e", - "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb", - "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe", - "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe", - "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d", - "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209", - "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af" - ], - "markers": "python_version >= '3.7'", - "version": "==1.10.7" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_version >= '3.1'", - "version": "==3.0.9" - }, - "pyrsistent": { - "hashes": [ - "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8", - "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440", - "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a", - "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c", - "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3", - "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393", - "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9", - "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da", - "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf", - "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64", - "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a", - "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3", - "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98", - "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2", - "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8", - "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf", - "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc", - "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7", - "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28", - "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2", - "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b", - "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a", - "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64", - "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19", - "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1", - "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9", - "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c" - ], - "markers": "python_version >= '3.7'", - "version": "==0.19.3" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pyyaml": { - "hashes": [ - "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", - "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", - "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", - "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", - "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", - "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", - "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", - "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", - "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", - "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", - "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", - "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", - "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", - "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", - "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", - "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", - "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", - "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", - "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", - "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", - "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", - "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", - "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", - "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", - "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", - "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", - "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", - "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", - "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.4.1" - }, - "requests": { - "hashes": [ - "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294", - "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4" - ], - "markers": "python_version >= '3.7'", - "version": "==2.30.0" - }, - "requests-oauthlib": { - "hashes": [ - "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", - "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.3.1" - }, - "requests-toolbelt": { - "hashes": [ - "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7", - "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.1" - }, - "rsa": { - "hashes": [ - "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", - "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" - ], - "markers": "python_version >= '3.6'", - "version": "==4.9" - }, - "setuptools": { - "hashes": [ - "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", - "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" - ], - "markers": "python_version >= '3.7'", - "version": "==67.8.0" - }, - "shapely": { - "hashes": [ - "sha256:02dd5d7dc6e46515d88874134dc8fcdc65826bca93c3eecee59d1910c42c1b17", - "sha256:0b4ee3132ee90f07d63db3aea316c4c065ed7a26231458dda0874414a09d6ba3", - "sha256:0d885cb0cf670c1c834df3f371de8726efdf711f18e2a75da5cfa82843a7ab65", - "sha256:147066da0be41b147a61f8eb805dea3b13709dbc873a431ccd7306e24d712bc0", - "sha256:21776184516a16bf82a0c3d6d6a312b3cd15a4cabafc61ee01cf2714a82e8396", - "sha256:2e0a8c2e55f1be1312b51c92b06462ea89e6bb703fab4b114e7a846d941cfc40", - "sha256:2fd15397638df291c427a53d641d3e6fd60458128029c8c4f487190473a69a91", - "sha256:3480657460e939f45a7d359ef0e172a081f249312557fe9aa78c4fd3a362d993", - "sha256:370b574c78dc5af3a198a6da5d9b3d7c04654bd2ef7e80e80a3a0992dfb2d9cd", - "sha256:38f0fbbcb8ca20c16451c966c1f527cc43968e121c8a048af19ed3e339a921cd", - "sha256:4728666fff8cccc65a07448cae72c75a8773fea061c3f4f139c44adc429b18c3", - "sha256:48dcfffb9e225c0481120f4bdf622131c8c95f342b00b158cdbe220edbbe20b6", - "sha256:4b47bb6f9369e8bf3e6dbd33e6a25a47ee02b2874792a529fe04a49bf8bc0df6", - "sha256:532a55ee2a6c52d23d6f7d1567c8f0473635f3b270262c44e1b0c88096827e22", - "sha256:5d7f85c2d35d39ff53c9216bc76b7641c52326f7e09aaad1789a3611a0f812f2", - "sha256:65b21243d8f6bcd421210daf1fabb9de84de2c04353c5b026173b88d17c1a581", - "sha256:66bdac74fbd1d3458fa787191a90fa0ae610f09e2a5ec398c36f968cc0ed743f", - "sha256:6d388c0c1bd878ed1af4583695690aa52234b02ed35f93a1c8486ff52a555838", - "sha256:6fe855e7d45685926b6ba00aaeb5eba5862611f7465775dacd527e081a8ced6d", - "sha256:753ed0e21ab108bd4282405b9b659f2e985e8502b1a72b978eaa51d3496dee19", - "sha256:783bad5f48e2708a0e2f695a34ed382e4162c795cb2f0368b39528ac1d6db7ed", - "sha256:78fb9d929b8ee15cfd424b6c10879ce1907f24e05fb83310fc47d2cd27088e40", - "sha256:84010db15eb364a52b74ea8804ef92a6a930dfc1981d17a369444b6ddec66efd", - "sha256:89164e7a9776a19e29f01369a98529321994e2e4d852b92b7e01d4d9804c55bf", - "sha256:8d086591f744be483b34628b391d741e46f2645fe37594319e0a673cc2c26bcf", - "sha256:8e59817b0fe63d34baedaabba8c393c0090f061917d18fc0bcc2f621937a8f73", - "sha256:99a2f0da0109e81e0c101a2b4cd8412f73f5f299e7b5b2deaf64cd2a100ac118", - "sha256:99ab0ddc05e44acabdbe657c599fdb9b2d82e86c5493bdae216c0c4018a82dee", - "sha256:a23ef3882d6aa203dd3623a3d55d698f59bfbd9f8a3bfed52c2da05a7f0f8640", - "sha256:a354199219c8d836f280b88f2c5102c81bb044ccea45bd361dc38a79f3873714", - "sha256:a74631e511153366c6dbe3229fa93f877e3c87ea8369cd00f1d38c76b0ed9ace", - "sha256:ab38f7b5196ace05725e407cb8cab9ff66edb8e6f7bb36a398e8f73f52a7aaa2", - "sha256:adcf8a11b98af9375e32bff91de184f33a68dc48b9cb9becad4f132fa25cfa3c", - "sha256:b65f5d530ba91e49ffc7c589255e878d2506a8b96ffce69d3b7c4500a9a9eaf8", - "sha256:be9423d5a3577ac2e92c7e758bd8a2b205f5e51a012177a590bc46fc51eb4834", - "sha256:c2822111ddc5bcfb116e6c663e403579d0fe3f147d2a97426011a191c43a7458", - "sha256:c6a9a4a31cd6e86d0fbe8473ceed83d4fe760b19d949fb557ef668defafea0f6", - "sha256:d048f93e42ba578b82758c15d8ae037d08e69d91d9872bca5a1895b118f4e2b0", - "sha256:d8a2b2a65fa7f97115c1cd989fe9d6f39281ca2a8a014f1d4904c1a6e34d7f25", - "sha256:e9c30b311de2513555ab02464ebb76115d242842b29c412f5a9aa0cac57be9f6", - "sha256:ec14ceca36f67cb48b34d02d7f65a9acae15cd72b48e303531893ba4a960f3ea", - "sha256:ef3be705c3eac282a28058e6c6e5503419b250f482320df2172abcbea642c831" - ], - "markers": "python_version >= '3.6'", - "version": "==1.8.5.post1" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "strip-hints": { - "hashes": [ - "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f" - ], - "version": "==0.1.10" - }, - "tabulate": { - "hashes": [ - "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", - "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" - ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" - }, - "termcolor": { - "hashes": [ - "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475", - "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.0" - }, - "typer": { - "hashes": [ - "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2", - "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee" - ], - "markers": "python_version >= '3.6'", - "version": "==0.9.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", - "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" - ], - "markers": "python_version < '3.9'", - "version": "==4.5.0" - }, - "uritemplate": { - "hashes": [ - "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f", - "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.0.1" - }, - "urllib3": { - "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" - }, - "vertex-components": { - "editable": true, - "path": "./../components/vertex-components" - }, - "websocket-client": { - "hashes": [ - "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40", - "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.1" - }, - "wheel": { - "hashes": [ - "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873", - "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247" - ], - "markers": "python_version >= '3.7'", - "version": "==0.40.0" - }, - "wrapt": { - "hashes": [ - "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", - "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", - "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", - "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", - "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", - "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", - "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", - "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", - "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", - "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", - "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", - "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", - "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", - "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", - "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", - "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", - "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", - "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", - "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", - "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", - "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", - "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", - "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", - "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", - "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", - "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", - "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", - "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", - "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", - "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", - "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", - "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", - "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", - "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", - "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", - "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", - "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", - "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", - "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", - "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", - "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", - "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", - "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", - "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", - "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", - "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", - "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", - "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", - "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", - "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", - "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", - "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", - "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", - "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", - "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", - "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", - "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", - "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", - "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", - "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", - "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", - "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", - "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", - "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", - "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", - "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", - "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", - "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", - "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", - "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", - "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", - "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", - "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", - "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", - "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.15.0" - }, - "zipp": { - "hashes": [ - "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", - "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556" - ], - "markers": "python_version < '3.10'", - "version": "==3.15.0" - } - }, - "develop": { - "cfgv": { - "hashes": [ - "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426", - "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.3.1" - }, - "coverage": { - "hashes": [ - "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3", - "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a", - "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813", - "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0", - "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a", - "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd", - "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139", - "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b", - "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252", - "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790", - "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045", - "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce", - "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200", - "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718", - "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b", - "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f", - "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5", - "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade", - "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5", - "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a", - "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8", - "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33", - "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e", - "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c", - "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3", - "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969", - "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068", - "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2", - "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771", - "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed", - "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212", - "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614", - "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88", - "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3", - "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c", - "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84", - "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11", - "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1", - "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1", - "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e", - "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1", - "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd", - "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47", - "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a", - "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c", - "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31", - "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5", - "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6", - "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303", - "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5", - "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47" - ], - "index": "pypi", - "version": "==7.2.5" - }, - "distlib": { - "hashes": [ - "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46", - "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e" - ], - "version": "==0.3.6" - }, - "exceptiongroup": { - "hashes": [ - "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e", - "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785" - ], - "markers": "python_version < '3.11'", - "version": "==1.1.1" - }, - "filelock": { - "hashes": [ - "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9", - "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718" - ], - "markers": "python_version >= '3.7'", - "version": "==3.12.0" - }, - "identify": { - "hashes": [ - "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4", - "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.24" - }, - "importlib-metadata": { - "hashes": [ - "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed", - "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705" - ], - "markers": "python_version < '3.8'", - "version": "==6.6.0" - }, - "iniconfig": { - "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" - }, - "nodeenv": { - "hashes": [ - "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2", - "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==1.8.0" - }, - "packaging": { - "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1" - }, - "platformdirs": { - "hashes": [ - "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f", - "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5" - ], - "markers": "python_version >= '3.7'", - "version": "==3.5.1" - }, - "pluggy": { - "hashes": [ - "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", - "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" - }, - "pre-commit": { - "hashes": [ - "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658", - "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad" - ], - "index": "pypi", - "version": "==2.21.0" - }, - "pytest": { - "hashes": [ - "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362", - "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3" - ], - "index": "pypi", - "version": "==7.3.1" - }, - "pyyaml": { - "hashes": [ - "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", - "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", - "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", - "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", - "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", - "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", - "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", - "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", - "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", - "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", - "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", - "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", - "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", - "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", - "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", - "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", - "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", - "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", - "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", - "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", - "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", - "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", - "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", - "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", - "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", - "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", - "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", - "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", - "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==5.4.1" - }, - "setuptools": { - "hashes": [ - "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", - "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" - ], - "markers": "python_version >= '3.7'", - "version": "==67.8.0" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11'", - "version": "==2.0.1" - }, - "typing-extensions": { - "hashes": [ - "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", - "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" - ], - "markers": "python_version < '3.9'", - "version": "==4.5.0" - }, - "virtualenv": { - "hashes": [ - "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e", - "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924" - ], - "markers": "python_version >= '3.7'", - "version": "==20.23.0" - }, - "zipp": { - "hashes": [ - "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", - "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556" - ], - "markers": "python_version < '3.10'", - "version": "==3.15.0" - } - } -} diff --git a/pipelines/poetry.lock b/pipelines/poetry.lock index b04293b3..1069ab60 100644 --- a/pipelines/poetry.lock +++ b/pipelines/poetry.lock @@ -24,6 +24,9 @@ files = [ {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, ] +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[docs,tests]", "pre-commit"] @@ -37,15 +40,11 @@ version = "0.1.0" description = "Bigquery components for vertex pipelines" category = "main" optional = false -python-versions = ">=3.9,<3.11" +python-versions = ">=3.7,<3.11" files = [] develop = true [package.dependencies] -google-cloud-aiplatform = "1.24.1" -google-cloud-bigquery = "2.30.0" -google-cloud-pipeline-components = "1.0.42" -Jinja2 = ">=3.0.1,<4.0.0" kfp = "==1.8.21" [package.source] @@ -54,14 +53,14 @@ url = "../components/bigquery-components" [[package]] name = "cachetools" -version = "5.3.0" +version = "5.3.1" description = "Extensible memoizing collections and decorators" category = "main" optional = false -python-versions = "~=3.7" +python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, - {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, ] [[package]] @@ -187,6 +186,7 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "cloudpickle" @@ -214,21 +214,21 @@ files = [ [[package]] name = "deprecated" -version = "1.2.13" +version = "1.2.14" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ - {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, - {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, ] [package.dependencies] wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] [[package]] name = "distlib" @@ -258,7 +258,7 @@ files = [ name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -347,20 +347,20 @@ uritemplate = ">=3.0.0,<4dev" [[package]] name = "google-auth" -version = "2.18.1" +version = "2.19.0" description = "Google Authentication Library" category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +python-versions = ">=3.6" files = [ - {file = "google-auth-2.18.1.tar.gz", hash = "sha256:d7a3249027e7f464fbbfd7ee8319a08ad09d2eea51578575c4bd360ffa049ccb"}, - {file = "google_auth-2.18.1-py2.py3-none-any.whl", hash = "sha256:55a395cdfd3f3dd3f649131d41f97c17b4ed8a2aac1be3502090c716314e8a37"}, + {file = "google-auth-2.19.0.tar.gz", hash = "sha256:f39d528077ac540793dd3c22a8706178f157642a67d874db25c640b7fead277e"}, + {file = "google_auth-2.19.0-py2.py3-none-any.whl", hash = "sha256:be617bfaf77774008e9d177573f782e109188c8a64ae6e744285df5cea3e7df6"}, ] [package.dependencies] cachetools = ">=2.0.0,<6.0" pyasn1-modules = ">=0.2.1" -rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} +rsa = ">=3.1.4,<5" six = ">=1.9.0" urllib3 = "<2.0" @@ -428,34 +428,35 @@ xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] [[package]] name = "google-cloud-bigquery" -version = "2.30.0" +version = "3.10.0" description = "Google BigQuery API client library" category = "main" optional = false -python-versions = ">=3.6, <3.11" +python-versions = ">=3.7" files = [ - {file = "google-cloud-bigquery-2.30.0.tar.gz", hash = "sha256:fdb57aa5e8af7d692eb5835739d9339dc1b5e89836430fe88dd1ddc1b0047639"}, - {file = "google_cloud_bigquery-2.30.0-py2.py3-none-any.whl", hash = "sha256:f6546ee96d57f45eaac8c4b24b52228a7f21a33669730efc9e69569bc88a04ad"}, + {file = "google-cloud-bigquery-3.10.0.tar.gz", hash = "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df"}, + {file = "google_cloud_bigquery-3.10.0-py2.py3-none-any.whl", hash = "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a"}, ] [package.dependencies] -google-api-core = {version = ">=1.29.0,<3.0.0dev", extras = ["grpc"]} -google-cloud-core = ">=1.4.1,<3.0.0dev" +google-api-core = {version = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} +google-cloud-core = ">=1.6.0,<3.0.0dev" google-resumable-media = ">=0.6.0,<3.0dev" -grpcio = ">=1.38.1,<2.0dev" -packaging = ">=14.3" -proto-plus = ">=1.10.0" -protobuf = ">=3.12.0" +grpcio = ">=1.47.0,<2.0dev" +packaging = ">=20.0.0" +proto-plus = ">=1.15.0,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" python-dateutil = ">=2.7.2,<3.0dev" -requests = ">=2.18.0,<3.0.0dev" +requests = ">=2.21.0,<3.0.0dev" [package.extras] -all = ["Shapely (>=1.6.0,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.0.0,<3.0.0dev)", "grpcio (>=1.38.1,<2.0dev)", "opentelemetry-api (>=0.11b0)", "opentelemetry-instrumentation (>=0.11b0)", "opentelemetry-sdk (>=0.11b0)", "pandas (>=0.24.2)", "pyarrow (>=3.0.0,<7.0dev)", "tqdm (>=4.7.4,<5.0.0dev)"] -bignumeric-type = ["pyarrow (>=3.0.0,<7.0dev)"] -bqstorage = ["google-cloud-bigquery-storage (>=2.0.0,<3.0.0dev)", "grpcio (>=1.38.1,<2.0dev)", "pyarrow (>=3.0.0,<7.0dev)"] -geopandas = ["Shapely (>=1.6.0,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)"] -opentelemetry = ["opentelemetry-api (>=0.11b0)", "opentelemetry-instrumentation (>=0.11b0)", "opentelemetry-sdk (>=0.11b0)"] -pandas = ["pandas (>=0.24.2)", "pyarrow (>=3.0.0,<7.0dev)"] +all = ["Shapely (>=1.8.4,<2.0dev)", "db-dtypes (>=0.3.0,<2.0.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)", "ipywidgets (>=7.7.0)", "opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)", "tqdm (>=4.7.4,<5.0.0dev)"] +bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "pyarrow (>=3.0.0)"] +geopandas = ["Shapely (>=1.8.4,<2.0dev)", "geopandas (>=0.9.0,<1.0dev)"] +ipython = ["ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)"] +ipywidgets = ["ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)"] +opentelemetry = ["opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)"] +pandas = ["db-dtypes (>=0.3.0,<2.0.0dev)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)"] tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] [[package]] @@ -812,11 +813,51 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "importlib-metadata" +version = "6.6.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, + {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "importlib-resources" +version = "5.12.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, + {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -856,7 +897,11 @@ files = [ [package.dependencies] attrs = ">=17.4.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] @@ -895,6 +940,7 @@ requests-toolbelt = ">=0.8.0,<1" strip-hints = ">=0.1.8,<1" tabulate = ">=0.8.6,<1" typer = ">=0.3.2,<1.0" +typing-extensions = {version = ">=3.7.4,<5", markers = "python_version < \"3.9\""} uritemplate = ">=3.0.1,<4" urllib3 = "<2" @@ -1063,6 +1109,18 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] +[[package]] +name = "pkgutil-resolve-name" +version = "1.3.10" +description = "Resolve a name to an object." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, + {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, +] + [[package]] name = "platformdirs" version = "3.5.1" @@ -1075,6 +1133,9 @@ files = [ {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.5", markers = "python_version < \"3.8\""} + [package.extras] docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] @@ -1083,7 +1144,7 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest- name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1091,6 +1152,9 @@ files = [ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] @@ -1110,6 +1174,7 @@ files = [ [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" @@ -1300,7 +1365,7 @@ files = [ name = "pytest" version = "7.3.1" description = "pytest: simple powerful testing with Python" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1311,6 +1376,7 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" @@ -1578,7 +1644,7 @@ tests = ["pytest", "pytest-cov"] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1655,13 +1721,11 @@ version = "0.1.0" description = "KFP components for interacting with Vertex AI" category = "main" optional = false -python-versions = ">=3.9,<3.11" +python-versions = ">=3.7,<3.11" files = [] develop = true [package.dependencies] -google-cloud-aiplatform = "1.24.1" -google-cloud-pipeline-components = "1.0.42" kfp = "==1.8.21" [package.source] @@ -1683,6 +1747,7 @@ files = [ [package.dependencies] distlib = ">=0.3.6,<1" filelock = ">=3.11,<4" +importlib-metadata = {version = ">=6.4.1", markers = "python_version < \"3.8\""} platformdirs = ">=3.2,<4" [package.extras] @@ -1806,7 +1871,23 @@ files = [ {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, ] +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [metadata] lock-version = "2.0" -python-versions = ">=3.9,<3.11" -content-hash = "c464813a2e2cb67c3d2e0ae229ca383b847a62fca4486fdf2f8ab90f335c624b" +python-versions = ">=3.7,<3.11" +content-hash = "3daa25983216b9a58680e4423147d310550b5277bffaca44f1284d421086506d" diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index cf42b576..049fbeaa 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -7,23 +7,20 @@ readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.7", ] packages = [ { include = "pipelines", from = "src" }, ] - [tool.poetry.dependencies] -python = ">=3.9,<3.11" +python = ">=3.7,<3.11" Jinja2 = ">=3.0.1,<4.0.0" google-cloud-aiplatform = "1.24.1" google-cloud-pipeline-components = "1.0.42" bigquery-components = { path = "../components/bigquery-components", develop = true } vertex-components = { path = "../components/vertex-components", develop = true } - - [tool.poetry.group.dev.dependencies] pytest = ">=7.3.1,<8.0.0" pre-commit = ">=2.14.1,<3.0.0" From fc131744d86f0cdf3395a0a198d44655cab3cf02 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 16:19:54 +0100 Subject: [PATCH 073/238] style(Makefile): fix Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 422fe6b7..258df1d0 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ pre-commit: ## Runs the pre-commit checks over entire repo poetry run pre-commit run --all-files setup: ## Set up local environment for Python development on pipelines - @poetry install && \ + @pip install poetry && \ cd pipelines && \ poetry install --with dev From ee5bb52c15946f45e0d67e4213bf519e5ca20d3b Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 18:28:52 +0100 Subject: [PATCH 074/238] style(pyproject.toml): change description --- pipelines/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index 049fbeaa..69ab2a3e 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -2,7 +2,7 @@ name = "pipelines" version = "0.1.0" authors = ["Example User "] -description = "Turbo Template" +description = "Vertex AI Pipelines end-to-end sample" readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", From 43463f86d9636bd80e63124f64a9b8c7af037fea Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 18:31:20 +0100 Subject: [PATCH 075/238] style(pyproject.toml): change description --- components/bigquery-components/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bigquery-components/pyproject.toml b/components/bigquery-components/pyproject.toml index 19db2207..ee3e0477 100644 --- a/components/bigquery-components/pyproject.toml +++ b/components/bigquery-components/pyproject.toml @@ -2,7 +2,7 @@ name = "bigquery-components" version = "0.1.0" authors = ["Example User "] -description = "Bigquery components for vertex pipelines" +description = "KubeFlow components for BigQuery" readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", From bf2fa788016f273c626ccbf6a404a807c35f901e Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 18:32:58 +0100 Subject: [PATCH 076/238] docs(README.md): update docs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db16eafd..0d4a2b9f 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,8 @@ please note: `poetry install` or `poetry add`, installs packages within the proj ### Configuring poetry to detect python version using pyenv -1. run in terminal `poetry config virtualenvs.prefer-active-python true` -1. Manage and install project's dependencies using `poetry install` +1. Run `poetry config virtualenvs.prefer-active-python true` +1. Install project dependencies using `poetry install` ### Deploying Cloud Infrastructure From e39dc49195e1f110810c6e6049066094e38818fd Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 18:34:56 +0100 Subject: [PATCH 077/238] docs(CONTRIBUTING.md): update docs --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index da7e6ba7..39ee273e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -211,7 +211,7 @@ def test_vertex_endpoint_uri(output_uri: str): ``` ## Adding or changing python dependencies -We use [poetry](https://python-poetry.org/docs/#installation) to handle our packages and their dependencies. Each group of pipeline components (e.g. [aiplatform](./pipeline_components/aiplatform/)) containers its own poetry environment, and there is a [separate poetry environment](./pipelines/) for the ML pipelines themselves and the pipeline trigger code. +We use [poetry](https://python-poetry.org/docs/#installation) to handle our packages and their dependencies. Each group of pipeline components (e.g. [vertex](./components/vertex-components/)) includes its own poetry environment, and there is a [separate poetry environment](./pipelines/) for the ML pipelines themselves and the pipeline trigger code. ### Adding python dependencies You may need to add new packages for your own use cases. To do this, run the following from the relevant directory ([pipelines](./pipelines) for the main ML pipeline dependencies or the directory of the relevant component group e.g. [aiplatform](./pipeline_components/aiplatform/)): From 4adc66e0686c6bc43cf89a4eb0becff62b1e6c32 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 18:37:24 +0100 Subject: [PATCH 078/238] style(pyproject.toml): change description --- components/vertex-components/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/vertex-components/pyproject.toml b/components/vertex-components/pyproject.toml index 91f212a6..f608436f 100644 --- a/components/vertex-components/pyproject.toml +++ b/components/vertex-components/pyproject.toml @@ -2,7 +2,7 @@ name = "vertex-components" version = "0.1.0" authors = ["Example User "] -description = "KFP components for interacting with Vertex AI" +description = "KubeFlow components for Vertex AI" readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", From ebcc5915e422a1bdaae0b9db29be2e89db96aa56 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 18:42:07 +0100 Subject: [PATCH 079/238] style(.python-version): fix python version --- .python-version | 2 +- components/vertex-components/.python-version | 2 +- pipelines/.python-version | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.python-version b/.python-version index 8a433dd7..f7e5aa84 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.7.12 +3.7.12 diff --git a/components/vertex-components/.python-version b/components/vertex-components/.python-version index 8a433dd7..f7e5aa84 100644 --- a/components/vertex-components/.python-version +++ b/components/vertex-components/.python-version @@ -1 +1 @@ -3.7.12 +3.7.12 diff --git a/pipelines/.python-version b/pipelines/.python-version index 8a433dd7..f7e5aa84 100644 --- a/pipelines/.python-version +++ b/pipelines/.python-version @@ -1 +1 @@ -3.7.12 +3.7.12 From 3a694cddcaaa71869e6572dfc56ed89fe64dd000 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Tue, 30 May 2023 18:44:49 +0100 Subject: [PATCH 080/238] style(Makefile): update makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 258df1d0..0b9b5aa5 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ pre-commit: ## Runs the pre-commit checks over entire repo setup: ## Set up local environment for Python development on pipelines @pip install poetry && \ cd pipelines && \ - poetry install --with dev + poetry install --with dev test-trigger: ## Runs unit tests for the pipeline trigger code @cd pipelines && \ @@ -37,7 +37,7 @@ compile-pipeline: ## Compile the pipeline to training.json or prediction.json. M setup-components: ## Run unit tests for a component group @cd "components/${GROUP}" && \ - poetry install --with dev + poetry install --with dev setup-all-components: ## Run unit tests for all pipeline components @set -e && \ From f74931b46877eebc9939af17824108a49b57e5a9 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Wed, 31 May 2023 12:56:59 +0100 Subject: [PATCH 081/238] Add ADDITIONAL_SUFFIX environment variable - include in env.sh.example - adjust xgboost training pipeline - adjust Makefile --- Makefile | 2 +- env.sh.example | 1 + pipelines/src/pipelines/xgboost/training/pipeline.py | 9 +++++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 4dc752c9..176371ca 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ test-all-components-coverage: ## Run tests with coverage sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ] ; then \ echo "Syncing assets to GCS" && \ - gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}/assets ; \ + gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}/assets${ADDITIONAL_SUFFIX} ; \ else \ echo "No assets folder found" ; \ fi ; diff --git a/env.sh.example b/env.sh.example index 59b518f6..455baeb3 100644 --- a/env.sh.example +++ b/env.sh.example @@ -20,6 +20,7 @@ export VERTEX_CMEK_IDENTIFIER= # optional export VERTEX_LOCATION=europe-west2 export VERTEX_NETWORK= # optional export VERTEX_PROJECT_ID=my-gcp-project +export ADDITIONAL_SUFFIX= # optional, add underscore for better readability e.g. _suffix # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 8499ce8a..565b985c 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -39,6 +39,7 @@ def xgboost_pipeline( timestamp: str = "2022-12-01 00:00:00", staging_bucket: str = os.environ.get("VERTEX_PIPELINE_ROOT"), pipeline_files_gcs_path: str = os.environ.get("PIPELINE_FILES_GCS_PATH"), + additional_suffix: str = os.environ.get("ADDITIONAL_SUFFIX"), test_dataset_uri: str = "", ): """ @@ -64,6 +65,8 @@ def xgboost_pipeline( staging_bucket (str): Staging bucket for pipeline artifacts. pipeline_files_gcs_path (str): GCS path where the pipeline files are located. test_dataset_uri (str): Optional. GCS URI of statis held-out test dataset. + additional_suffix (str): Optional. Additional suffix to append GCS asset path + and BQ tables that get overwritten. """ # Create variables to ensure the same arguments are passed @@ -71,14 +74,16 @@ def xgboost_pipeline( label_column_name = "total_fare" time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_xgb_training" # suffix to table names + table_suffix = "_xgb_training" + str(additional_suffix) # suffix to table names ingested_table = "ingested_data" + table_suffix preprocessed_table = "preprocessed_data" + table_suffix train_table = "train_data" + table_suffix valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/assets/train_xgb_model.py" + train_script_uri = ( + f"{pipeline_files_gcs_path}/assets{additional_suffix}/train_xgb_model.py" + ) hparams = dict( n_estimators=200, early_stopping_rounds=10, From db7fe79df85038eb996208bb915a9f9e229e07f3 Mon Sep 17 00:00:00 2001 From: Felix Schaumann <89205956+felix-datatonic@users.noreply.github.com> Date: Fri, 2 Jun 2023 08:56:30 +0200 Subject: [PATCH 082/238] Update components/README.md --- components/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/README.md b/components/README.md index 6f58f74b..b168bcca 100644 --- a/components/README.md +++ b/components/README.md @@ -4,7 +4,7 @@ This directory contains multiple Python packages that are used to define pipelin ## Creating a new pipeline components package -To create a new set of components (with different Python dependencies), copy one of the existing subdirectories and rename the different files and directories as appropriate (e.g. `bigquery-components` -> `my-new-components`). You will also need to update any references in the Python files themselves, as well as the `poetry.lock file` and `pyproject.toml`. +To create a new set of components (with different Python dependencies), copy one of the existing subdirectories and rename the different files and directories as appropriate (e.g. `vertex-components` -> `my-new-components`). You will also need to update any references in the Python files themselves, as well as `poetry.lock` and `pyproject.toml`. Your Python dependencies should be defined in `poetry.lock file`, `pyproject.toml`, and in `packages_to_install` (in the `@component` decorator): From f5a5572255feb465def22054a47986a9597695d9 Mon Sep 17 00:00:00 2001 From: Felix Schaumann <89205956+felix-datatonic@users.noreply.github.com> Date: Fri, 2 Jun 2023 08:56:38 +0200 Subject: [PATCH 083/238] Update components/README.md --- components/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/README.md b/components/README.md index b168bcca..f601b9bf 100644 --- a/components/README.md +++ b/components/README.md @@ -6,7 +6,7 @@ This directory contains multiple Python packages that are used to define pipelin To create a new set of components (with different Python dependencies), copy one of the existing subdirectories and rename the different files and directories as appropriate (e.g. `vertex-components` -> `my-new-components`). You will also need to update any references in the Python files themselves, as well as `poetry.lock` and `pyproject.toml`. -Your Python dependencies should be defined in `poetry.lock file`, `pyproject.toml`, and in `packages_to_install` (in the `@component` decorator): +Your Python dependencies should be defined in `poetry.lock`, `pyproject.toml`, and in `packages_to_install` (in the `@component` decorator): - In `pyproject.toml`, add `kfp` to the `[dependencies]` section (pinned to a specific version), and add any dependencies that your component uses under `[tool.poetry.dependencies]`(each pinned to a specific version) - In `packages_to_install` (in the `@component` decorator used to define your component), add any dependencies that your component uses (each pinned to a specific version) From 8df34131713862a36b9f35fa67a7f1e410ff3024 Mon Sep 17 00:00:00 2001 From: Felix Schaumann <89205956+felix-datatonic@users.noreply.github.com> Date: Fri, 2 Jun 2023 08:56:47 +0200 Subject: [PATCH 084/238] Update components/bigquery-components/.python-version --- components/bigquery-components/.python-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bigquery-components/.python-version b/components/bigquery-components/.python-version index 8a433dd7..f7e5aa84 100644 --- a/components/bigquery-components/.python-version +++ b/components/bigquery-components/.python-version @@ -1 +1 @@ -3.7.12 +3.7.12 From ee5c1aaca8071ce830ef060f9275ea5bf1fc848d Mon Sep 17 00:00:00 2001 From: Felix Schaumann Date: Fri, 2 Jun 2023 09:13:21 +0200 Subject: [PATCH 085/238] Cleanup --- .gitignore | 7 ------- Makefile | 9 +++++---- README.md | 3 ++- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index c9c0cfa7..930b7889 100644 --- a/.gitignore +++ b/.gitignore @@ -95,13 +95,6 @@ target/ profile_default/ ipython_config.py -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ diff --git a/Makefile b/Makefile index 0b9b5aa5..359c9a88 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,8 @@ pre-commit: ## Runs the pre-commit checks over entire repo poetry run pre-commit run --all-files setup: ## Set up local environment for Python development on pipelines - @pip install poetry && \ + @pip install pip --upgrade && \ + pip install poetry --upgrade && \ cd pipelines && \ poetry install --with dev @@ -59,11 +60,11 @@ test-all-components: ## Run unit tests for all pipeline components test-components-coverage: ## Run tests with coverage @cd "components/${GROUP}" && \ - pipenv run coverage run -m pytest && \ - pipenv run coverage report -m + poetry run coverage run -m pytest && \ + poetry run coverage report -m test-all-components-coverage: ## Run tests with coverage - @set -e && \ + @set -e && \ for component_group in components/*/ ; do \ echo "Test components under $$component_group" && \ $(MAKE) test-components-coverage GROUP=$$(basename $$component_group) ; \ diff --git a/README.md b/README.md index 0d4a2b9f..a26f5ff4 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,8 @@ In a production MLOps solution, your ML pipelines need to be repeatable. So, we 1. Copy `env.sh.example` to `env.sh`, and update the environment variables in `env.sh` 1. Load the environment variables in `env.sh` by running `source env.sh` -please note: `poetry install` or `poetry add`, installs packages within the project's virtual environment. If you use pip directly, you might accidentally install packages globally or in the wrong environment, leading to conflicts or difficulties in managing dependencies. +Note: `poetry install` or `poetry add`, installs packages within the project's virtual environment. +If you use `pip` directly, you might accidentally install packages globally or in the wrong environment, leading to conflicts or difficulties in managing dependencies. ### Configuring poetry to detect python version using pyenv From 874d2823954fc0535f451a2edb44bf5d02ca7ae9 Mon Sep 17 00:00:00 2001 From: roberta-dt Date: Fri, 2 Jun 2023 12:15:43 +0100 Subject: [PATCH 086/238] build(pyproject.toml): add coverage package --- .python-version | 2 +- pipelines/poetry.lock | 88 ++++++++++++++++++++++++++++++++++------ pipelines/pyproject.toml | 1 + 3 files changed, 78 insertions(+), 13 deletions(-) diff --git a/.python-version b/.python-version index f7e5aa84..8a433dd7 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.7.12 +3.7.12 diff --git a/pipelines/poetry.lock b/pipelines/poetry.lock index 1069ab60..e8388b8a 100644 --- a/pipelines/poetry.lock +++ b/pipelines/poetry.lock @@ -37,7 +37,7 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte [[package]] name = "bigquery-components" version = "0.1.0" -description = "Bigquery components for vertex pipelines" +description = "KubeFlow components for BigQuery" category = "main" optional = false python-versions = ">=3.7,<3.11" @@ -212,6 +212,70 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "coverage" +version = "7.2.5" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coverage-7.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c"}, + {file = "coverage-7.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c"}, + {file = "coverage-7.2.5-cp310-cp310-win32.whl", hash = "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5"}, + {file = "coverage-7.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c"}, + {file = "coverage-7.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce"}, + {file = "coverage-7.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6"}, + {file = "coverage-7.2.5-cp311-cp311-win32.whl", hash = "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b"}, + {file = "coverage-7.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068"}, + {file = "coverage-7.2.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969"}, + {file = "coverage-7.2.5-cp37-cp37m-win32.whl", hash = "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718"}, + {file = "coverage-7.2.5-cp37-cp37m-win_amd64.whl", hash = "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0"}, + {file = "coverage-7.2.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84"}, + {file = "coverage-7.2.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1"}, + {file = "coverage-7.2.5-cp38-cp38-win32.whl", hash = "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813"}, + {file = "coverage-7.2.5-cp38-cp38-win_amd64.whl", hash = "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212"}, + {file = "coverage-7.2.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b"}, + {file = "coverage-7.2.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1"}, + {file = "coverage-7.2.5-cp39-cp39-win32.whl", hash = "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31"}, + {file = "coverage-7.2.5-cp39-cp39-win_amd64.whl", hash = "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252"}, + {file = "coverage-7.2.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3"}, + {file = "coverage-7.2.5.tar.gz", hash = "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47"}, +] + +[package.extras] +toml = ["tomli"] + [[package]] name = "deprecated" version = "1.2.14" @@ -347,14 +411,14 @@ uritemplate = ">=3.0.0,<4dev" [[package]] name = "google-auth" -version = "2.19.0" +version = "2.19.1" description = "Google Authentication Library" category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "google-auth-2.19.0.tar.gz", hash = "sha256:f39d528077ac540793dd3c22a8706178f157642a67d874db25c640b7fead277e"}, - {file = "google_auth-2.19.0-py2.py3-none-any.whl", hash = "sha256:be617bfaf77774008e9d177573f782e109188c8a64ae6e744285df5cea3e7df6"}, + {file = "google-auth-2.19.1.tar.gz", hash = "sha256:a9cfa88b3e16196845e64a3658eb953992129d13ac7337b064c6546f77c17183"}, + {file = "google_auth-2.19.1-py2.py3-none-any.whl", hash = "sha256:ea165e014c7cbd496558796b627c271aa8c18b4cba79dc1cc962b24c5efdfb85"}, ] [package.dependencies] @@ -428,14 +492,14 @@ xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] [[package]] name = "google-cloud-bigquery" -version = "3.10.0" +version = "3.11.0" description = "Google BigQuery API client library" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-bigquery-3.10.0.tar.gz", hash = "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df"}, - {file = "google_cloud_bigquery-3.10.0-py2.py3-none-any.whl", hash = "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a"}, + {file = "google-cloud-bigquery-3.11.0.tar.gz", hash = "sha256:3a1c1b3566f9f27ea83a36aaf54eb879444ee73ef42456764018a0b3b556d0fb"}, + {file = "google_cloud_bigquery-3.11.0-py2.py3-none-any.whl", hash = "sha256:f568c36dfc9b8a2cf7e8ac10dd0889e0a52e8edf3dd556f3dd1eb441eda14364"}, ] [package.dependencies] @@ -1676,14 +1740,14 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.6.2" +version = "4.6.3" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, - {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, + {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, + {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, ] [[package]] @@ -1718,7 +1782,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "vertex-components" version = "0.1.0" -description = "KFP components for interacting with Vertex AI" +description = "KubeFlow components for Vertex AI" category = "main" optional = false python-versions = ">=3.7,<3.11" @@ -1890,4 +1954,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = ">=3.7,<3.11" -content-hash = "3daa25983216b9a58680e4423147d310550b5277bffaca44f1284d421086506d" +content-hash = "60130a208e8b1b38937ed8c91f1aa32e75c3c86be061ca321457ebc6f3a7d72c" diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index 69ab2a3e..9548d91c 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -24,6 +24,7 @@ vertex-components = { path = "../components/vertex-components", develop = true } [tool.poetry.group.dev.dependencies] pytest = ">=7.3.1,<8.0.0" pre-commit = ">=2.14.1,<3.0.0" +coverage = "==7.2.5" [build-system] requires = ["poetry-core>=1.0.0"] From 2a763771875f4100e7467c6a2ebfb83abfa33e63 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Thu, 1 Jun 2023 10:25:37 +0100 Subject: [PATCH 087/238] feat: return gcp resources (draft) --- .../src/vertex_components/custom_train_job.py | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/components/vertex-components/src/vertex_components/custom_train_job.py b/components/vertex-components/src/vertex_components/custom_train_job.py index 0a2773bd..c79836dc 100644 --- a/components/vertex-components/src/vertex_components/custom_train_job.py +++ b/components/vertex-components/src/vertex_components/custom_train_job.py @@ -1,5 +1,5 @@ # Copyright 2022 Google LLC -from typing import List, Dict +from typing import List, Dict, NamedTuple # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ @component( base_image="python:3.7", - packages_to_install=["google-cloud-aiplatform==1.24.1"], + packages_to_install=["google-cloud-aiplatform==1.24.1", "google-cloud-pipeline-components==1.0.42"], ) def custom_train_job( train_script_uri: str, @@ -41,7 +41,7 @@ def custom_train_job( accelerator_type: str = "ACCELERATOR_TYPE_UNSPECIFIED", accelerator_count: int = 0, parent_model: str = None, -): +) -> NamedTuple("Outputs", [("gcp_resources", str)]): """Run a custom training job using a training script. The provided script will be invoked by passing the following command-line arguments: @@ -93,6 +93,8 @@ def custom_train_job( import os.path import time import google.cloud.aiplatform as aip + from google_cloud_pipeline_components.proto.gcp_resources_pb2 import GcpResources + from google.protobuf.json_format import MessageToJson logging.info(f"Using train script: {train_script_uri}") script_path = "/gcs/" + train_script_uri[5:] @@ -143,3 +145,19 @@ def custom_train_job( for k, v in parsed_metrics.items(): if type(v) is float: metrics.log_metric(k, v) + + custom_train_job_name = job.name + logging.info(f"CustomJobName: {job.name }") + + custom_train_job_resources = GcpResources() + ctr = custom_train_job_resources.resources.add() + ctr.resource_type = "CustomJob" + ctr.resource_uri=f"https://{project_location}-aiplatform.googleapis.com/v1/projects/{project_id}/locations/{project_location}/customJobs/{custom_train_job_name}" + # ctr.resource_uri=f"https://{project_location}-aiplatform.googleapis.com/v1/projects/{project_id}/locations/{project_location}/trainingPipelines/{custom_train_job_name}" ? + + gcp_resources=MessageToJson(custom_train_job_resources) + logging.info(f"GCPresources -: {gcp_resources }") + return gcp_resources + + # return NamedTuple("Outputs", [("gcp_resources", str)])(gcp_resources) ? + From 3b68c316109df569a1cd8b1f0e794a71132d7db0 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Thu, 1 Jun 2023 11:31:22 +0100 Subject: [PATCH 088/238] feat: return gcp_resources --- .../src/vertex_components/custom_train_job.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/components/vertex-components/src/vertex_components/custom_train_job.py b/components/vertex-components/src/vertex_components/custom_train_job.py index c79836dc..ead998ca 100644 --- a/components/vertex-components/src/vertex_components/custom_train_job.py +++ b/components/vertex-components/src/vertex_components/custom_train_job.py @@ -87,6 +87,7 @@ def custom_train_job( Returns: parent_model (str): Resource URI of the parent model (empty string if the trained model is the first model version of its kind). + NamedTuple: gcp_resources for Vertex AI UI integration. """ import json import logging @@ -146,18 +147,10 @@ def custom_train_job( if type(v) is float: metrics.log_metric(k, v) - custom_train_job_name = job.name - logging.info(f"CustomJobName: {job.name }") - + # return GCP resource for Vertex AI UI integration custom_train_job_resources = GcpResources() ctr = custom_train_job_resources.resources.add() - ctr.resource_type = "CustomJob" - ctr.resource_uri=f"https://{project_location}-aiplatform.googleapis.com/v1/projects/{project_id}/locations/{project_location}/customJobs/{custom_train_job_name}" - # ctr.resource_uri=f"https://{project_location}-aiplatform.googleapis.com/v1/projects/{project_id}/locations/{project_location}/trainingPipelines/{custom_train_job_name}" ? - + ctr.resource_type = "CustomTrainingJob" + ctr.resource_uri=f"https://{project_location}-aiplatform.googleapis.com/v1/{job.resource_name}" gcp_resources=MessageToJson(custom_train_job_resources) - logging.info(f"GCPresources -: {gcp_resources }") - return gcp_resources - - # return NamedTuple("Outputs", [("gcp_resources", str)])(gcp_resources) ? - + return (gcp_resources,) From 62ce7e37f2159608e20e2cab0b434388e3b3cfc0 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Thu, 1 Jun 2023 12:38:17 +0100 Subject: [PATCH 089/238] feat: change uri --- .../src/vertex_components/custom_train_job.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/vertex-components/src/vertex_components/custom_train_job.py b/components/vertex-components/src/vertex_components/custom_train_job.py index ead998ca..172df2cd 100644 --- a/components/vertex-components/src/vertex_components/custom_train_job.py +++ b/components/vertex-components/src/vertex_components/custom_train_job.py @@ -150,7 +150,7 @@ def custom_train_job( # return GCP resource for Vertex AI UI integration custom_train_job_resources = GcpResources() ctr = custom_train_job_resources.resources.add() - ctr.resource_type = "CustomTrainingJob" - ctr.resource_uri=f"https://{project_location}-aiplatform.googleapis.com/v1/{job.resource_name}" + ctr.resource_type = "CustomJob" + ctr.resource_uri=f"https://{project_location}-aiplatform.googleapis.com/v1/projects/{project_id}/locations/{project_location}/customJobs/{job.name}" gcp_resources=MessageToJson(custom_train_job_resources) return (gcp_resources,) From becdefd46110e9ed04ddfd96ac03b182686857bd Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Thu, 1 Jun 2023 14:55:02 +0100 Subject: [PATCH 090/238] feat: change resource uri to backingCustomJob --- .../src/vertex_components/custom_train_job.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/vertex-components/src/vertex_components/custom_train_job.py b/components/vertex-components/src/vertex_components/custom_train_job.py index 172df2cd..f25b8077 100644 --- a/components/vertex-components/src/vertex_components/custom_train_job.py +++ b/components/vertex-components/src/vertex_components/custom_train_job.py @@ -147,10 +147,12 @@ def custom_train_job( if type(v) is float: metrics.log_metric(k, v) + # return GCP resource for Vertex AI UI integration + custom_job_name = job.to_dict()['trainingTaskMetadata']['backingCustomJob'] custom_train_job_resources = GcpResources() ctr = custom_train_job_resources.resources.add() ctr.resource_type = "CustomJob" - ctr.resource_uri=f"https://{project_location}-aiplatform.googleapis.com/v1/projects/{project_id}/locations/{project_location}/customJobs/{job.name}" + ctr.resource_uri=custom_job_name gcp_resources=MessageToJson(custom_train_job_resources) return (gcp_resources,) From cca75d5d713ea3398dfbf4cb540f8b92f9702d1a Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Thu, 1 Jun 2023 16:13:07 +0100 Subject: [PATCH 091/238] style: formatting --- .../src/vertex_components/custom_train_job.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/components/vertex-components/src/vertex_components/custom_train_job.py b/components/vertex-components/src/vertex_components/custom_train_job.py index f25b8077..9cc51b17 100644 --- a/components/vertex-components/src/vertex_components/custom_train_job.py +++ b/components/vertex-components/src/vertex_components/custom_train_job.py @@ -18,7 +18,10 @@ @component( base_image="python:3.7", - packages_to_install=["google-cloud-aiplatform==1.24.1", "google-cloud-pipeline-components==1.0.42"], + packages_to_install=[ + "google-cloud-aiplatform==1.24.1", + "google-cloud-pipeline-components==1.0.42", + ], ) def custom_train_job( train_script_uri: str, @@ -41,7 +44,7 @@ def custom_train_job( accelerator_type: str = "ACCELERATOR_TYPE_UNSPECIFIED", accelerator_count: int = 0, parent_model: str = None, -) -> NamedTuple("Outputs", [("gcp_resources", str)]): +) -> NamedTuple("Outputs", [("gcp_resources", str)]): """Run a custom training job using a training script. The provided script will be invoked by passing the following command-line arguments: @@ -146,13 +149,12 @@ def custom_train_job( for k, v in parsed_metrics.items(): if type(v) is float: metrics.log_metric(k, v) - # return GCP resource for Vertex AI UI integration - custom_job_name = job.to_dict()['trainingTaskMetadata']['backingCustomJob'] + custom_job_name = job.to_dict()["trainingTaskMetadata"]["backingCustomJob"] custom_train_job_resources = GcpResources() ctr = custom_train_job_resources.resources.add() ctr.resource_type = "CustomJob" - ctr.resource_uri=custom_job_name - gcp_resources=MessageToJson(custom_train_job_resources) + ctr.resource_uri = custom_job_name + gcp_resources = MessageToJson(custom_train_job_resources) return (gcp_resources,) From 15675d413f2df18a772ea1f5ed85850abcc80f38 Mon Sep 17 00:00:00 2001 From: Donatas M <125287387+donatas-dt@users.noreply.github.com> Date: Fri, 2 Jun 2023 09:33:03 +0100 Subject: [PATCH 092/238] test: gcp resources unit tests --- .../tests/test_custom_training_job.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/components/vertex-components/tests/test_custom_training_job.py b/components/vertex-components/tests/test_custom_training_job.py index 46ec46ca..ac7675c4 100644 --- a/components/vertex-components/tests/test_custom_training_job.py +++ b/components/vertex-components/tests/test_custom_training_job.py @@ -2,7 +2,7 @@ from kfp.v2.dsl import Dataset, Metrics, Artifact from unittest import mock import pytest - +import json import vertex_components @@ -24,7 +24,12 @@ def test_custom_train_job(mock_open, mock_exists, mock_job, tmpdir): mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) mock_metrics = Metrics(uri=tmpdir) - custom_train_job( + mock_job_instance = mock_job.return_value + mock_job_instance.to_dict.return_value = { + "trainingTaskMetadata": {"backingCustomJob": "mock_custom_job_name"} + } + + (gcp_resources,) = custom_train_job( train_script_uri="gs://my-bucket/train_script.py", train_data=mock_train_data, valid_data=mock_valid_data, @@ -53,6 +58,11 @@ def test_custom_train_job(mock_open, mock_exists, mock_job, tmpdir): # Assert metrics loading mock_open.assert_called_once_with(tmpdir, "r") + # Assert gcp_resources contains the expected value + assert ( + json.loads(gcp_resources)["resources"][0]["resourceUri"] + == "mock_custom_job_name" + ) @mock.patch("google.cloud.aiplatform.CustomTrainingJob") @@ -72,7 +82,7 @@ def test_custom_train_script_not_found(mock_open, mock_exists, mock_job, tmpdir) mock_metrics = Metrics(uri=tmpdir) with pytest.raises(ValueError): - custom_train_job( + (gcp_resources,) = custom_train_job( train_script_uri="gs://my-bucket/train_script.py", train_data=mock_train_data, valid_data=mock_valid_data, From ece44440fd4f1306c3e0dfd03a48af03ec912882 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Mon, 5 Jun 2023 10:06:34 +0100 Subject: [PATCH 093/238] Add ADDITIONAL_SUFFIX for all other pipelines --- .../src/pipelines/tensorflow/prediction/pipeline.py | 5 ++++- pipelines/src/pipelines/tensorflow/training/pipeline.py | 9 +++++++-- pipelines/src/pipelines/xgboost/prediction/pipeline.py | 5 ++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index 7290a4c9..a3e29c54 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -36,6 +36,7 @@ def tensorflow_pipeline( batch_prediction_machine_type: str = "n1-standard-4", batch_prediction_min_replicas: int = 3, batch_prediction_max_replicas: int = 10, + additional_suffix: str = os.environ.get("ADDITIONAL_SUFFIX"), ): """ Tensorflow prediction pipeline which: @@ -68,6 +69,8 @@ def tensorflow_pipeline( Vertex Batch Prediction job for horizontal scalability batch_prediction_max_replicas (int): Maximum no of machines to distribute the Vertex Batch Prediction job for horizontal scalability. + additional_suffix (str): Optional. Additional suffix to append GCS asset path + and BQ tables that get overwritten. Returns: None @@ -79,7 +82,7 @@ def tensorflow_pipeline( file_pattern = "" # e.g. "files-*.csv", used as file pattern on storage time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_tf_prediction" # suffix to table names + table_suffix = "_tf_prediction" + str(additional_suffix) # suffix to table names ingested_table = "ingested_data" + table_suffix monitoring_alert_email_addresses = [] monitoring_skew_config = {"defaultSkewThreshold": {"value": 0.001}} diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 0dfb1bb2..90b48b08 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -39,6 +39,7 @@ def tensorflow_pipeline( timestamp: str = "2022-12-01 00:00:00", staging_bucket: str = os.environ.get("VERTEX_PIPELINE_ROOT"), pipeline_files_gcs_path: str = os.environ.get("PIPELINE_FILES_GCS_PATH"), + additional_suffix: str = os.environ.get("ADDITIONAL_SUFFIX"), test_dataset_uri: str = "", ): """ @@ -65,6 +66,8 @@ def tensorflow_pipeline( If any time part is missing, it will be regarded as zero. staging_bucket (str): Staging bucket for pipeline artifacts. pipeline_files_gcs_path (str): GCS path where the pipeline files are located + additional_suffix (str): Optional. Additional suffix to append GCS asset path + and BQ tables that get overwritten. test_dataset_uri (str): Optional. GCS URI of statis held-out test dataset. """ @@ -73,14 +76,16 @@ def tensorflow_pipeline( label_column_name = "total_fare" time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_tf_training" # suffix to table names + table_suffix = "_tf_training" + str(additional_suffix) # suffix to table names ingested_table = "ingested_data" + table_suffix preprocessed_table = "preprocessed_data" + table_suffix train_table = "train_data" + table_suffix valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/assets/train_tf_model.py" + train_script_uri = ( + f"{pipeline_files_gcs_path}/assets{additional_suffix}/train_tf_model.py" + ) hparams = dict( batch_size=100, epochs=5, diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index 5592a121..3c83d490 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -36,6 +36,7 @@ def xgboost_pipeline( batch_prediction_machine_type: str = "n1-standard-4", batch_prediction_min_replicas: int = 3, batch_prediction_max_replicas: int = 10, + additional_suffix: str = os.environ.get("ADDITIONAL_SUFFIX"), ): """ XGB prediction pipeline which: @@ -65,6 +66,8 @@ def xgboost_pipeline( Vertex Batch Prediction job for horizontal scalability batch_prediction_max_replicas (int): Maximum no of machines to distribute the Vertex Batch Prediction job for horizontal scalability. + additional_suffix (str): Optional. Additional suffix to append GCS asset path + and BQ tables that get overwritten. Returns: None @@ -75,7 +78,7 @@ def xgboost_pipeline( # into different components of the pipeline time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_xgb_prediction" # suffix to table names + table_suffix = "_xgb_prediction" + str(additional_suffix) # suffix to table names ingested_table = "ingested_data" + table_suffix monitoring_alert_email_addresses = [] monitoring_skew_config = {"defaultSkewThreshold": {"value": 0.001}} From 3ea015a2876f46a96a8e9029bc8378e4a5a64a83 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Mon, 5 Jun 2023 10:09:32 +0100 Subject: [PATCH 094/238] more comprehensive comment for ADDITIONAL_SUFFIX --- env.sh.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/env.sh.example b/env.sh.example index 455baeb3..b4064f21 100644 --- a/env.sh.example +++ b/env.sh.example @@ -20,7 +20,7 @@ export VERTEX_CMEK_IDENTIFIER= # optional export VERTEX_LOCATION=europe-west2 export VERTEX_NETWORK= # optional export VERTEX_PROJECT_ID=my-gcp-project -export ADDITIONAL_SUFFIX= # optional, add underscore for better readability e.g. _suffix +export ADDITIONAL_SUFFIX= # optional, additional suffix to for BQ and GCS resource names to prevent developers overwriting when working in the same Google Cloud Project # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com From 01e7d02ca783d59f07912fc2bda7dfa1d30be9c8 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Mon, 5 Jun 2023 11:08:51 +0100 Subject: [PATCH 095/238] extend comment for ADDITIONAL_SUFFIX --- env.sh.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/env.sh.example b/env.sh.example index b4064f21..c4d7f303 100644 --- a/env.sh.example +++ b/env.sh.example @@ -20,7 +20,7 @@ export VERTEX_CMEK_IDENTIFIER= # optional export VERTEX_LOCATION=europe-west2 export VERTEX_NETWORK= # optional export VERTEX_PROJECT_ID=my-gcp-project -export ADDITIONAL_SUFFIX= # optional, additional suffix to for BQ and GCS resource names to prevent developers overwriting when working in the same Google Cloud Project +export ADDITIONAL_SUFFIX= # optional, additional suffix (e.g. _) for BQ and GCS outputs to prevent developers overwriting them when working in the same Google Cloud Project. # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com From 6d6fc45674f15ccda4f0de7101e6c22f9bcade55 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Mon, 5 Jun 2023 11:42:10 +0100 Subject: [PATCH 096/238] Add empty ADDITIONAL_SUFFIX in cloudbuild e2e tests --- cloudbuild/e2e-test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index c2e28095..13a2b0f9 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -45,6 +45,7 @@ steps: - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} + - ADDITIONAL_SUFFIX= options: logging: CLOUD_LOGGING_ONLY From 8e54ee18f584d6f043fc170fa83203d7bcd19107 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Mon, 5 Jun 2023 13:13:40 +0100 Subject: [PATCH 097/238] Combine ADDITIONAL_SUFFIX into PIPELINE_FILES_GCS_PATH --- Makefile | 2 +- env.sh.example | 2 +- pipelines/src/pipelines/tensorflow/training/pipeline.py | 5 +---- pipelines/src/pipelines/xgboost/training/pipeline.py | 4 +--- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 2342612a..8627895a 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ test-all-components-coverage: ## Run tests with coverage sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ] ; then \ echo "Syncing assets to GCS" && \ - gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}/assets${ADDITIONAL_SUFFIX} ; \ + gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}; \ else \ echo "No assets folder found" ; \ fi ; diff --git a/env.sh.example b/env.sh.example index c4d7f303..c6bd03e3 100644 --- a/env.sh.example +++ b/env.sh.example @@ -24,5 +24,5 @@ export ADDITIONAL_SUFFIX= # optional, additional suffix (e.g. _) for # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com -export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets +export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/assets${ADDITIONAL_SUFFIX} export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 90b48b08..36f042a6 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -52,7 +52,6 @@ def tensorflow_pipeline( Args: project_id (str): project id of the Google Cloud project project_location (str): location of the Google Cloud project - pipeline_files_gcs_path (str): GCS path where the pipeline files are located ingestion_project_id (str): project id containing the source bigquery data for ingestion. This can be the same as `project_id` if the source data is in the same project where the ML pipeline is executed. @@ -83,9 +82,7 @@ def tensorflow_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = ( - f"{pipeline_files_gcs_path}/assets{additional_suffix}/train_tf_model.py" - ) + train_script_uri = f"{pipeline_files_gcs_path}/train_tf_model.py" hparams = dict( batch_size=100, epochs=5, diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 565b985c..7cbecbd1 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -81,9 +81,7 @@ def xgboost_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = ( - f"{pipeline_files_gcs_path}/assets{additional_suffix}/train_xgb_model.py" - ) + train_script_uri = f"{pipeline_files_gcs_path}/train_xgb_model.py" hparams = dict( n_estimators=200, early_stopping_rounds=10, From 52e6ac3fe8d948df419a7632bbb31d3190564ff1 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 09:37:55 +0100 Subject: [PATCH 098/238] Change to RESOURCE_SUFFIX env var that is the current git user name --- Makefile | 3 +++ cloudbuild/e2e-test.yaml | 2 +- env.sh.example | 4 +++- pipelines/src/pipelines/tensorflow/prediction/pipeline.py | 8 ++++---- pipelines/src/pipelines/tensorflow/training/pipeline.py | 8 ++++---- pipelines/src/pipelines/xgboost/prediction/pipeline.py | 8 ++++---- pipelines/src/pipelines/xgboost/training/pipeline.py | 8 ++++---- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 8627895a..c3d1ad8d 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,9 @@ -include env.sh export +RESOURCE_SUFFIX := $(shell . env.sh && echo $$RESOURCE_SUFFIX) +PIPELINE_FILES_GCS_PATH := $(shell . env.sh && echo $$PIPELINE_FILES_GCS_PATH) + help: ## Display this help screen @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 13a2b0f9..923275d9 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -45,7 +45,7 @@ steps: - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} - - ADDITIONAL_SUFFIX= + - RESOURCE_SUFFIX=_${$SHORT_SHA} options: logging: CLOUD_LOGGING_ONLY diff --git a/env.sh.example b/env.sh.example index c6bd03e3..b7289123 100644 --- a/env.sh.example +++ b/env.sh.example @@ -20,7 +20,9 @@ export VERTEX_CMEK_IDENTIFIER= # optional export VERTEX_LOCATION=europe-west2 export VERTEX_NETWORK= # optional export VERTEX_PROJECT_ID=my-gcp-project -export ADDITIONAL_SUFFIX= # optional, additional suffix (e.g. _) for BQ and GCS outputs to prevent developers overwriting them when working in the same Google Cloud Project. + +# Suffix to facilitate running concurrent pipelines in the same Google Cloud project. Uncomment if working in a team to avoid overwriting resources during development +# export RESOURCE_SUFFIX=_$(git config --get user.name) # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index a3e29c54..ff25c119 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -36,7 +36,7 @@ def tensorflow_pipeline( batch_prediction_machine_type: str = "n1-standard-4", batch_prediction_min_replicas: int = 3, batch_prediction_max_replicas: int = 10, - additional_suffix: str = os.environ.get("ADDITIONAL_SUFFIX"), + resource_suffix: str = os.environ.get("RESOURCE_SUFFIX"), ): """ Tensorflow prediction pipeline which: @@ -69,8 +69,8 @@ def tensorflow_pipeline( Vertex Batch Prediction job for horizontal scalability batch_prediction_max_replicas (int): Maximum no of machines to distribute the Vertex Batch Prediction job for horizontal scalability. - additional_suffix (str): Optional. Additional suffix to append GCS asset path - and BQ tables that get overwritten. + resource_suffix (str): Optional. Additional suffix to append GCS resources + that get overwritten. Returns: None @@ -82,7 +82,7 @@ def tensorflow_pipeline( file_pattern = "" # e.g. "files-*.csv", used as file pattern on storage time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_tf_prediction" + str(additional_suffix) # suffix to table names + table_suffix = "_tf_prediction" + str(resource_suffix) # suffix to table names ingested_table = "ingested_data" + table_suffix monitoring_alert_email_addresses = [] monitoring_skew_config = {"defaultSkewThreshold": {"value": 0.001}} diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 36f042a6..9f1d0544 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -39,7 +39,7 @@ def tensorflow_pipeline( timestamp: str = "2022-12-01 00:00:00", staging_bucket: str = os.environ.get("VERTEX_PIPELINE_ROOT"), pipeline_files_gcs_path: str = os.environ.get("PIPELINE_FILES_GCS_PATH"), - additional_suffix: str = os.environ.get("ADDITIONAL_SUFFIX"), + resource_suffix: str = os.environ.get("RESOURCE_SUFFIX"), test_dataset_uri: str = "", ): """ @@ -65,8 +65,8 @@ def tensorflow_pipeline( If any time part is missing, it will be regarded as zero. staging_bucket (str): Staging bucket for pipeline artifacts. pipeline_files_gcs_path (str): GCS path where the pipeline files are located - additional_suffix (str): Optional. Additional suffix to append GCS asset path - and BQ tables that get overwritten. + resource_suffix (str): Optional. Additional suffix to append GCS resources + that get overwritten. test_dataset_uri (str): Optional. GCS URI of statis held-out test dataset. """ @@ -75,7 +75,7 @@ def tensorflow_pipeline( label_column_name = "total_fare" time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_tf_training" + str(additional_suffix) # suffix to table names + table_suffix = "_tf_training" + str(resource_suffix) # suffix to table names ingested_table = "ingested_data" + table_suffix preprocessed_table = "preprocessed_data" + table_suffix train_table = "train_data" + table_suffix diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index 3c83d490..f077d32c 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -36,7 +36,7 @@ def xgboost_pipeline( batch_prediction_machine_type: str = "n1-standard-4", batch_prediction_min_replicas: int = 3, batch_prediction_max_replicas: int = 10, - additional_suffix: str = os.environ.get("ADDITIONAL_SUFFIX"), + resource_suffix: str = os.environ.get("RESOURCE_SUFFIX"), ): """ XGB prediction pipeline which: @@ -66,8 +66,8 @@ def xgboost_pipeline( Vertex Batch Prediction job for horizontal scalability batch_prediction_max_replicas (int): Maximum no of machines to distribute the Vertex Batch Prediction job for horizontal scalability. - additional_suffix (str): Optional. Additional suffix to append GCS asset path - and BQ tables that get overwritten. + resource_suffix (str): Optional. Additional suffix to append GCS resources + that get overwritten. Returns: None @@ -78,7 +78,7 @@ def xgboost_pipeline( # into different components of the pipeline time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_xgb_prediction" + str(additional_suffix) # suffix to table names + table_suffix = "_xgb_prediction" + str(resource_suffix) # suffix to table names ingested_table = "ingested_data" + table_suffix monitoring_alert_email_addresses = [] monitoring_skew_config = {"defaultSkewThreshold": {"value": 0.001}} diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 7cbecbd1..64f3061c 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -39,7 +39,7 @@ def xgboost_pipeline( timestamp: str = "2022-12-01 00:00:00", staging_bucket: str = os.environ.get("VERTEX_PIPELINE_ROOT"), pipeline_files_gcs_path: str = os.environ.get("PIPELINE_FILES_GCS_PATH"), - additional_suffix: str = os.environ.get("ADDITIONAL_SUFFIX"), + resource_suffix: str = os.environ.get("RESOURCE_SUFFIX"), test_dataset_uri: str = "", ): """ @@ -64,9 +64,9 @@ def xgboost_pipeline( If any time part is missing, it will be regarded as zero. staging_bucket (str): Staging bucket for pipeline artifacts. pipeline_files_gcs_path (str): GCS path where the pipeline files are located. + resource_suffix (str): Optional. Additional suffix to append GCS resources + that get overwritten. test_dataset_uri (str): Optional. GCS URI of statis held-out test dataset. - additional_suffix (str): Optional. Additional suffix to append GCS asset path - and BQ tables that get overwritten. """ # Create variables to ensure the same arguments are passed @@ -74,7 +74,7 @@ def xgboost_pipeline( label_column_name = "total_fare" time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_xgb_training" + str(additional_suffix) # suffix to table names + table_suffix = "_xgb_training" + str(resource_suffix) # suffix to table names ingested_table = "ingested_data" + table_suffix preprocessed_table = "preprocessed_data" + table_suffix train_table = "train_data" + table_suffix From 630833ce90b26a7e9b6a2c618942e82effc50342 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 09:45:04 +0100 Subject: [PATCH 099/238] Update README.md with info about RESOURCE_SUFFIX --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a26f5ff4..25bdb446 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,8 @@ This will execute the pipeline using the chosen template on Vertex AI, namely it 1. Copy the `assets` folders to Cloud Storage 1. Trigger the pipeline with the help of `pipelines/trigger/main.py` +To avoid resource conflicts when running pipelines concurrently in the same Google Cloud project with multiple developers, uncomment the `RESOURCE_SUFFIX` environment variable in your `env.sh` file. This will append your Git username as the suffix to Google resources and ensure each developer's resources remain unique and separate, preventing unintentional overwriting. + #### Pipeline input parameters The ML pipelines have input parameters. As you can see in the pipeline definition files (`pipelines/pipelines///pipeline.py`), they have default values, and some of these default values are derived from environment variables (which in turn are defined in `env.sh`). From d850a5a35860e735887896ec1e0e2f6ab27ce819 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 10:13:44 +0100 Subject: [PATCH 100/238] Comment out Make variables to check if they're affecting e2e tests --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c3d1ad8d..a639ae6a 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,8 @@ -include env.sh export -RESOURCE_SUFFIX := $(shell . env.sh && echo $$RESOURCE_SUFFIX) -PIPELINE_FILES_GCS_PATH := $(shell . env.sh && echo $$PIPELINE_FILES_GCS_PATH) +# RESOURCE_SUFFIX := $(shell . env.sh && echo $$RESOURCE_SUFFIX) +# PIPELINE_FILES_GCS_PATH := $(shell . env.sh && echo $$PIPELINE_FILES_GCS_PATH) help: ## Display this help screen @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' From f8ef02b2b79d1df9b3972599ad7c1433fc2d024c Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 10:25:03 +0100 Subject: [PATCH 101/238] Fix e2e tests by sourcing env.sh as part of Make commands --- Makefile | 9 ++++----- cloudbuild/e2e-test.yaml | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index a639ae6a..6c52aded 100644 --- a/Makefile +++ b/Makefile @@ -15,9 +15,6 @@ -include env.sh export -# RESOURCE_SUFFIX := $(shell . env.sh && echo $$RESOURCE_SUFFIX) -# PIPELINE_FILES_GCS_PATH := $(shell . env.sh && echo $$PIPELINE_FILES_GCS_PATH) - help: ## Display this help screen @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @@ -36,7 +33,8 @@ test-trigger: ## Runs unit tests for the pipeline trigger code poetry run python -m pytest tests/trigger compile-pipeline: ## Compile the pipeline to training.json or prediction.json. Must specify pipeline= - @cd pipelines/src && \ + @source ./env.sh && \ + cd pipelines/src && \ poetry run python -m pipelines.${PIPELINE_TEMPLATE}.${pipeline}.pipeline setup-components: ## Run unit tests for a component group @@ -76,7 +74,8 @@ test-all-components-coverage: ## Run tests with coverage sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ] ; then \ echo "Syncing assets to GCS" && \ - gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}; \ + source ./env.sh && \ + gsutil -m rsync -r -d ./pipelines/assets $$PIPELINE_FILES_GCS_PATH; \ else \ echo "No assets folder found" ; \ fi ; diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 923275d9..412536f8 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -45,7 +45,7 @@ steps: - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} - - RESOURCE_SUFFIX=_${$SHORT_SHA} + - RESOURCE_SUFFIX=_${SHORT_SHA} options: logging: CLOUD_LOGGING_ONLY From b4e2abcafa81b90dd7bb3aa2e9a04a905ecfd580 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 10:30:27 +0100 Subject: [PATCH 102/238] Change from source to . in makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6c52aded..6e9276c8 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ test-trigger: ## Runs unit tests for the pipeline trigger code poetry run python -m pytest tests/trigger compile-pipeline: ## Compile the pipeline to training.json or prediction.json. Must specify pipeline= - @source ./env.sh && \ + @. ./env.sh && \ cd pipelines/src && \ poetry run python -m pipelines.${PIPELINE_TEMPLATE}.${pipeline}.pipeline @@ -74,7 +74,7 @@ test-all-components-coverage: ## Run tests with coverage sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ] ; then \ echo "Syncing assets to GCS" && \ - source ./env.sh && \ + . ./env.sh && \ gsutil -m rsync -r -d ./pipelines/assets $$PIPELINE_FILES_GCS_PATH; \ else \ echo "No assets folder found" ; \ From dba5afd3d9dbffe993a7a714cf14767f51992ba4 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 10:44:50 +0100 Subject: [PATCH 103/238] Try using conditional assignment in Makefile to fix e2e tests --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 6e9276c8..adeb7724 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +RESOURCE_SUFFIX ?= $(shell . env.sh && echo $$RESOURCE_SUFFIX) +PIPELINE_FILES_GCS_PATH ?= $(shell . env.sh && echo $$PIPELINE_FILES_GCS_PATH) + -include env.sh export @@ -33,8 +36,7 @@ test-trigger: ## Runs unit tests for the pipeline trigger code poetry run python -m pytest tests/trigger compile-pipeline: ## Compile the pipeline to training.json or prediction.json. Must specify pipeline= - @. ./env.sh && \ - cd pipelines/src && \ + @cd pipelines/src && \ poetry run python -m pipelines.${PIPELINE_TEMPLATE}.${pipeline}.pipeline setup-components: ## Run unit tests for a component group @@ -74,7 +76,6 @@ test-all-components-coverage: ## Run tests with coverage sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ] ; then \ echo "Syncing assets to GCS" && \ - . ./env.sh && \ gsutil -m rsync -r -d ./pipelines/assets $$PIPELINE_FILES_GCS_PATH; \ else \ echo "No assets folder found" ; \ From 1b11d02d6e802ff263f084eea5fee70296aa6782 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 11:00:03 +0100 Subject: [PATCH 104/238] Update PIPELINE_FILES_GCS_PATH env var in e2e tests to include assets sub directory --- Makefile | 2 +- cloudbuild/e2e-test.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index adeb7724..fba2a29e 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,7 @@ test-all-components-coverage: ## Run tests with coverage sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ] ; then \ echo "Syncing assets to GCS" && \ - gsutil -m rsync -r -d ./pipelines/assets $$PIPELINE_FILES_GCS_PATH; \ + gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}; \ else \ echo "No assets folder found" ; \ fi ; diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 412536f8..d2754603 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -44,7 +44,7 @@ steps: - VERTEX_NETWORK=${_TEST_VERTEX_NETWORK} - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} + - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA}/assets - RESOURCE_SUFFIX=_${SHORT_SHA} options: From 617ed59ff9a29e5385db43350b551f869381b354 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 16:33:51 +0100 Subject: [PATCH 105/238] Revert to letting users specify their own suffix --- Makefile | 3 --- README.md | 2 +- env.sh.example | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index fba2a29e..8627895a 100644 --- a/Makefile +++ b/Makefile @@ -12,9 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -RESOURCE_SUFFIX ?= $(shell . env.sh && echo $$RESOURCE_SUFFIX) -PIPELINE_FILES_GCS_PATH ?= $(shell . env.sh && echo $$PIPELINE_FILES_GCS_PATH) - -include env.sh export diff --git a/README.md b/README.md index 25bdb446..6fcd6069 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ This will execute the pipeline using the chosen template on Vertex AI, namely it 1. Copy the `assets` folders to Cloud Storage 1. Trigger the pipeline with the help of `pipelines/trigger/main.py` -To avoid resource conflicts when running pipelines concurrently in the same Google Cloud project with multiple developers, uncomment the `RESOURCE_SUFFIX` environment variable in your `env.sh` file. This will append your Git username as the suffix to Google resources and ensure each developer's resources remain unique and separate, preventing unintentional overwriting. +To avoid resource conflicts when running pipelines concurrently in the same Google Cloud project with multiple developers, populate the `RESOURCE_SUFFIX` environment variable in your `env.sh` file. This will append your defined suffix to Google resources and ensure each developer's resources remain unique and separate, preventing unintentional overwriting. #### Pipeline input parameters diff --git a/env.sh.example b/env.sh.example index b7289123..530189f6 100644 --- a/env.sh.example +++ b/env.sh.example @@ -21,8 +21,8 @@ export VERTEX_LOCATION=europe-west2 export VERTEX_NETWORK= # optional export VERTEX_PROJECT_ID=my-gcp-project -# Suffix to facilitate running concurrent pipelines in the same Google Cloud project. Uncomment if working in a team to avoid overwriting resources during development -# export RESOURCE_SUFFIX=_$(git config --get user.name) +# Suffix (e.g. _) to facilitate running concurrent pipelines in the same Google Cloud project. Populate if working in a team to avoid overwriting resources during development +export RESOURCE_SUFFIX= # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com From c480f6f9cbd7fa26ebdd911431f32cda018f8e97 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 16:35:34 +0100 Subject: [PATCH 106/238] RESOURCE_SUFFIX typo in env.sh.example --- env.sh.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/env.sh.example b/env.sh.example index 530189f6..99fa5181 100644 --- a/env.sh.example +++ b/env.sh.example @@ -26,5 +26,5 @@ export RESOURCE_SUFFIX= # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com -export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/assets${ADDITIONAL_SUFFIX} +export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/assets${RESOURCE_SUFFIX} export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root From 60de248fde1c6ed37180b8fbf6b75412a05f52f8 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 17:33:23 +0100 Subject: [PATCH 107/238] Revert including `assets` as part of pipeline_files_gcs_path --- Makefile | 2 +- env.sh.example | 2 +- pipelines/src/pipelines/tensorflow/training/pipeline.py | 2 +- pipelines/src/pipelines/xgboost/training/pipeline.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 8627895a..7e762bbd 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ test-all-components-coverage: ## Run tests with coverage sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ] ; then \ echo "Syncing assets to GCS" && \ - gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}; \ + gsutil -m rsync -r ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}/assets; \ else \ echo "No assets folder found" ; \ fi ; diff --git a/env.sh.example b/env.sh.example index 99fa5181..627502c4 100644 --- a/env.sh.example +++ b/env.sh.example @@ -26,5 +26,5 @@ export RESOURCE_SUFFIX= # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com -export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/assets${RESOURCE_SUFFIX} +export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/${RESOURCE_SUFFIX} export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 9f1d0544..f4474fb8 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -82,7 +82,7 @@ def tensorflow_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/train_tf_model.py" + train_script_uri = f"{pipeline_files_gcs_path}/assets/train_tf_model.py" hparams = dict( batch_size=100, epochs=5, diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 64f3061c..e0e041a7 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -81,7 +81,7 @@ def xgboost_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/train_xgb_model.py" + train_script_uri = f"{pipeline_files_gcs_path}/assets/train_xgb_model.py" hparams = dict( n_estimators=200, early_stopping_rounds=10, From 020bc8a140472cc09ae7e3eb5026e91f92840f73 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 8 Jun 2023 17:51:32 +0100 Subject: [PATCH 108/238] Revert "Revert including `assets` as part of pipeline_files_gcs_path" This reverts commit 60de248fde1c6ed37180b8fbf6b75412a05f52f8. --- Makefile | 2 +- env.sh.example | 2 +- pipelines/src/pipelines/tensorflow/training/pipeline.py | 2 +- pipelines/src/pipelines/xgboost/training/pipeline.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 7e762bbd..8627895a 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ test-all-components-coverage: ## Run tests with coverage sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ] ; then \ echo "Syncing assets to GCS" && \ - gsutil -m rsync -r ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}/assets; \ + gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}; \ else \ echo "No assets folder found" ; \ fi ; diff --git a/env.sh.example b/env.sh.example index 627502c4..99fa5181 100644 --- a/env.sh.example +++ b/env.sh.example @@ -26,5 +26,5 @@ export RESOURCE_SUFFIX= # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com -export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/${RESOURCE_SUFFIX} +export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/assets${RESOURCE_SUFFIX} export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index f4474fb8..9f1d0544 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -82,7 +82,7 @@ def tensorflow_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/assets/train_tf_model.py" + train_script_uri = f"{pipeline_files_gcs_path}/train_tf_model.py" hparams = dict( batch_size=100, epochs=5, diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index e0e041a7..64f3061c 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -81,7 +81,7 @@ def xgboost_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/assets/train_xgb_model.py" + train_script_uri = f"{pipeline_files_gcs_path}/train_xgb_model.py" hparams = dict( n_estimators=200, early_stopping_rounds=10, From b4623a9268f16f2c4501fff1f5e36b67a542376e Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Fri, 9 Jun 2023 16:39:08 +0100 Subject: [PATCH 109/238] Revert PIPELINE_GCS_PATH to NOT include assets subdirectory --- Makefile | 14 +++++++++----- cloudbuild/e2e-test.yaml | 4 ++-- env.sh.example | 2 +- .../src/pipelines/tensorflow/training/pipeline.py | 2 +- .../src/pipelines/xgboost/training/pipeline.py | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 8627895a..11ea0ccf 100644 --- a/Makefile +++ b/Makefile @@ -71,12 +71,16 @@ test-all-components-coverage: ## Run tests with coverage done sync-assets: ## Sync assets folder to GCS. - @if [ -d "./pipelines/assets/" ] ; then \ - echo "Syncing assets to GCS" && \ - gsutil -m rsync -r -d ./pipelines/assets ${PIPELINE_FILES_GCS_PATH}; \ + @if [ -d "./pipelines/assets/" ]; then \ + echo "Syncing assets to GCS"; \ + if [[ "$${PIPELINE_FILES_GCS_PATH}" == */ ]]; then \ + gsutil -m rsync -r -d ./pipelines/assets "$${PIPELINE_FILES_GCS_PATH}assets"; \ + else \ + gsutil -m rsync -r -d ./pipelines/assets "$${PIPELINE_FILES_GCS_PATH}/assets"; \ + fi; \ else \ - echo "No assets folder found" ; \ - fi ; + echo "No assets folder found"; \ + fi; run: ## Compile pipeline, copy assets to GCS, and run pipeline in sandbox environment. Must specify pipeline=. Optionally specify enable_pipeline_caching= (defaults to default Vertex caching behaviour) @ $(MAKE) compile-pipeline && \ diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index d2754603..431bd515 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -44,8 +44,8 @@ steps: - VERTEX_NETWORK=${_TEST_VERTEX_NETWORK} - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA}/assets - - RESOURCE_SUFFIX=_${SHORT_SHA} + - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} + - RESOURCE_SUFFIX=_${COMMIT_SHA} options: logging: CLOUD_LOGGING_ONLY diff --git a/env.sh.example b/env.sh.example index 99fa5181..627502c4 100644 --- a/env.sh.example +++ b/env.sh.example @@ -26,5 +26,5 @@ export RESOURCE_SUFFIX= # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com -export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/assets${RESOURCE_SUFFIX} +export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/${RESOURCE_SUFFIX} export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 9f1d0544..f4474fb8 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -82,7 +82,7 @@ def tensorflow_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/train_tf_model.py" + train_script_uri = f"{pipeline_files_gcs_path}/assets/train_tf_model.py" hparams = dict( batch_size=100, epochs=5, diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 64f3061c..e0e041a7 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -81,7 +81,7 @@ def xgboost_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/train_xgb_model.py" + train_script_uri = f"{pipeline_files_gcs_path}/assets/train_xgb_model.py" hparams = dict( n_estimators=200, early_stopping_rounds=10, From b318d077c700e2280e1856ab8759d51336ffb545 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Fri, 9 Jun 2023 16:42:24 +0100 Subject: [PATCH 110/238] update scheduled_jobs.auto.tfvars.example --- .../scheduled_jobs.auto.tfvars.example | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example b/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example index a92fd2df..a08ca038 100644 --- a/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example +++ b/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example @@ -26,7 +26,7 @@ cloud_schedulers_config = { pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west2" - pipeline_files_gcs_path = "gs://my-assets-bucket/" + pipeline_files_gcs_path = "gs://my-assets-bucket//assets" ingestion_project_id = "my-project-id" model_name = "xgboost_with_preprocessing" model_label = "label_name" @@ -49,7 +49,7 @@ cloud_schedulers_config = { pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west4" - pipeline_files_gcs_path = "gs://my-assets-bucket/" + pipeline_files_gcs_path = "gs://my-assets-bucket//assets" ingestion_project_id = "my-project-id" model_name = "xgboost_with_preprocessing" model_label = "label_name" @@ -70,12 +70,12 @@ cloud_schedulers_config = { description = "Trigger my TensorFlow training pipeline in Vertex" schedule = "0 0 * * 0" time_zone = "UTC" - template_path = "gs://my-assets-bucket//training/training.json" + template_path = "gs://my-assets-bucket//assets/training/training.json" enable_caching = null pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west4" - pipeline_files_gcs_path = "gs://my-assets-bucket/" + pipeline_files_gcs_path = "gs://my-assets-bucket//assets" ingestion_project_id = "my-project-id" model_name = "tensorflow_with_preprocessing" model_label = "label_name" @@ -98,7 +98,7 @@ cloud_schedulers_config = { pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west4" - pipeline_files_gcs_path = "gs://my-assets-bucket/" + pipeline_files_gcs_path = "gs://my-assets-bucket//assets" ingestion_project_id = "my-project-id" model_name = "tensorflow_with_preprocessing" model_label = "label_name" From 29e25b02180bb8427b690e249b1bd74a94288ee8 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Mon, 12 Jun 2023 12:49:43 +0100 Subject: [PATCH 111/238] Add single quotes in env.sh.example comment --- env.sh.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/env.sh.example b/env.sh.example index 627502c4..24622887 100644 --- a/env.sh.example +++ b/env.sh.example @@ -21,7 +21,7 @@ export VERTEX_LOCATION=europe-west2 export VERTEX_NETWORK= # optional export VERTEX_PROJECT_ID=my-gcp-project -# Suffix (e.g. _) to facilitate running concurrent pipelines in the same Google Cloud project. Populate if working in a team to avoid overwriting resources during development +# Suffix (e.g. '_') to facilitate running concurrent pipelines in the same Google Cloud project. Populate if working in a team to avoid overwriting resources during development export RESOURCE_SUFFIX= # Leave as-is From 14f354d49ede7ab3f526fadf14985b2fc6681d04 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Wed, 14 Jun 2023 11:02:59 +0100 Subject: [PATCH 112/238] revert changes made to pipeline_files_gcs_path in scheduled_jobs.auto.tfvars.example --- .../scheduled_jobs.auto.tfvars.example | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example b/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example index a08ca038..a92fd2df 100644 --- a/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example +++ b/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example @@ -26,7 +26,7 @@ cloud_schedulers_config = { pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west2" - pipeline_files_gcs_path = "gs://my-assets-bucket//assets" + pipeline_files_gcs_path = "gs://my-assets-bucket/" ingestion_project_id = "my-project-id" model_name = "xgboost_with_preprocessing" model_label = "label_name" @@ -49,7 +49,7 @@ cloud_schedulers_config = { pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west4" - pipeline_files_gcs_path = "gs://my-assets-bucket//assets" + pipeline_files_gcs_path = "gs://my-assets-bucket/" ingestion_project_id = "my-project-id" model_name = "xgboost_with_preprocessing" model_label = "label_name" @@ -70,12 +70,12 @@ cloud_schedulers_config = { description = "Trigger my TensorFlow training pipeline in Vertex" schedule = "0 0 * * 0" time_zone = "UTC" - template_path = "gs://my-assets-bucket//assets/training/training.json" + template_path = "gs://my-assets-bucket//training/training.json" enable_caching = null pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west4" - pipeline_files_gcs_path = "gs://my-assets-bucket//assets" + pipeline_files_gcs_path = "gs://my-assets-bucket/" ingestion_project_id = "my-project-id" model_name = "tensorflow_with_preprocessing" model_label = "label_name" @@ -98,7 +98,7 @@ cloud_schedulers_config = { pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west4" - pipeline_files_gcs_path = "gs://my-assets-bucket//assets" + pipeline_files_gcs_path = "gs://my-assets-bucket/" ingestion_project_id = "my-project-id" model_name = "tensorflow_with_preprocessing" model_label = "label_name" From d240286fa957bcee58627ea47b2b86aa3f402762 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Thu, 15 Jun 2023 12:41:00 +0100 Subject: [PATCH 113/238] Handle resource suffix in Makefile --- Makefile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 11ea0ccf..ad275e40 100644 --- a/Makefile +++ b/Makefile @@ -73,11 +73,8 @@ test-all-components-coverage: ## Run tests with coverage sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ]; then \ echo "Syncing assets to GCS"; \ - if [[ "$${PIPELINE_FILES_GCS_PATH}" == */ ]]; then \ - gsutil -m rsync -r -d ./pipelines/assets "$${PIPELINE_FILES_GCS_PATH}assets"; \ - else \ - gsutil -m rsync -r -d ./pipelines/assets "$${PIPELINE_FILES_GCS_PATH}/assets"; \ - fi; \ + PIPELINE_FILES_GCS_PATH=$${PIPELINE_FILES_GCS_PATH}$(if $(strip $(RESOURCE_SUFFIX)),/$(RESOURCE_SUFFIX)); \ + gsutil -m rsync -r -d ./pipelines/assets "$${PIPELINE_FILES_GCS_PATH}/assets"; \ else \ echo "No assets folder found"; \ fi; From cbac0c082f219d7a5de4bff9a6853f29cc6cd209 Mon Sep 17 00:00:00 2001 From: rachel-datatonic Date: Mon, 26 Jun 2023 18:02:17 +0100 Subject: [PATCH 114/238] set the logging level to DEBUG in the sandbox_run function without causing duplicated logs --- pipelines/src/pipelines/trigger/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pipelines/src/pipelines/trigger/main.py b/pipelines/src/pipelines/trigger/main.py index 8aae023f..491361e2 100644 --- a/pipelines/src/pipelines/trigger/main.py +++ b/pipelines/src/pipelines/trigger/main.py @@ -191,7 +191,9 @@ def sandbox_run(args: List[str] = None) -> aiplatform.PipelineJob: Returns the PipelineJob object of the triggered pipeline run. Usage: python main.py --template_path=pipeline.json --enable_caching=true """ - logging.basicConfig(level=logging.DEBUG) + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + logger.debug("This is a debug message") parser = argparse.ArgumentParser() parser.add_argument( From 40ac52f92659d7fff4ce1c0b961ab22627198378 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Mon, 26 Jun 2023 21:20:13 +0200 Subject: [PATCH 115/238] Fix RESOURCE_SUFFIX bug --- Makefile | 12 ++++++++++-- env.sh.example | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index ad275e40..c7cff312 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,15 @@ -include env.sh export +# Check if RESOURCE_SUFFIX is empty +ifeq ($(strip $(RESOURCE_SUFFIX)),) + PIPELINE_FILES_GCS_PATH := gs://${VERTEX_PROJECT_ID}-pl-assets +else + PIPELINE_FILES_GCS_PATH := gs://${VERTEX_PROJECT_ID}-pl-assets/$(RESOURCE_SUFFIX) +endif + +export PIPELINE_FILES_GCS_PATH + help: ## Display this help screen @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @@ -73,8 +82,7 @@ test-all-components-coverage: ## Run tests with coverage sync-assets: ## Sync assets folder to GCS. @if [ -d "./pipelines/assets/" ]; then \ echo "Syncing assets to GCS"; \ - PIPELINE_FILES_GCS_PATH=$${PIPELINE_FILES_GCS_PATH}$(if $(strip $(RESOURCE_SUFFIX)),/$(RESOURCE_SUFFIX)); \ - gsutil -m rsync -r -d ./pipelines/assets "$${PIPELINE_FILES_GCS_PATH}/assets"; \ + gsutil -m rsync -r -d ./pipelines/assets $(PIPELINE_FILES_GCS_PATH)/assets ; \ else \ echo "No assets folder found"; \ fi; diff --git a/env.sh.example b/env.sh.example index 24622887..d8d355a7 100644 --- a/env.sh.example +++ b/env.sh.example @@ -26,5 +26,5 @@ export RESOURCE_SUFFIX= # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com -export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/${RESOURCE_SUFFIX} +export PIPELINE_FILES_GCS_PATH="gs://${VERTEX_PROJECT_ID}-pl-assets" export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root From 2492cffcc04b6829bbad7b14e11f0669639f146b Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Mon, 26 Jun 2023 21:23:46 +0200 Subject: [PATCH 116/238] Remove double quotes from PIPELINE_FILES_GCS_PATH --- env.sh.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/env.sh.example b/env.sh.example index d8d355a7..bd838c01 100644 --- a/env.sh.example +++ b/env.sh.example @@ -26,5 +26,5 @@ export RESOURCE_SUFFIX= # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com -export PIPELINE_FILES_GCS_PATH="gs://${VERTEX_PROJECT_ID}-pl-assets" +export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root From 9d6a6a8b6eb76c427f2598c2c53ed55da4870bd6 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Tue, 27 Jun 2023 09:29:38 +0200 Subject: [PATCH 117/238] Fix e2e tests by using RESOURCE_SUFFIX when uploading assets --- cloudbuild/e2e-test.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 431bd515..2f5039d4 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -24,7 +24,9 @@ steps: - | mkdir -p ${COMMIT_SHA}/assets && \ cp -r pipelines/assets ${COMMIT_SHA} && \ - gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} + gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${RESOURCE_SUFFIX} + env: + - RESOURCE_SUFFIX=_${COMMIT_SHA} # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines @@ -44,8 +46,8 @@ steps: - VERTEX_NETWORK=${_TEST_VERTEX_NETWORK} - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} - RESOURCE_SUFFIX=_${COMMIT_SHA} + - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${RESOURCE_SUFFIX} options: logging: CLOUD_LOGGING_ONLY From a8a22afcb41fba1740394730241d4f27f3e0303c Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Tue, 27 Jun 2023 10:30:22 +0200 Subject: [PATCH 118/238] Use custom substitutions for RESOURCE_SUFFIX in e2e-tests --- cloudbuild/e2e-test.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 2f5039d4..71aa800a 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -25,8 +25,8 @@ steps: mkdir -p ${COMMIT_SHA}/assets && \ cp -r pipelines/assets ${COMMIT_SHA} && \ gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${RESOURCE_SUFFIX} - env: - - RESOURCE_SUFFIX=_${COMMIT_SHA} + substitutions: + RESOURCE_SUFFIX: _${COMMIT_SHA} # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines @@ -46,8 +46,10 @@ steps: - VERTEX_NETWORK=${_TEST_VERTEX_NETWORK} - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - - RESOURCE_SUFFIX=_${COMMIT_SHA} + - RESOURCE_SUFFIX=${RESOURCE_SUFFIX} - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${RESOURCE_SUFFIX} + substitutions: + RESOURCE_SUFFIX: _${COMMIT_SHA} options: logging: CLOUD_LOGGING_ONLY From 938200cb2633d0901343ad5e309fb31e10bb6597 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Tue, 27 Jun 2023 10:32:30 +0200 Subject: [PATCH 119/238] Move substitutions to EOF in e2e-test.yaml --- cloudbuild/e2e-test.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 71aa800a..861a48d1 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -25,8 +25,6 @@ steps: mkdir -p ${COMMIT_SHA}/assets && \ cp -r pipelines/assets ${COMMIT_SHA} && \ gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${RESOURCE_SUFFIX} - substitutions: - RESOURCE_SUFFIX: _${COMMIT_SHA} # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines @@ -48,8 +46,9 @@ steps: - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - RESOURCE_SUFFIX=${RESOURCE_SUFFIX} - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${RESOURCE_SUFFIX} - substitutions: - RESOURCE_SUFFIX: _${COMMIT_SHA} + +substitutions: + RESOURCE_SUFFIX: _${COMMIT_SHA} options: logging: CLOUD_LOGGING_ONLY From 97a080f6b10bafd715549a1c64f2f44d95914a07 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Tue, 27 Jun 2023 10:35:17 +0200 Subject: [PATCH 120/238] ADD underscore to RESOURCE_SUFFIX in e2e-tests.yaml --- cloudbuild/e2e-test.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 861a48d1..237784e5 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -24,7 +24,7 @@ steps: - | mkdir -p ${COMMIT_SHA}/assets && \ cp -r pipelines/assets ${COMMIT_SHA} && \ - gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${RESOURCE_SUFFIX} + gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${_RESOURCE_SUFFIX} # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines @@ -44,11 +44,11 @@ steps: - VERTEX_NETWORK=${_TEST_VERTEX_NETWORK} - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - - RESOURCE_SUFFIX=${RESOURCE_SUFFIX} - - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${RESOURCE_SUFFIX} + - RESOURCE_SUFFIX=${_RESOURCE_SUFFIX} + - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${_RESOURCE_SUFFIX} substitutions: - RESOURCE_SUFFIX: _${COMMIT_SHA} + _RESOURCE_SUFFIX: _${COMMIT_SHA} options: logging: CLOUD_LOGGING_ONLY From e9d410b608bea329827c5ff1ecd07ca530581307 Mon Sep 17 00:00:00 2001 From: rachel-datatonic Date: Tue, 27 Jun 2023 10:17:58 +0100 Subject: [PATCH 121/238] remove log in sandbox_run to avoid duplicated log --- pipelines/src/pipelines/trigger/main.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pipelines/src/pipelines/trigger/main.py b/pipelines/src/pipelines/trigger/main.py index 491361e2..fc8263fc 100644 --- a/pipelines/src/pipelines/trigger/main.py +++ b/pipelines/src/pipelines/trigger/main.py @@ -15,7 +15,6 @@ import argparse import base64 import json -import logging import os import distutils.util from typing import Optional, List @@ -191,10 +190,6 @@ def sandbox_run(args: List[str] = None) -> aiplatform.PipelineJob: Returns the PipelineJob object of the triggered pipeline run. Usage: python main.py --template_path=pipeline.json --enable_caching=true """ - logger = logging.getLogger(__name__) - logger.setLevel(logging.DEBUG) - logger.debug("This is a debug message") - parser = argparse.ArgumentParser() parser.add_argument( "--template_path", help="Path to the compiled pipeline (JSON)", type=str From 600e3e9c6d3118064d35f3a7c6f456508c4424b3 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Tue, 27 Jun 2023 11:22:35 +0200 Subject: [PATCH 122/238] Simplify solution by giving RESOURCE_SUFFIX a default value --- Makefile | 8 -------- cloudbuild/e2e-test.yaml | 9 +++------ env.sh.example | 6 +++--- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index c7cff312..2ce9ca3b 100644 --- a/Makefile +++ b/Makefile @@ -15,14 +15,6 @@ -include env.sh export -# Check if RESOURCE_SUFFIX is empty -ifeq ($(strip $(RESOURCE_SUFFIX)),) - PIPELINE_FILES_GCS_PATH := gs://${VERTEX_PROJECT_ID}-pl-assets -else - PIPELINE_FILES_GCS_PATH := gs://${VERTEX_PROJECT_ID}-pl-assets/$(RESOURCE_SUFFIX) -endif - -export PIPELINE_FILES_GCS_PATH help: ## Display this help screen @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 237784e5..431bd515 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -24,7 +24,7 @@ steps: - | mkdir -p ${COMMIT_SHA}/assets && \ cp -r pipelines/assets ${COMMIT_SHA} && \ - gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${_RESOURCE_SUFFIX} + gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines @@ -44,11 +44,8 @@ steps: - VERTEX_NETWORK=${_TEST_VERTEX_NETWORK} - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - - RESOURCE_SUFFIX=${_RESOURCE_SUFFIX} - - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${_RESOURCE_SUFFIX} - -substitutions: - _RESOURCE_SUFFIX: _${COMMIT_SHA} + - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} + - RESOURCE_SUFFIX=_${COMMIT_SHA} options: logging: CLOUD_LOGGING_ONLY diff --git a/env.sh.example b/env.sh.example index bd838c01..9041c783 100644 --- a/env.sh.example +++ b/env.sh.example @@ -21,10 +21,10 @@ export VERTEX_LOCATION=europe-west2 export VERTEX_NETWORK= # optional export VERTEX_PROJECT_ID=my-gcp-project -# Suffix (e.g. '_') to facilitate running concurrent pipelines in the same Google Cloud project. Populate if working in a team to avoid overwriting resources during development -export RESOURCE_SUFFIX= +# Suffix (e.g. '_') to facilitate running concurrent pipelines in the same Google Cloud project. Change if working in a team to avoid overwriting resources during development +export RESOURCE_SUFFIX=_default # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com -export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets +export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/${RESOURCE_SUFFIX} export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root From 25a39906cef57f2ca6c1c672e8746bae66f5d8c3 Mon Sep 17 00:00:00 2001 From: jan-zajac-dt Date: Tue, 27 Jun 2023 14:42:49 +0200 Subject: [PATCH 123/238] remove underscore from suffix in env.sh.example and adjust pipelines to add it for BigQuery tables --- cloudbuild/e2e-test.yaml | 2 +- env.sh.example | 4 ++-- pipelines/src/pipelines/tensorflow/training/pipeline.py | 2 +- pipelines/src/pipelines/xgboost/training/pipeline.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 431bd515..b71430d2 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -45,7 +45,7 @@ steps: - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} - - RESOURCE_SUFFIX=_${COMMIT_SHA} + - RESOURCE_SUFFIX=${COMMIT_SHA} options: logging: CLOUD_LOGGING_ONLY diff --git a/env.sh.example b/env.sh.example index 9041c783..3e747dfe 100644 --- a/env.sh.example +++ b/env.sh.example @@ -21,8 +21,8 @@ export VERTEX_LOCATION=europe-west2 export VERTEX_NETWORK= # optional export VERTEX_PROJECT_ID=my-gcp-project -# Suffix (e.g. '_') to facilitate running concurrent pipelines in the same Google Cloud project. Change if working in a team to avoid overwriting resources during development -export RESOURCE_SUFFIX=_default +# Suffix (e.g. '') to facilitate running concurrent pipelines in the same Google Cloud project. Change if working in a team to avoid overwriting resources during development +export RESOURCE_SUFFIX=default # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index f4474fb8..371928f5 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -75,7 +75,7 @@ def tensorflow_pipeline( label_column_name = "total_fare" time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_tf_training" + str(resource_suffix) # suffix to table names + table_suffix = f"_tf_training_{resource_suffix}" # suffix to table names ingested_table = "ingested_data" + table_suffix preprocessed_table = "preprocessed_data" + table_suffix train_table = "train_data" + table_suffix diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index e0e041a7..7db36231 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -74,7 +74,7 @@ def xgboost_pipeline( label_column_name = "total_fare" time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_xgb_training" + str(resource_suffix) # suffix to table names + table_suffix = f"_xgb_training_{resource_suffix}" # suffix to table names ingested_table = "ingested_data" + table_suffix preprocessed_table = "preprocessed_data" + table_suffix train_table = "train_data" + table_suffix From a981b7329e715b301a2d38ce50d9ffb3430cfcaf Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Mon, 10 Jul 2023 21:21:25 +0100 Subject: [PATCH 124/238] feat: upgrade to KFP V2 --- components/bigquery-components/poetry.lock | 555 +--------------- components/bigquery-components/pyproject.toml | 2 +- .../extract_bq_to_dataset.py | 2 +- .../tests/test_extract_bq_to_dataset.py | 2 +- components/vertex-components/poetry.lock | 602 +---------------- components/vertex-components/pyproject.toml | 4 +- .../src/vertex_components/custom_train_job.py | 2 +- .../import_model_evaluation.py | 2 +- .../src/vertex_components/lookup_model.py | 2 +- .../vertex_components/model_batch_predict.py | 2 +- .../vertex_components/update_best_model.py | 2 +- .../tests/test_custom_training_job.py | 2 +- .../tests/test_import_model_evaluation.py | 2 +- .../tests/test_lookup_model.py | 2 +- .../tests/test_model_batch_predict.py | 2 +- .../tests/test_update_best_model.py | 2 +- pipelines/poetry.lock | 619 +----------------- pipelines/pyproject.toml | 3 +- .../tensorflow/prediction/pipeline.py | 2 +- .../pipelines/tensorflow/training/pipeline.py | 2 +- .../pipelines/xgboost/prediction/pipeline.py | 2 +- .../pipelines/xgboost/training/pipeline.py | 2 +- pipelines/tests/e2e/test_e2e.py | 2 +- 23 files changed, 113 insertions(+), 1706 deletions(-) diff --git a/components/bigquery-components/poetry.lock b/components/bigquery-components/poetry.lock index 8f0f3273..7fa1ecb4 100644 --- a/components/bigquery-components/poetry.lock +++ b/components/bigquery-components/poetry.lock @@ -1,44 +1,9 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. - -[[package]] -name = "absl-py" -version = "1.4.0" -description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "absl-py-1.4.0.tar.gz", hash = "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"}, - {file = "absl_py-1.4.0-py3-none-any.whl", hash = "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47"}, -] - -[[package]] -name = "attrs" -version = "23.1.0" -description = "Classes Without Boilerplate" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. [[package]] name = "cachetools" version = "5.3.1" description = "Extensible memoizing collections and decorators" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -50,7 +15,6 @@ files = [ name = "certifi" version = "2023.5.7" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -62,7 +26,6 @@ files = [ name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -147,7 +110,6 @@ files = [ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -159,23 +121,10 @@ files = [ colorama = {version = "*", markers = "platform_system == \"Windows\""} importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -[[package]] -name = "cloudpickle" -version = "2.2.1" -description = "Extended pickling support for Python objects" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "cloudpickle-2.2.1-py3-none-any.whl", hash = "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f"}, - {file = "cloudpickle-2.2.1.tar.gz", hash = "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5"}, -] - [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -183,29 +132,10 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "deprecated" -version = "1.2.14" -description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, - {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, -] - -[package.dependencies] -wrapt = ">=1.10,<2" - -[package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] - [[package]] name = "docstring-parser" version = "0.15" description = "Parse Python docstrings in reST, Google and Numpydoc format" -category = "main" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -217,7 +147,6 @@ files = [ name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -228,26 +157,10 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "fire" -version = "0.5.0" -description = "A library for automatically generating command line interfaces." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "fire-0.5.0.tar.gz", hash = "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6"}, -] - -[package.dependencies] -six = "*" -termcolor = "*" - [[package]] name = "google-api-core" version = "2.11.0" description = "Google API client core library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -268,31 +181,10 @@ grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] -[[package]] -name = "google-api-python-client" -version = "1.12.11" -description = "Google API Client Library for Python" -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -files = [ - {file = "google-api-python-client-1.12.11.tar.gz", hash = "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9"}, - {file = "google_api_python_client-1.12.11-py2.py3-none-any.whl", hash = "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.21.0,<3dev", markers = "python_version >= \"3\""} -google-auth = {version = ">=1.16.0,<3dev", markers = "python_version >= \"3\""} -google-auth-httplib2 = ">=0.0.3" -httplib2 = ">=0.15.0,<1dev" -six = ">=1.13.0,<2dev" -uritemplate = ">=3.0.0,<4dev" - [[package]] name = "google-auth" version = "2.19.0" description = "Google Authentication Library" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -314,28 +206,10 @@ pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0dev)"] -[[package]] -name = "google-auth-httplib2" -version = "0.1.0" -description = "Google Authentication Library: httplib2 transport" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, - {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, -] - -[package.dependencies] -google-auth = "*" -httplib2 = ">=0.15.0" -six = "*" - [[package]] name = "google-cloud-bigquery" version = "2.30.0" description = "Google BigQuery API client library" -category = "dev" optional = false python-versions = ">=3.6, <3.11" files = [ @@ -367,7 +241,6 @@ tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] name = "google-cloud-core" version = "2.3.2" description = "Google Cloud API client core library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -376,7 +249,7 @@ files = [ ] [package.dependencies] -google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.25.0,<3.0dev" [package.extras] @@ -386,7 +259,6 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)"] name = "google-cloud-storage" version = "2.9.0" description = "Google Cloud Storage API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -395,7 +267,7 @@ files = [ ] [package.dependencies] -google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.25.0,<3.0dev" google-cloud-core = ">=2.3.0,<3.0dev" google-resumable-media = ">=2.3.2" @@ -408,7 +280,6 @@ protobuf = ["protobuf (<5.0.0dev)"] name = "google-crc32c" version = "1.5.0" description = "A python wrapper of the C library 'Google CRC32C'" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -489,7 +360,6 @@ testing = ["pytest"] name = "google-resumable-media" version = "2.5.0" description = "Utilities for Google Media Downloads and Resumable Uploads" -category = "main" optional = false python-versions = ">= 3.7" files = [ @@ -508,7 +378,6 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] name = "googleapis-common-protos" version = "1.59.0" description = "Common protobufs used in Google APIs" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -526,7 +395,6 @@ grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] name = "grpcio" version = "1.54.2" description = "HTTP/2-based RPC framework" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -584,7 +452,6 @@ protobuf = ["grpcio-tools (>=1.54.2)"] name = "grpcio-status" version = "1.48.2" description = "Status proto mapping for gRPC" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -597,26 +464,10 @@ googleapis-common-protos = ">=1.5.5" grpcio = ">=1.48.2" protobuf = ">=3.12.0" -[[package]] -name = "httplib2" -version = "0.22.0" -description = "A comprehensive HTTP client library." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, - {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, -] - -[package.dependencies] -pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} - [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -628,7 +479,6 @@ files = [ name = "importlib-metadata" version = "6.6.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -645,30 +495,10 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker perf = ["ipython"] testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] -[[package]] -name = "importlib-resources" -version = "5.12.0" -description = "Read resources from Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, - {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -676,79 +506,44 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "jsonschema" -version = "4.17.3" -description = "An implementation of JSON Schema validation for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, - {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, -] - -[package.dependencies] -attrs = ">=17.4.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} -pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - [[package]] name = "kfp" -version = "1.8.21" -description = "KubeFlow Pipelines SDK" -category = "main" +version = "2.0.1" +description = "Kubeflow Pipelines SDK" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.7.0,<3.12.0" files = [ - {file = "kfp-1.8.21.tar.gz", hash = "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c"}, + {file = "kfp-2.0.1.tar.gz", hash = "sha256:4a65811778c3a45892d8d11c0f95ba5e410476157ed63cacb1dd82316f60cd4d"}, ] [package.dependencies] -absl-py = ">=0.9,<2" -click = ">=7.1.2,<9" -cloudpickle = ">=2.0.0,<3" -Deprecated = ">=1.2.7,<2" +click = ">=8.0.0,<9" docstring-parser = ">=0.7.3,<1" -fire = ">=0.3.1,<1" -google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" -google-api-python-client = ">=1.7.8,<2" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.6.1,<3" -google-cloud-storage = ">=1.20.0,<3" -jsonschema = ">=3.0.1,<5" -kfp-pipeline-spec = ">=0.1.16,<0.2.0" -kfp-server-api = ">=1.1.2,<2.0.0" -kubernetes = ">=8.0.0,<26" +google-cloud-storage = ">=2.2.1,<3" +kfp-pipeline-spec = "0.2.2" +kfp-server-api = ">=2.0.0,<2.1.0" +kubernetes = ">=8.0.0,<27" protobuf = ">=3.13.0,<4" -pydantic = ">=1.8.2,<2" -PyYAML = ">=5.3,<6" +PyYAML = ">=5.3,<7" requests-toolbelt = ">=0.8.0,<1" -strip-hints = ">=0.1.8,<1" tabulate = ">=0.8.6,<1" -typer = ">=0.3.2,<1.0" typing-extensions = {version = ">=3.7.4,<5", markers = "python_version < \"3.9\""} -uritemplate = ">=3.0.1,<4" -urllib3 = "<2" +urllib3 = "<2.0.0" [package.extras] -all = ["docker"] +all = ["docker", "kfp-kubernetes (<2)"] +kubernetes = ["kfp-kubernetes (<2)"] [[package]] name = "kfp-pipeline-spec" -version = "0.1.16" +version = "0.2.2" description = "Kubeflow Pipelines pipeline spec" -category = "main" optional = false python-versions = ">=3.7.0" files = [ - {file = "kfp_pipeline_spec-0.1.16-py3-none-any.whl", hash = "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25"}, + {file = "kfp_pipeline_spec-0.2.2-py3-none-any.whl", hash = "sha256:e83b58ffed3f7ca154d62ab20e14dc9a1a3c9dad589e856dd2b421e226f3b8d0"}, ] [package.dependencies] @@ -756,13 +551,12 @@ protobuf = ">=3.13.0,<4" [[package]] name = "kfp-server-api" -version = "1.8.5" +version = "2.0.0" description = "Kubeflow Pipelines API" -category = "main" optional = false python-versions = "*" files = [ - {file = "kfp-server-api-1.8.5.tar.gz", hash = "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de"}, + {file = "kfp-server-api-2.0.0.tar.gz", hash = "sha256:c0462045244a69211bc82a8d6af6602ae8b4f716ee0ac0be5dc34b7ea76c7bd1"}, ] [package.dependencies] @@ -775,7 +569,6 @@ urllib3 = ">=1.15" name = "kubernetes" version = "25.3.0" description = "Kubernetes python client" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -793,7 +586,7 @@ requests-oauthlib = "*" setuptools = ">=21.0.0" six = ">=1.9.0" urllib3 = ">=1.24.2" -websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" +websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.dev0 || >=0.43.dev0" [package.extras] adal = ["adal (>=1.0.2)"] @@ -802,7 +595,6 @@ adal = ["adal (>=1.0.2)"] name = "oauthlib" version = "3.2.2" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -819,7 +611,6 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -827,23 +618,10 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] -[[package]] -name = "pkgutil-resolve-name" -version = "1.3.10" -description = "Resolve a name to an object." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, - {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, -] - [[package]] name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -862,7 +640,6 @@ testing = ["pytest", "pytest-benchmark"] name = "proto-plus" version = "1.22.2" description = "Beautiful, Pythonic protocol buffers." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -880,7 +657,6 @@ testing = ["google-api-core[grpc] (>=1.31.5)"] name = "protobuf" version = "3.20.3" description = "Protocol Buffers" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -912,7 +688,6 @@ files = [ name = "pyasn1" version = "0.5.0" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -924,7 +699,6 @@ files = [ name = "pyasn1-modules" version = "0.3.0" description = "A collection of ASN.1-based protocols modules" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -935,116 +709,10 @@ files = [ [package.dependencies] pyasn1 = ">=0.4.6,<0.6.0" -[[package]] -name = "pydantic" -version = "1.10.8" -description = "Data validation and settings management using python type hints" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, - {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, - {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, - {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, - {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, - {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, - {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, - {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, - {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, - {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, - {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, - {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, - {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, - {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, - {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, - {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, - {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, - {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, - {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, - {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, - {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, - {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, - {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, - {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, - {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, - {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, - {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, - {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, - {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, - {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, - {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, - {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, - {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, - {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, - {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, - {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, -] - -[package.dependencies] -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" -optional = false -python-versions = ">=3.6.8" -files = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pyrsistent" -version = "0.19.3" -description = "Persistent/Functional/Immutable data structures" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, - {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, - {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, - {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, - {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, - {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, -] - [[package]] name = "pytest" version = "7.3.1" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1068,7 +736,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1083,7 +750,6 @@ six = ">=1.5" name = "pyyaml" version = "5.4.1" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -1122,7 +788,6 @@ files = [ name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1144,7 +809,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-oauthlib" version = "1.3.1" description = "OAuthlib authentication support for Requests." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1163,7 +827,6 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"] name = "requests-toolbelt" version = "0.10.1" description = "A utility belt for advanced users of python-requests" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1178,7 +841,6 @@ requests = ">=2.0.1,<3.0.0" name = "rsa" version = "4.9" description = "Pure-Python RSA implementation" -category = "main" optional = false python-versions = ">=3.6,<4" files = [ @@ -1193,7 +855,6 @@ pyasn1 = ">=0.1.3" name = "setuptools" version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1210,7 +871,6 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1218,25 +878,10 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[[package]] -name = "strip-hints" -version = "0.1.10" -description = "Function and command-line program to strip Python type hints." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "strip-hints-0.1.10.tar.gz", hash = "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f"}, -] - -[package.dependencies] -wheel = "*" - [[package]] name = "tabulate" version = "0.9.0" description = "Pretty-print tabular data" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1247,26 +892,10 @@ files = [ [package.extras] widechars = ["wcwidth"] -[[package]] -name = "termcolor" -version = "2.3.0" -description = "ANSI color formatting for output in terminal" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, - {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, -] - -[package.extras] -tests = ["pytest", "pytest-cov"] - [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1274,33 +903,10 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "typer" -version = "0.9.0" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, - {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, -] - -[package.dependencies] -click = ">=7.1.1,<9.0.0" -typing-extensions = ">=3.7.4.3" - -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] - [[package]] name = "typing-extensions" version = "4.6.2" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1308,23 +914,10 @@ files = [ {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, ] -[[package]] -name = "uritemplate" -version = "3.0.1" -description = "URI templates" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, - {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, -] - [[package]] name = "urllib3" version = "1.26.16" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -1341,7 +934,6 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "websocket-client" version = "1.5.2" description = "WebSocket client for Python with low level API options" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1354,111 +946,10 @@ docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] optional = ["python-socks", "wsaccel"] test = ["websockets"] -[[package]] -name = "wheel" -version = "0.40.0" -description = "A built-package format for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, - {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, -] - -[package.extras] -test = ["pytest (>=6.0.0)"] - -[[package]] -name = "wrapt" -version = "1.15.0" -description = "Module for decorators, wrappers and monkey patching." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, - {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, - {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, - {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, - {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, - {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, - {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, - {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, - {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, - {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, - {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, - {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, - {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, - {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, - {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, - {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, - {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, - {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, - {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, -] - [[package]] name = "zipp" version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1473,4 +964,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = ">=3.7,<3.11" -content-hash = "d7bc7cb1b7d234cc70d56b33a1253a6a52b0f6d9ab3114fd47ab47dbfcdadfa0" +content-hash = "6f7ca2171a974f20e9e0c7d38859b0570337711f678f0fed05a9560ce299f0e0" diff --git a/components/bigquery-components/pyproject.toml b/components/bigquery-components/pyproject.toml index ee3e0477..9d6cd0db 100644 --- a/components/bigquery-components/pyproject.toml +++ b/components/bigquery-components/pyproject.toml @@ -12,7 +12,7 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.7,<3.11" -kfp = "==1.8.21" +kfp = "^2.0.1" [tool.poetry.group.dev.dependencies] google-cloud-bigquery = "2.30.0" diff --git a/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py b/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py index 3effe09a..4846e448 100644 --- a/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py +++ b/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from kfp.v2.dsl import Dataset, Output, component +from kfp.dsl import Dataset, Output, component @component( diff --git a/components/bigquery-components/tests/test_extract_bq_to_dataset.py b/components/bigquery-components/tests/test_extract_bq_to_dataset.py index be3084e6..dec9f1eb 100644 --- a/components/bigquery-components/tests/test_extract_bq_to_dataset.py +++ b/components/bigquery-components/tests/test_extract_bq_to_dataset.py @@ -1,5 +1,5 @@ import google.cloud.bigquery # noqa -from kfp.v2.dsl import Dataset +from kfp.dsl import Dataset from unittest import mock import bigquery_components diff --git a/components/vertex-components/poetry.lock b/components/vertex-components/poetry.lock index ca09c083..c03e62bb 100644 --- a/components/vertex-components/poetry.lock +++ b/components/vertex-components/poetry.lock @@ -1,44 +1,9 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. - -[[package]] -name = "absl-py" -version = "1.4.0" -description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "absl-py-1.4.0.tar.gz", hash = "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"}, - {file = "absl_py-1.4.0-py3-none-any.whl", hash = "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47"}, -] - -[[package]] -name = "attrs" -version = "23.1.0" -description = "Classes Without Boilerplate" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. [[package]] name = "cachetools" version = "5.3.1" description = "Extensible memoizing collections and decorators" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -50,7 +15,6 @@ files = [ name = "certifi" version = "2023.5.7" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -62,7 +26,6 @@ files = [ name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -147,7 +110,6 @@ files = [ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -159,23 +121,10 @@ files = [ colorama = {version = "*", markers = "platform_system == \"Windows\""} importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -[[package]] -name = "cloudpickle" -version = "2.2.1" -description = "Extended pickling support for Python objects" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "cloudpickle-2.2.1-py3-none-any.whl", hash = "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f"}, - {file = "cloudpickle-2.2.1.tar.gz", hash = "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5"}, -] - [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -183,29 +132,10 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "deprecated" -version = "1.2.14" -description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, - {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, -] - -[package.dependencies] -wrapt = ">=1.10,<2" - -[package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] - [[package]] name = "docstring-parser" version = "0.15" description = "Parse Python docstrings in reST, Google and Numpydoc format" -category = "main" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -217,7 +147,6 @@ files = [ name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -228,26 +157,10 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "fire" -version = "0.5.0" -description = "A library for automatically generating command line interfaces." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "fire-0.5.0.tar.gz", hash = "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6"}, -] - -[package.dependencies] -six = "*" -termcolor = "*" - [[package]] name = "google-api-core" version = "2.11.0" description = "Google API client core library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -268,31 +181,10 @@ grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] -[[package]] -name = "google-api-python-client" -version = "1.12.11" -description = "Google API Client Library for Python" -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -files = [ - {file = "google-api-python-client-1.12.11.tar.gz", hash = "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9"}, - {file = "google_api_python_client-1.12.11-py2.py3-none-any.whl", hash = "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.21.0,<3dev", markers = "python_version >= \"3\""} -google-auth = {version = ">=1.16.0,<3dev", markers = "python_version >= \"3\""} -google-auth-httplib2 = ">=0.0.3" -httplib2 = ">=0.15.0,<1dev" -six = ">=1.13.0,<2dev" -uritemplate = ">=3.0.0,<4dev" - [[package]] name = "google-auth" version = "2.19.0" description = "Google Authentication Library" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -314,28 +206,10 @@ pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0dev)"] -[[package]] -name = "google-auth-httplib2" -version = "0.1.0" -description = "Google Authentication Library: httplib2 transport" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, - {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, -] - -[package.dependencies] -google-auth = "*" -httplib2 = ">=0.15.0" -six = "*" - [[package]] name = "google-cloud-aiplatform" version = "1.24.1" description = "Vertex AI API client library" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -344,7 +218,7 @@ files = [ ] [package.dependencies] -google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +google-api-core = {version = ">=1.32.0,<2.0.dev0 || >=2.8.dev0,<3.0.0dev", extras = ["grpc"]} google-cloud-bigquery = ">=1.15.0,<4.0.0dev" google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" google-cloud-storage = ">=1.32.0,<3.0.0dev" @@ -373,7 +247,6 @@ xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] name = "google-cloud-bigquery" version = "3.10.0" description = "Google BigQuery API client library" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -382,7 +255,7 @@ files = [ ] [package.dependencies] -google-api-core = {version = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} +google-api-core = {version = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} google-cloud-core = ">=1.6.0,<3.0.0dev" google-resumable-media = ">=0.6.0,<3.0dev" grpcio = ">=1.47.0,<2.0dev" @@ -406,7 +279,6 @@ tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] name = "google-cloud-core" version = "2.3.2" description = "Google Cloud API client core library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -415,59 +287,35 @@ files = [ ] [package.dependencies] -google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.25.0,<3.0dev" [package.extras] grpc = ["grpcio (>=1.38.0,<2.0dev)"] -[[package]] -name = "google-cloud-notebooks" -version = "1.7.0" -description = "Google Cloud Notebooks API client library" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-notebooks-1.7.0.tar.gz", hash = "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e"}, - {file = "google_cloud_notebooks-1.7.0-py2.py3-none-any.whl", hash = "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = ">=1.22.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - [[package]] name = "google-cloud-pipeline-components" -version = "1.0.42" +version = "2.0.0" description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." -category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7.0,<3.12.0" files = [ - {file = "google_cloud_pipeline_components-1.0.42-py3-none-any.whl", hash = "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623"}, + {file = "google_cloud_pipeline_components-2.0.0-py3-none-any.whl", hash = "sha256:4a3708dda8d68989f37d6b4e184e783ab3dc2ff8f0ddf69f5cd4a160603ce252"}, ] [package.dependencies] -google-api-core = ">=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" google-cloud-aiplatform = ">=1.14.0,<2" -google-cloud-notebooks = ">=0.4.0" -google-cloud-storage = ">=2.2.1,<3" -googleapis-common-protos = ">=1.56.2,<2.0dev" -grpcio-status = "<=1.47.0" -kfp = ">=1.8.9,<2.0.0" -protobuf = ">=3.19.0,<4.0.0dev" +kfp = ">=2.0.0b10,<3.0.0" [package.extras] -tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev)", "google-cloud-aiplatform (>=1.14.0,<2)", "google-cloud-notebooks (>=0.4.0)", "google-cloud-storage (>=2.2.1,<3)", "googleapis-common-protos (>=1.56.2,<2.0dev)", "grpcio-status (<=1.47.0)", "kfp (>=1.8.9,<2.0.0)", "mock (>=4.0.0)", "protobuf (>=3.19.0,<4.0.0dev)", "pytest (>=6.0.0)"] +docs = ["autodocsumm (==0.2.9)", "grpcio-status (<=1.47.0)", "m2r2 (==0.3.2)", "protobuf (>=3.19.0,<4.0.0dev)", "sphinx (==5.0.2)", "sphinx-immaterial (==0.9.0)", "sphinx-notfound-page (==0.8.3)", "sphinx-rtd-theme (==1.0.0)"] +tests = ["flake8 (>=3.0.0)", "mock (>=4.0.0)", "pytest (>=6.0.0)"] [[package]] name = "google-cloud-resource-manager" version = "1.10.1" description = "Google Cloud Resource Manager API client library" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -476,7 +324,7 @@ files = [ ] [package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" proto-plus = ">=1.22.0,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" @@ -485,7 +333,6 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4 name = "google-cloud-storage" version = "2.9.0" description = "Google Cloud Storage API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -494,7 +341,7 @@ files = [ ] [package.dependencies] -google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.25.0,<3.0dev" google-cloud-core = ">=2.3.0,<3.0dev" google-resumable-media = ">=2.3.2" @@ -507,7 +354,6 @@ protobuf = ["protobuf (<5.0.0dev)"] name = "google-crc32c" version = "1.5.0" description = "A python wrapper of the C library 'Google CRC32C'" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -588,7 +434,6 @@ testing = ["pytest"] name = "google-resumable-media" version = "2.5.0" description = "Utilities for Google Media Downloads and Resumable Uploads" -category = "main" optional = false python-versions = ">= 3.7" files = [ @@ -607,7 +452,6 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] name = "googleapis-common-protos" version = "1.59.0" description = "Common protobufs used in Google APIs" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -626,7 +470,6 @@ grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] name = "grpc-google-iam-v1" version = "0.12.6" description = "IAM API client library" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -643,7 +486,6 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 name = "grpcio" version = "1.54.2" description = "HTTP/2-based RPC framework" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -701,7 +543,6 @@ protobuf = ["grpcio-tools (>=1.54.2)"] name = "grpcio-status" version = "1.47.0" description = "Status proto mapping for gRPC" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -714,26 +555,10 @@ googleapis-common-protos = ">=1.5.5" grpcio = ">=1.47.0" protobuf = ">=3.12.0" -[[package]] -name = "httplib2" -version = "0.22.0" -description = "A comprehensive HTTP client library." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, - {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, -] - -[package.dependencies] -pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} - [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -745,7 +570,6 @@ files = [ name = "importlib-metadata" version = "6.6.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -762,30 +586,10 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker perf = ["ipython"] testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] -[[package]] -name = "importlib-resources" -version = "5.12.0" -description = "Read resources from Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, - {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -793,79 +597,44 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "jsonschema" -version = "4.17.3" -description = "An implementation of JSON Schema validation for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, - {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, -] - -[package.dependencies] -attrs = ">=17.4.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} -pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - [[package]] name = "kfp" -version = "1.8.21" -description = "KubeFlow Pipelines SDK" -category = "main" +version = "2.0.1" +description = "Kubeflow Pipelines SDK" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.7.0,<3.12.0" files = [ - {file = "kfp-1.8.21.tar.gz", hash = "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c"}, + {file = "kfp-2.0.1.tar.gz", hash = "sha256:4a65811778c3a45892d8d11c0f95ba5e410476157ed63cacb1dd82316f60cd4d"}, ] [package.dependencies] -absl-py = ">=0.9,<2" -click = ">=7.1.2,<9" -cloudpickle = ">=2.0.0,<3" -Deprecated = ">=1.2.7,<2" +click = ">=8.0.0,<9" docstring-parser = ">=0.7.3,<1" -fire = ">=0.3.1,<1" -google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" -google-api-python-client = ">=1.7.8,<2" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.6.1,<3" -google-cloud-storage = ">=1.20.0,<3" -jsonschema = ">=3.0.1,<5" -kfp-pipeline-spec = ">=0.1.16,<0.2.0" -kfp-server-api = ">=1.1.2,<2.0.0" -kubernetes = ">=8.0.0,<26" +google-cloud-storage = ">=2.2.1,<3" +kfp-pipeline-spec = "0.2.2" +kfp-server-api = ">=2.0.0,<2.1.0" +kubernetes = ">=8.0.0,<27" protobuf = ">=3.13.0,<4" -pydantic = ">=1.8.2,<2" -PyYAML = ">=5.3,<6" +PyYAML = ">=5.3,<7" requests-toolbelt = ">=0.8.0,<1" -strip-hints = ">=0.1.8,<1" tabulate = ">=0.8.6,<1" -typer = ">=0.3.2,<1.0" typing-extensions = {version = ">=3.7.4,<5", markers = "python_version < \"3.9\""} -uritemplate = ">=3.0.1,<4" -urllib3 = "<2" +urllib3 = "<2.0.0" [package.extras] -all = ["docker"] +all = ["docker", "kfp-kubernetes (<2)"] +kubernetes = ["kfp-kubernetes (<2)"] [[package]] name = "kfp-pipeline-spec" -version = "0.1.16" +version = "0.2.2" description = "Kubeflow Pipelines pipeline spec" -category = "main" optional = false python-versions = ">=3.7.0" files = [ - {file = "kfp_pipeline_spec-0.1.16-py3-none-any.whl", hash = "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25"}, + {file = "kfp_pipeline_spec-0.2.2-py3-none-any.whl", hash = "sha256:e83b58ffed3f7ca154d62ab20e14dc9a1a3c9dad589e856dd2b421e226f3b8d0"}, ] [package.dependencies] @@ -873,13 +642,12 @@ protobuf = ">=3.13.0,<4" [[package]] name = "kfp-server-api" -version = "1.8.5" +version = "2.0.0" description = "Kubeflow Pipelines API" -category = "main" optional = false python-versions = "*" files = [ - {file = "kfp-server-api-1.8.5.tar.gz", hash = "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de"}, + {file = "kfp-server-api-2.0.0.tar.gz", hash = "sha256:c0462045244a69211bc82a8d6af6602ae8b4f716ee0ac0be5dc34b7ea76c7bd1"}, ] [package.dependencies] @@ -892,7 +660,6 @@ urllib3 = ">=1.15" name = "kubernetes" version = "25.3.0" description = "Kubernetes python client" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -910,7 +677,7 @@ requests-oauthlib = "*" setuptools = ">=21.0.0" six = ">=1.9.0" urllib3 = ">=1.24.2" -websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" +websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.dev0 || >=0.43.dev0" [package.extras] adal = ["adal (>=1.0.2)"] @@ -919,7 +686,6 @@ adal = ["adal (>=1.0.2)"] name = "oauthlib" version = "3.2.2" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -936,7 +702,6 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -944,23 +709,10 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] -[[package]] -name = "pkgutil-resolve-name" -version = "1.3.10" -description = "Resolve a name to an object." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, - {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, -] - [[package]] name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -979,7 +731,6 @@ testing = ["pytest", "pytest-benchmark"] name = "proto-plus" version = "1.22.2" description = "Beautiful, Pythonic protocol buffers." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -997,7 +748,6 @@ testing = ["google-api-core[grpc] (>=1.31.5)"] name = "protobuf" version = "3.20.3" description = "Protocol Buffers" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1029,7 +779,6 @@ files = [ name = "pyasn1" version = "0.5.0" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -1041,7 +790,6 @@ files = [ name = "pyasn1-modules" version = "0.3.0" description = "A collection of ASN.1-based protocols modules" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -1052,116 +800,10 @@ files = [ [package.dependencies] pyasn1 = ">=0.4.6,<0.6.0" -[[package]] -name = "pydantic" -version = "1.10.8" -description = "Data validation and settings management using python type hints" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, - {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, - {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, - {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, - {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, - {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, - {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, - {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, - {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, - {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, - {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, - {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, - {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, - {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, - {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, - {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, - {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, - {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, - {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, - {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, - {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, - {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, - {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, - {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, - {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, - {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, - {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, - {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, - {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, - {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, - {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, - {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, - {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, - {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, - {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, - {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, -] - -[package.dependencies] -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" -optional = false -python-versions = ">=3.6.8" -files = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pyrsistent" -version = "0.19.3" -description = "Persistent/Functional/Immutable data structures" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, - {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, - {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, - {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, - {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, - {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, -] - [[package]] name = "pytest" version = "7.3.1" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1185,7 +827,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1200,7 +841,6 @@ six = ">=1.5" name = "pyyaml" version = "5.4.1" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -1239,7 +879,6 @@ files = [ name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1261,7 +900,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-oauthlib" version = "1.3.1" description = "OAuthlib authentication support for Requests." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1280,7 +918,6 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"] name = "requests-toolbelt" version = "0.10.1" description = "A utility belt for advanced users of python-requests" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1295,7 +932,6 @@ requests = ">=2.0.1,<3.0.0" name = "rsa" version = "4.9" description = "Pure-Python RSA implementation" -category = "main" optional = false python-versions = ">=3.6,<4" files = [ @@ -1310,7 +946,6 @@ pyasn1 = ">=0.1.3" name = "setuptools" version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1327,7 +962,6 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( name = "shapely" version = "1.8.5.post1" description = "Geometric objects, predicates, and operations" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1384,7 +1018,6 @@ vectorized = ["numpy"] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1392,25 +1025,10 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[[package]] -name = "strip-hints" -version = "0.1.10" -description = "Function and command-line program to strip Python type hints." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "strip-hints-0.1.10.tar.gz", hash = "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f"}, -] - -[package.dependencies] -wheel = "*" - [[package]] name = "tabulate" version = "0.9.0" description = "Pretty-print tabular data" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1421,26 +1039,10 @@ files = [ [package.extras] widechars = ["wcwidth"] -[[package]] -name = "termcolor" -version = "2.3.0" -description = "ANSI color formatting for output in terminal" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, - {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, -] - -[package.extras] -tests = ["pytest", "pytest-cov"] - [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1448,33 +1050,10 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "typer" -version = "0.9.0" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, - {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, -] - -[package.dependencies] -click = ">=7.1.1,<9.0.0" -typing-extensions = ">=3.7.4.3" - -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] - [[package]] name = "typing-extensions" version = "4.6.2" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1482,23 +1061,10 @@ files = [ {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, ] -[[package]] -name = "uritemplate" -version = "3.0.1" -description = "URI templates" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, - {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, -] - [[package]] name = "urllib3" version = "1.26.16" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -1515,7 +1081,6 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "websocket-client" version = "1.5.2" description = "WebSocket client for Python with low level API options" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1528,111 +1093,10 @@ docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] optional = ["python-socks", "wsaccel"] test = ["websockets"] -[[package]] -name = "wheel" -version = "0.40.0" -description = "A built-package format for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, - {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, -] - -[package.extras] -test = ["pytest (>=6.0.0)"] - -[[package]] -name = "wrapt" -version = "1.15.0" -description = "Module for decorators, wrappers and monkey patching." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, - {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, - {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, - {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, - {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, - {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, - {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, - {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, - {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, - {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, - {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, - {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, - {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, - {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, - {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, - {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, - {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, - {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, - {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, -] - [[package]] name = "zipp" version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1647,4 +1111,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = ">=3.7,<3.11" -content-hash = "9ab64756b47c22efb2437659a3a7b8ca9a5b5c019f86217a03bdfbfb620d83ee" +content-hash = "60358af42655b9cff7889a1d3d0fbcf4ca40982c4dd4d29784544c511a63cb96" diff --git a/components/vertex-components/pyproject.toml b/components/vertex-components/pyproject.toml index f608436f..9e628dc1 100644 --- a/components/vertex-components/pyproject.toml +++ b/components/vertex-components/pyproject.toml @@ -12,12 +12,12 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.7,<3.11" -kfp = "==1.8.21" +kfp = ">=2.0.1,<3.0.0" [tool.poetry.group.dev.dependencies] google-cloud-aiplatform = "1.24.1" -google-cloud-pipeline-components = "1.0.42" pytest = ">=7.3.1,<8.0.0" +google-cloud-pipeline-components = "^2.0.0" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/components/vertex-components/src/vertex_components/custom_train_job.py b/components/vertex-components/src/vertex_components/custom_train_job.py index 9cc51b17..8e2f220a 100644 --- a/components/vertex-components/src/vertex_components/custom_train_job.py +++ b/components/vertex-components/src/vertex_components/custom_train_job.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from kfp.v2.dsl import Input, component, Metrics, Output, Artifact, Dataset +from kfp.dsl import Input, component, Metrics, Output, Artifact, Dataset @component( diff --git a/components/vertex-components/src/vertex_components/import_model_evaluation.py b/components/vertex-components/src/vertex_components/import_model_evaluation.py index b7a2913e..acfec517 100644 --- a/components/vertex-components/src/vertex_components/import_model_evaluation.py +++ b/components/vertex-components/src/vertex_components/import_model_evaluation.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from kfp.v2.dsl import Input, Model, Metrics, component, Dataset +from kfp.dsl import Input, Model, Metrics, component, Dataset from typing import NamedTuple diff --git a/components/vertex-components/src/vertex_components/lookup_model.py b/components/vertex-components/src/vertex_components/lookup_model.py index c235b373..a4d6f3db 100644 --- a/components/vertex-components/src/vertex_components/lookup_model.py +++ b/components/vertex-components/src/vertex_components/lookup_model.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from kfp.v2.dsl import component, Output, Model +from kfp.dsl import component, Output, Model from typing import NamedTuple diff --git a/components/vertex-components/src/vertex_components/model_batch_predict.py b/components/vertex-components/src/vertex_components/model_batch_predict.py index 849ebd28..36916ec4 100644 --- a/components/vertex-components/src/vertex_components/model_batch_predict.py +++ b/components/vertex-components/src/vertex_components/model_batch_predict.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from kfp.v2.dsl import Input, Model, component +from kfp.dsl import Input, Model, component from typing import List, NamedTuple diff --git a/components/vertex-components/src/vertex_components/update_best_model.py b/components/vertex-components/src/vertex_components/update_best_model.py index eeb45461..9ec4ff9b 100644 --- a/components/vertex-components/src/vertex_components/update_best_model.py +++ b/components/vertex-components/src/vertex_components/update_best_model.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from kfp.v2.dsl import Input, Model, component +from kfp.dsl import Input, Model, component @component( diff --git a/components/vertex-components/tests/test_custom_training_job.py b/components/vertex-components/tests/test_custom_training_job.py index ac7675c4..3cd43b6e 100644 --- a/components/vertex-components/tests/test_custom_training_job.py +++ b/components/vertex-components/tests/test_custom_training_job.py @@ -1,5 +1,5 @@ import google.cloud.aiplatform as aip # noqa -from kfp.v2.dsl import Dataset, Metrics, Artifact +from kfp.dsl import Dataset, Metrics, Artifact from unittest import mock import pytest import json diff --git a/components/vertex-components/tests/test_import_model_evaluation.py b/components/vertex-components/tests/test_import_model_evaluation.py index 56cdf8bd..cfe8c5ab 100644 --- a/components/vertex-components/tests/test_import_model_evaluation.py +++ b/components/vertex-components/tests/test_import_model_evaluation.py @@ -1,5 +1,5 @@ from unittest import mock -from kfp.v2.dsl import Model, Metrics, Dataset +from kfp.dsl import Model, Metrics, Dataset import vertex_components from google.cloud.aiplatform_v1 import ModelEvaluation diff --git a/components/vertex-components/tests/test_lookup_model.py b/components/vertex-components/tests/test_lookup_model.py index 6950dd1b..a869f7cd 100644 --- a/components/vertex-components/tests/test_lookup_model.py +++ b/components/vertex-components/tests/test_lookup_model.py @@ -13,7 +13,7 @@ # limitations under the License. import google.cloud.aiplatform # noqa -from kfp.v2.dsl import Model +from kfp.dsl import Model from unittest import mock import pytest diff --git a/components/vertex-components/tests/test_model_batch_predict.py b/components/vertex-components/tests/test_model_batch_predict.py index 76bca67d..8131794d 100644 --- a/components/vertex-components/tests/test_model_batch_predict.py +++ b/components/vertex-components/tests/test_model_batch_predict.py @@ -14,7 +14,7 @@ import json import pytest from unittest import mock -from kfp.v2.dsl import Model +from kfp.dsl import Model from google.cloud.aiplatform_v1beta1.types.job_state import JobState diff --git a/components/vertex-components/tests/test_update_best_model.py b/components/vertex-components/tests/test_update_best_model.py index 4ef6ba21..481cd605 100644 --- a/components/vertex-components/tests/test_update_best_model.py +++ b/components/vertex-components/tests/test_update_best_model.py @@ -13,7 +13,7 @@ # limitations under the License. from unittest import mock -from kfp.v2.dsl import Model +from kfp.dsl import Model import vertex_components diff --git a/pipelines/poetry.lock b/pipelines/poetry.lock index e8388b8a..5fc4247c 100644 --- a/pipelines/poetry.lock +++ b/pipelines/poetry.lock @@ -1,51 +1,16 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. - -[[package]] -name = "absl-py" -version = "1.4.0" -description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "absl-py-1.4.0.tar.gz", hash = "sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d"}, - {file = "absl_py-1.4.0-py3-none-any.whl", hash = "sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47"}, -] - -[[package]] -name = "attrs" -version = "23.1.0" -description = "Classes Without Boilerplate" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. [[package]] name = "bigquery-components" version = "0.1.0" description = "KubeFlow components for BigQuery" -category = "main" optional = false python-versions = ">=3.7,<3.11" files = [] develop = true [package.dependencies] -kfp = "==1.8.21" +kfp = "^2.0.1" [package.source] type = "directory" @@ -55,7 +20,6 @@ url = "../components/bigquery-components" name = "cachetools" version = "5.3.1" description = "Extensible memoizing collections and decorators" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -67,7 +31,6 @@ files = [ name = "certifi" version = "2023.5.7" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -79,7 +42,6 @@ files = [ name = "cfgv" version = "3.3.1" description = "Validate configuration and produce human readable error messages." -category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -91,7 +53,6 @@ files = [ name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -176,7 +137,6 @@ files = [ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -188,23 +148,10 @@ files = [ colorama = {version = "*", markers = "platform_system == \"Windows\""} importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -[[package]] -name = "cloudpickle" -version = "2.2.1" -description = "Extended pickling support for Python objects" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "cloudpickle-2.2.1-py3-none-any.whl", hash = "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f"}, - {file = "cloudpickle-2.2.1.tar.gz", hash = "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5"}, -] - [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -216,7 +163,6 @@ files = [ name = "coverage" version = "7.2.5" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -276,29 +222,10 @@ files = [ [package.extras] toml = ["tomli"] -[[package]] -name = "deprecated" -version = "1.2.14" -description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, - {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, -] - -[package.dependencies] -wrapt = ">=1.10,<2" - -[package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] - [[package]] name = "distlib" version = "0.3.6" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" files = [ @@ -310,7 +237,6 @@ files = [ name = "docstring-parser" version = "0.15" description = "Parse Python docstrings in reST, Google and Numpydoc format" -category = "main" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -322,7 +248,6 @@ files = [ name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -337,7 +262,6 @@ test = ["pytest (>=6)"] name = "filelock" version = "3.12.0" description = "A platform independent file lock." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -349,26 +273,10 @@ files = [ docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] -[[package]] -name = "fire" -version = "0.5.0" -description = "A library for automatically generating command line interfaces." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "fire-0.5.0.tar.gz", hash = "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6"}, -] - -[package.dependencies] -six = "*" -termcolor = "*" - [[package]] name = "google-api-core" version = "2.11.0" description = "Google API client core library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -389,31 +297,10 @@ grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] -[[package]] -name = "google-api-python-client" -version = "1.12.11" -description = "Google API Client Library for Python" -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -files = [ - {file = "google-api-python-client-1.12.11.tar.gz", hash = "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9"}, - {file = "google_api_python_client-1.12.11-py2.py3-none-any.whl", hash = "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.21.0,<3dev", markers = "python_version >= \"3\""} -google-auth = {version = ">=1.16.0,<3dev", markers = "python_version >= \"3\""} -google-auth-httplib2 = ">=0.0.3" -httplib2 = ">=0.15.0,<1dev" -six = ">=1.13.0,<2dev" -uritemplate = ">=3.0.0,<4dev" - [[package]] name = "google-auth" version = "2.19.1" description = "Google Authentication Library" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -435,28 +322,10 @@ pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0dev)"] -[[package]] -name = "google-auth-httplib2" -version = "0.1.0" -description = "Google Authentication Library: httplib2 transport" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, - {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, -] - -[package.dependencies] -google-auth = "*" -httplib2 = ">=0.15.0" -six = "*" - [[package]] name = "google-cloud-aiplatform" version = "1.24.1" description = "Vertex AI API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -465,7 +334,7 @@ files = [ ] [package.dependencies] -google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +google-api-core = {version = ">=1.32.0,<2.0.dev0 || >=2.8.dev0,<3.0.0dev", extras = ["grpc"]} google-cloud-bigquery = ">=1.15.0,<4.0.0dev" google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" google-cloud-storage = ">=1.32.0,<3.0.0dev" @@ -494,7 +363,6 @@ xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] name = "google-cloud-bigquery" version = "3.11.0" description = "Google BigQuery API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -503,7 +371,7 @@ files = [ ] [package.dependencies] -google-api-core = {version = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} +google-api-core = {version = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} google-cloud-core = ">=1.6.0,<3.0.0dev" google-resumable-media = ">=0.6.0,<3.0dev" grpcio = ">=1.47.0,<2.0dev" @@ -527,7 +395,6 @@ tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] name = "google-cloud-core" version = "2.3.2" description = "Google Cloud API client core library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -536,59 +403,35 @@ files = [ ] [package.dependencies] -google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.25.0,<3.0dev" [package.extras] grpc = ["grpcio (>=1.38.0,<2.0dev)"] -[[package]] -name = "google-cloud-notebooks" -version = "1.7.0" -description = "Google Cloud Notebooks API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-notebooks-1.7.0.tar.gz", hash = "sha256:dac73a5cd983a4344d1fa96f9a8e5849b0ff75d7a5fdde921023a2ef4566e75e"}, - {file = "google_cloud_notebooks-1.7.0-py2.py3-none-any.whl", hash = "sha256:8fbffb7ba535fc02c61f135d8863324a5a2d20dd58cafaae592f0b0172d6bdab"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = ">=1.22.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" - [[package]] name = "google-cloud-pipeline-components" -version = "1.0.42" +version = "2.0.0" description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." -category = "main" optional = false -python-versions = "*" +python-versions = ">=3.7.0,<3.12.0" files = [ - {file = "google_cloud_pipeline_components-1.0.42-py3-none-any.whl", hash = "sha256:bf833f325d1b4a89f1db9627d3f3e75d1bffc7b7725d4eb739488a68808fa623"}, + {file = "google_cloud_pipeline_components-2.0.0-py3-none-any.whl", hash = "sha256:4a3708dda8d68989f37d6b4e184e783ab3dc2ff8f0ddf69f5cd4a160603ce252"}, ] [package.dependencies] -google-api-core = ">=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" google-cloud-aiplatform = ">=1.14.0,<2" -google-cloud-notebooks = ">=0.4.0" -google-cloud-storage = ">=2.2.1,<3" -googleapis-common-protos = ">=1.56.2,<2.0dev" -grpcio-status = "<=1.47.0" -kfp = ">=1.8.9,<2.0.0" -protobuf = ">=3.19.0,<4.0.0dev" +kfp = ">=2.0.0b10,<3.0.0" [package.extras] -tests = ["flake8 (>=3.0.0)", "google-api-core (>=1.31.5,<2.0.0 || >=2.8.0,<3.0.0dev)", "google-cloud-aiplatform (>=1.14.0,<2)", "google-cloud-notebooks (>=0.4.0)", "google-cloud-storage (>=2.2.1,<3)", "googleapis-common-protos (>=1.56.2,<2.0dev)", "grpcio-status (<=1.47.0)", "kfp (>=1.8.9,<2.0.0)", "mock (>=4.0.0)", "protobuf (>=3.19.0,<4.0.0dev)", "pytest (>=6.0.0)"] +docs = ["autodocsumm (==0.2.9)", "grpcio-status (<=1.47.0)", "m2r2 (==0.3.2)", "protobuf (>=3.19.0,<4.0.0dev)", "sphinx (==5.0.2)", "sphinx-immaterial (==0.9.0)", "sphinx-notfound-page (==0.8.3)", "sphinx-rtd-theme (==1.0.0)"] +tests = ["flake8 (>=3.0.0)", "mock (>=4.0.0)", "pytest (>=6.0.0)"] [[package]] name = "google-cloud-resource-manager" version = "1.10.1" description = "Google Cloud Resource Manager API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -597,7 +440,7 @@ files = [ ] [package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" proto-plus = ">=1.22.0,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" @@ -606,7 +449,6 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4 name = "google-cloud-storage" version = "2.9.0" description = "Google Cloud Storage API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -615,7 +457,7 @@ files = [ ] [package.dependencies] -google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.25.0,<3.0dev" google-cloud-core = ">=2.3.0,<3.0dev" google-resumable-media = ">=2.3.2" @@ -628,7 +470,6 @@ protobuf = ["protobuf (<5.0.0dev)"] name = "google-crc32c" version = "1.5.0" description = "A python wrapper of the C library 'Google CRC32C'" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -709,7 +550,6 @@ testing = ["pytest"] name = "google-resumable-media" version = "2.5.0" description = "Utilities for Google Media Downloads and Resumable Uploads" -category = "main" optional = false python-versions = ">= 3.7" files = [ @@ -728,7 +568,6 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] name = "googleapis-common-protos" version = "1.59.0" description = "Common protobufs used in Google APIs" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -747,7 +586,6 @@ grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] name = "grpc-google-iam-v1" version = "0.12.6" description = "IAM API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -764,7 +602,6 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 name = "grpcio" version = "1.54.2" description = "HTTP/2-based RPC framework" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -822,7 +659,6 @@ protobuf = ["grpcio-tools (>=1.54.2)"] name = "grpcio-status" version = "1.47.0" description = "Status proto mapping for gRPC" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -835,26 +671,10 @@ googleapis-common-protos = ">=1.5.5" grpcio = ">=1.47.0" protobuf = ">=3.12.0" -[[package]] -name = "httplib2" -version = "0.22.0" -description = "A comprehensive HTTP client library." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, - {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, -] - -[package.dependencies] -pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} - [[package]] name = "identify" version = "2.5.24" description = "File identification library for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -869,7 +689,6 @@ license = ["ukkonen"] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -881,7 +700,6 @@ files = [ name = "importlib-metadata" version = "6.6.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -898,30 +716,10 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker perf = ["ipython"] testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] -[[package]] -name = "importlib-resources" -version = "5.12.0" -description = "Read resources from Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, - {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -933,7 +731,6 @@ files = [ name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -947,79 +744,44 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] -[[package]] -name = "jsonschema" -version = "4.17.3" -description = "An implementation of JSON Schema validation for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, - {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, -] - -[package.dependencies] -attrs = ">=17.4.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} -pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - [[package]] name = "kfp" -version = "1.8.21" -description = "KubeFlow Pipelines SDK" -category = "main" +version = "2.0.1" +description = "Kubeflow Pipelines SDK" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.7.0,<3.12.0" files = [ - {file = "kfp-1.8.21.tar.gz", hash = "sha256:038d77ec9145ccfade95ab3b4b53c32668ae498fede06647ed0425d093819b1c"}, + {file = "kfp-2.0.1.tar.gz", hash = "sha256:4a65811778c3a45892d8d11c0f95ba5e410476157ed63cacb1dd82316f60cd4d"}, ] [package.dependencies] -absl-py = ">=0.9,<2" -click = ">=7.1.2,<9" -cloudpickle = ">=2.0.0,<3" -Deprecated = ">=1.2.7,<2" +click = ">=8.0.0,<9" docstring-parser = ">=0.7.3,<1" -fire = ">=0.3.1,<1" -google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" -google-api-python-client = ">=1.7.8,<2" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.6.1,<3" -google-cloud-storage = ">=1.20.0,<3" -jsonschema = ">=3.0.1,<5" -kfp-pipeline-spec = ">=0.1.16,<0.2.0" -kfp-server-api = ">=1.1.2,<2.0.0" -kubernetes = ">=8.0.0,<26" +google-cloud-storage = ">=2.2.1,<3" +kfp-pipeline-spec = "0.2.2" +kfp-server-api = ">=2.0.0,<2.1.0" +kubernetes = ">=8.0.0,<27" protobuf = ">=3.13.0,<4" -pydantic = ">=1.8.2,<2" -PyYAML = ">=5.3,<6" +PyYAML = ">=5.3,<7" requests-toolbelt = ">=0.8.0,<1" -strip-hints = ">=0.1.8,<1" tabulate = ">=0.8.6,<1" -typer = ">=0.3.2,<1.0" typing-extensions = {version = ">=3.7.4,<5", markers = "python_version < \"3.9\""} -uritemplate = ">=3.0.1,<4" -urllib3 = "<2" +urllib3 = "<2.0.0" [package.extras] -all = ["docker"] +all = ["docker", "kfp-kubernetes (<2)"] +kubernetes = ["kfp-kubernetes (<2)"] [[package]] name = "kfp-pipeline-spec" -version = "0.1.16" +version = "0.2.2" description = "Kubeflow Pipelines pipeline spec" -category = "main" optional = false python-versions = ">=3.7.0" files = [ - {file = "kfp_pipeline_spec-0.1.16-py3-none-any.whl", hash = "sha256:4cefae00ac50145cf862127202a8b8a783ed7504c773d7d7c517bd115283be25"}, + {file = "kfp_pipeline_spec-0.2.2-py3-none-any.whl", hash = "sha256:e83b58ffed3f7ca154d62ab20e14dc9a1a3c9dad589e856dd2b421e226f3b8d0"}, ] [package.dependencies] @@ -1027,13 +789,12 @@ protobuf = ">=3.13.0,<4" [[package]] name = "kfp-server-api" -version = "1.8.5" +version = "2.0.0" description = "Kubeflow Pipelines API" -category = "main" optional = false python-versions = "*" files = [ - {file = "kfp-server-api-1.8.5.tar.gz", hash = "sha256:482d71765ba57c003164dbb980a8cb1a18d234b578d064dc88dbeb3e4c7ab6de"}, + {file = "kfp-server-api-2.0.0.tar.gz", hash = "sha256:c0462045244a69211bc82a8d6af6602ae8b4f716ee0ac0be5dc34b7ea76c7bd1"}, ] [package.dependencies] @@ -1046,7 +807,6 @@ urllib3 = ">=1.15" name = "kubernetes" version = "25.3.0" description = "Kubernetes python client" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1064,7 +824,7 @@ requests-oauthlib = "*" setuptools = ">=21.0.0" six = ">=1.9.0" urllib3 = ">=1.24.2" -websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0" +websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.dev0 || >=0.43.dev0" [package.extras] adal = ["adal (>=1.0.2)"] @@ -1073,7 +833,6 @@ adal = ["adal (>=1.0.2)"] name = "markupsafe" version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1133,7 +892,6 @@ files = [ name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" -category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ @@ -1148,7 +906,6 @@ setuptools = "*" name = "oauthlib" version = "3.2.2" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1165,7 +922,6 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1173,23 +929,10 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] -[[package]] -name = "pkgutil-resolve-name" -version = "1.3.10" -description = "Resolve a name to an object." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, - {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, -] - [[package]] name = "platformdirs" version = "3.5.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1208,7 +951,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest- name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1227,7 +969,6 @@ testing = ["pytest", "pytest-benchmark"] name = "pre-commit" version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1247,7 +988,6 @@ virtualenv = ">=20.10.0" name = "proto-plus" version = "1.22.2" description = "Beautiful, Pythonic protocol buffers." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1265,7 +1005,6 @@ testing = ["google-api-core[grpc] (>=1.31.5)"] name = "protobuf" version = "3.20.3" description = "Protocol Buffers" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1297,7 +1036,6 @@ files = [ name = "pyasn1" version = "0.5.0" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -1309,7 +1047,6 @@ files = [ name = "pyasn1-modules" version = "0.3.0" description = "A collection of ASN.1-based protocols modules" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -1320,116 +1057,10 @@ files = [ [package.dependencies] pyasn1 = ">=0.4.6,<0.6.0" -[[package]] -name = "pydantic" -version = "1.10.8" -description = "Data validation and settings management using python type hints" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, - {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, - {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, - {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, - {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, - {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, - {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, - {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, - {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, - {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, - {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, - {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, - {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, - {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, - {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, - {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, - {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, - {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, - {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, - {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, - {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, - {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, - {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, - {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, - {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, - {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, - {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, - {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, - {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, - {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, - {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, - {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, - {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, - {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, - {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, - {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, -] - -[package.dependencies] -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" -optional = false -python-versions = ">=3.6.8" -files = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pyrsistent" -version = "0.19.3" -description = "Persistent/Functional/Immutable data structures" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, - {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, - {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, - {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, - {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, - {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, -] - [[package]] name = "pytest" version = "7.3.1" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1453,7 +1084,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1468,7 +1098,6 @@ six = ">=1.5" name = "pyyaml" version = "5.4.1" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -1507,7 +1136,6 @@ files = [ name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1529,7 +1157,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-oauthlib" version = "1.3.1" description = "OAuthlib authentication support for Requests." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1548,7 +1175,6 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"] name = "requests-toolbelt" version = "0.10.1" description = "A utility belt for advanced users of python-requests" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1563,7 +1189,6 @@ requests = ">=2.0.1,<3.0.0" name = "rsa" version = "4.9" description = "Pure-Python RSA implementation" -category = "main" optional = false python-versions = ">=3.6,<4" files = [ @@ -1578,7 +1203,6 @@ pyasn1 = ">=0.1.3" name = "setuptools" version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1595,7 +1219,6 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( name = "shapely" version = "1.8.5.post1" description = "Geometric objects, predicates, and operations" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1652,7 +1275,6 @@ vectorized = ["numpy"] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1660,25 +1282,10 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[[package]] -name = "strip-hints" -version = "0.1.10" -description = "Function and command-line program to strip Python type hints." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "strip-hints-0.1.10.tar.gz", hash = "sha256:307c2bd147cd35997c8ed2e9a3bdca48ad9c9617e04ea46599095201b4ce998f"}, -] - -[package.dependencies] -wheel = "*" - [[package]] name = "tabulate" version = "0.9.0" description = "Pretty-print tabular data" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1689,26 +1296,10 @@ files = [ [package.extras] widechars = ["wcwidth"] -[[package]] -name = "termcolor" -version = "2.3.0" -description = "ANSI color formatting for output in terminal" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, - {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, -] - -[package.extras] -tests = ["pytest", "pytest-cov"] - [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1716,33 +1307,10 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "typer" -version = "0.9.0" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, - {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, -] - -[package.dependencies] -click = ">=7.1.1,<9.0.0" -typing-extensions = ">=3.7.4.3" - -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] - [[package]] name = "typing-extensions" version = "4.6.3" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1750,23 +1318,10 @@ files = [ {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, ] -[[package]] -name = "uritemplate" -version = "3.0.1" -description = "URI templates" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, - {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, -] - [[package]] name = "urllib3" version = "1.26.16" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -1783,14 +1338,13 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "vertex-components" version = "0.1.0" description = "KubeFlow components for Vertex AI" -category = "main" optional = false python-versions = ">=3.7,<3.11" files = [] develop = true [package.dependencies] -kfp = "==1.8.21" +kfp = ">=2.0.1,<3.0.0" [package.source] type = "directory" @@ -1800,7 +1354,6 @@ url = "../components/vertex-components" name = "virtualenv" version = "20.23.0" description = "Virtual Python Environment builder" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1822,7 +1375,6 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess name = "websocket-client" version = "1.5.2" description = "WebSocket client for Python with low level API options" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1835,111 +1387,10 @@ docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] optional = ["python-socks", "wsaccel"] test = ["websockets"] -[[package]] -name = "wheel" -version = "0.40.0" -description = "A built-package format for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "wheel-0.40.0-py3-none-any.whl", hash = "sha256:d236b20e7cb522daf2390fa84c55eea81c5c30190f90f29ae2ca1ad8355bf247"}, - {file = "wheel-0.40.0.tar.gz", hash = "sha256:cd1196f3faee2b31968d626e1731c94f99cbdb67cf5a46e4f5656cbee7738873"}, -] - -[package.extras] -test = ["pytest (>=6.0.0)"] - -[[package]] -name = "wrapt" -version = "1.15.0" -description = "Module for decorators, wrappers and monkey patching." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, - {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, - {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, - {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, - {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, - {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, - {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, - {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, - {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, - {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, - {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, - {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, - {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, - {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, - {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, - {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, - {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, - {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, - {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, -] - [[package]] name = "zipp" version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1954,4 +1405,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = ">=3.7,<3.11" -content-hash = "60130a208e8b1b38937ed8c91f1aa32e75c3c86be061ca321457ebc6f3a7d72c" +content-hash = "6360ac432e14023430d2290fcbd782c7693b0365570fd50f4ecc865d04a5718d" diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index 9548d91c..eb468200 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -17,9 +17,10 @@ packages = [ python = ">=3.7,<3.11" Jinja2 = ">=3.0.1,<4.0.0" google-cloud-aiplatform = "1.24.1" -google-cloud-pipeline-components = "1.0.42" +google-cloud-pipeline-components = "^2.0.0" bigquery-components = { path = "../components/bigquery-components", develop = true } vertex-components = { path = "../components/vertex-components", develop = true } +kfp = "^2.0.1" [tool.poetry.group.dev.dependencies] pytest = ">=7.3.1,<8.0.0" diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index ff25c119..20deca08 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -16,7 +16,7 @@ import pathlib from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp -from kfp.v2 import compiler, dsl +from kfp import compiler, dsl from pipelines import generate_query from vertex_components import lookup_model, model_batch_predict diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 371928f5..13ccb817 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -16,7 +16,7 @@ import pathlib from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp -from kfp.v2 import compiler, dsl +from kfp import compiler, dsl from pipelines import generate_query from bigquery_components import extract_bq_to_dataset from vertex_components import ( diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index f077d32c..c7225e10 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -16,7 +16,7 @@ import pathlib from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp -from kfp.v2 import compiler, dsl +from kfp import compiler, dsl from pipelines import generate_query from vertex_components import lookup_model, model_batch_predict diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 7db36231..411bc69e 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -16,7 +16,7 @@ import pathlib from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp -from kfp.v2 import compiler, dsl +from kfp import compiler, dsl from pipelines import generate_query from bigquery_components import extract_bq_to_dataset from vertex_components import ( diff --git a/pipelines/tests/e2e/test_e2e.py b/pipelines/tests/e2e/test_e2e.py index d4c70393..c7b8081c 100644 --- a/pipelines/tests/e2e/test_e2e.py +++ b/pipelines/tests/e2e/test_e2e.py @@ -18,7 +18,7 @@ import pytest import os from google.cloud import storage -from kfp.v2 import compiler +from kfp import compiler from pipelines.trigger.main import trigger_pipeline_from_payload From bfa29041301cb72a5d31edb44fad5a22d7f0c711 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Wed, 19 Jul 2023 22:34:34 +0100 Subject: [PATCH 125/238] feat: upgrade to kfp v2 and Python 3.9 --- .pre-commit-config.yaml | 2 +- .python-version | 2 +- Makefile | 6 + cloudbuild/e2e-test.yaml | 2 +- cloudbuild/pr-checks.yaml | 2 +- cloudbuild/release.yaml | 2 +- cloudbuild/trigger-tests.yaml | 2 +- .../bigquery-components/.python-version | 2 +- components/bigquery-components/poetry.lock | 62 ++-- components/bigquery-components/pyproject.toml | 4 +- .../extract_bq_to_dataset.py | 2 +- components/vertex-components/.python-version | 2 +- components/vertex-components/poetry.lock | 72 ++--- components/vertex-components/pyproject.toml | 4 +- .../src/vertex_components/__init__.py | 2 + .../src/vertex_components/custom_train_job.py | 160 ---------- .../import_model_evaluation.py | 86 ------ .../src/vertex_components/lookup_model.py | 99 ------ .../vertex_components/model_batch_predict.py | 2 +- .../vertex_components/update_best_model.py | 87 ------ .../src/vertex_components/upload_model.py | 216 +++++++++++++ env.sh.example | 1 + pipelines/.python-version | 2 +- pipelines/poetry.lock | 71 +---- pipelines/pyproject.toml | 6 +- .../pipelines/xgboost/training/pipeline.py | 95 +++--- terraform/modules/vertex_deployment/main.tf | 10 + training/.gitignore | 1 + training/Dockerfile | 8 + training/main.py | 147 +++++++++ training/poetry.lock | 283 ++++++++++++++++++ training/pyproject.toml | 17 ++ 32 files changed, 833 insertions(+), 626 deletions(-) delete mode 100644 components/vertex-components/src/vertex_components/custom_train_job.py delete mode 100644 components/vertex-components/src/vertex_components/import_model_evaluation.py delete mode 100644 components/vertex-components/src/vertex_components/lookup_model.py delete mode 100644 components/vertex-components/src/vertex_components/update_best_model.py create mode 100644 components/vertex-components/src/vertex_components/upload_model.py create mode 100644 training/.gitignore create mode 100644 training/Dockerfile create mode 100644 training/main.py create mode 100644 training/poetry.lock create mode 100644 training/pyproject.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3bd0bc1e..66dc32d3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ --- default_language_version: - python: python3.7 + python: python3.9 repos: - repo: https://github.com/gruntwork-io/pre-commit diff --git a/.python-version b/.python-version index 8a433dd7..7a72691a 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.7.12 +3.9.16 diff --git a/Makefile b/Makefile index 2ce9ca3b..d441b7f7 100644 --- a/Makefile +++ b/Makefile @@ -105,3 +105,9 @@ destroy-infra: ## DESTROY the Terraform infrastructure in your project. Requires @ cd terraform/envs/$(env) && \ terraform init -backend-config='bucket=${VERTEX_PROJECT_ID}-tfstate' && \ terraform destroy -var 'project_id=${VERTEX_PROJECT_ID}' -var 'region=${VERTEX_LOCATION}' + +build-training-container: ## Build and push training container image using Docker + @ cd training && \ + poetry export -f requirements.txt -o requirements.txt && \ + docker build -t ${TRAINING_CONTAINER_IMAGE} . && \ + docker push ${TRAINING_CONTAINER_IMAGE} diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index b71430d2..b3d054da 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -28,7 +28,7 @@ steps: # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines - - name: python:3.7 + - name: python:3.9 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/pr-checks.yaml b/cloudbuild/pr-checks.yaml index 9f0f4347..969e7bbf 100644 --- a/cloudbuild/pr-checks.yaml +++ b/cloudbuild/pr-checks.yaml @@ -17,7 +17,7 @@ steps: # Install poetry and deps, run pre-commit and unit tests # Then compile pipelines (to make sure they can compile) # need to run "git init" for pre-commit checks to work - - name: python:3.7 + - name: python:3.9 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index d5903468..0c22abdb 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -15,7 +15,7 @@ steps: # Install poetry, install deps, compile pipelines - - name: python:3.7 + - name: python:3.9 entrypoint: /bin/sh args: - -c diff --git a/cloudbuild/trigger-tests.yaml b/cloudbuild/trigger-tests.yaml index a3387438..295e2c50 100644 --- a/cloudbuild/trigger-tests.yaml +++ b/cloudbuild/trigger-tests.yaml @@ -13,7 +13,7 @@ # limitations under the License. --- steps: - - name: 'python:3.7' + - name: 'python:3.9' args: - '-c' - | diff --git a/components/bigquery-components/.python-version b/components/bigquery-components/.python-version index f7e5aa84..9f3d4c17 100644 --- a/components/bigquery-components/.python-version +++ b/components/bigquery-components/.python-version @@ -1 +1 @@ -3.7.12 +3.9.16 diff --git a/components/bigquery-components/poetry.lock b/components/bigquery-components/poetry.lock index 7fa1ecb4..d2a3ec40 100644 --- a/components/bigquery-components/poetry.lock +++ b/components/bigquery-components/poetry.lock @@ -5,7 +5,7 @@ name = "cachetools" version = "5.3.1" description = "Extensible memoizing collections and decorators" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, @@ -27,7 +27,7 @@ name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.9.0" files = [ {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, @@ -111,7 +111,7 @@ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, @@ -148,7 +148,7 @@ name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, @@ -162,7 +162,7 @@ name = "google-api-core" version = "2.11.0" description = "Google API client core library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-api-core-2.11.0.tar.gz", hash = "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22"}, {file = "google_api_core-2.11.0-py3-none-any.whl", hash = "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e"}, @@ -242,7 +242,7 @@ name = "google-cloud-core" version = "2.3.2" description = "Google Cloud API client core library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-cloud-core-2.3.2.tar.gz", hash = "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a"}, {file = "google_cloud_core-2.3.2-py2.py3-none-any.whl", hash = "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe"}, @@ -260,7 +260,7 @@ name = "google-cloud-storage" version = "2.9.0" description = "Google Cloud Storage API client library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-cloud-storage-2.9.0.tar.gz", hash = "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3"}, {file = "google_cloud_storage-2.9.0-py2.py3-none-any.whl", hash = "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94"}, @@ -281,7 +281,7 @@ name = "google-crc32c" version = "1.5.0" description = "A python wrapper of the C library 'Google CRC32C'" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, @@ -361,7 +361,7 @@ name = "google-resumable-media" version = "2.5.0" description = "Utilities for Google Media Downloads and Resumable Uploads" optional = false -python-versions = ">= 3.7" +python-versions = ">= 3.9" files = [ {file = "google-resumable-media-2.5.0.tar.gz", hash = "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93"}, {file = "google_resumable_media-2.5.0-py2.py3-none-any.whl", hash = "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec"}, @@ -379,7 +379,7 @@ name = "googleapis-common-protos" version = "1.59.0" description = "Common protobufs used in Google APIs" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "googleapis-common-protos-1.59.0.tar.gz", hash = "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44"}, {file = "googleapis_common_protos-1.59.0-py2.py3-none-any.whl", hash = "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f"}, @@ -396,7 +396,7 @@ name = "grpcio" version = "1.54.2" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "grpcio-1.54.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c"}, {file = "grpcio-1.54.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea"}, @@ -480,7 +480,7 @@ name = "importlib-metadata" version = "6.6.0" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, @@ -493,14 +493,14 @@ zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.9)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -511,7 +511,7 @@ name = "kfp" version = "2.0.1" description = "Kubeflow Pipelines SDK" optional = false -python-versions = ">=3.7.0,<3.12.0" +python-versions = ">=3.9.0,<3.12.0" files = [ {file = "kfp-2.0.1.tar.gz", hash = "sha256:4a65811778c3a45892d8d11c0f95ba5e410476157ed63cacb1dd82316f60cd4d"}, ] @@ -529,7 +529,7 @@ protobuf = ">=3.13.0,<4" PyYAML = ">=5.3,<7" requests-toolbelt = ">=0.8.0,<1" tabulate = ">=0.8.6,<1" -typing-extensions = {version = ">=3.7.4,<5", markers = "python_version < \"3.9\""} +typing-extensions = {version = ">=3.9.4,<5", markers = "python_version < \"3.9\""} urllib3 = "<2.0.0" [package.extras] @@ -541,7 +541,7 @@ name = "kfp-pipeline-spec" version = "0.2.2" description = "Kubeflow Pipelines pipeline spec" optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.9.0" files = [ {file = "kfp_pipeline_spec-0.2.2-py3-none-any.whl", hash = "sha256:e83b58ffed3f7ca154d62ab20e14dc9a1a3c9dad589e856dd2b421e226f3b8d0"}, ] @@ -612,7 +612,7 @@ name = "packaging" version = "23.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, @@ -658,7 +658,7 @@ name = "protobuf" version = "3.20.3" description = "Protocol Buffers" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, @@ -714,7 +714,7 @@ name = "pytest" version = "7.3.1" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, @@ -789,7 +789,7 @@ name = "requests" version = "2.31.0" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, @@ -856,7 +856,7 @@ name = "setuptools" version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, @@ -864,7 +864,7 @@ files = [ [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.9)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -883,7 +883,7 @@ name = "tabulate" version = "0.9.0" description = "Pretty-print tabular data" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, @@ -897,7 +897,7 @@ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -906,9 +906,9 @@ files = [ [[package]] name = "typing-extensions" version = "4.6.2" -description = "Backported and Experimental Type Hints for Python 3.7+" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, @@ -935,7 +935,7 @@ name = "websocket-client" version = "1.5.2" description = "WebSocket client for Python with low level API options" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "websocket-client-1.5.2.tar.gz", hash = "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b"}, {file = "websocket_client-1.5.2-py3-none-any.whl", hash = "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1"}, @@ -951,7 +951,7 @@ name = "zipp" version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, @@ -959,9 +959,9 @@ files = [ [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.9)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "2.0" -python-versions = ">=3.7,<3.11" +python-versions = ">=3.9,<3.11" content-hash = "6f7ca2171a974f20e9e0c7d38859b0570337711f678f0fed05a9560ce299f0e0" diff --git a/components/bigquery-components/pyproject.toml b/components/bigquery-components/pyproject.toml index 9d6cd0db..36d55a21 100644 --- a/components/bigquery-components/pyproject.toml +++ b/components/bigquery-components/pyproject.toml @@ -7,11 +7,11 @@ readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.9", ] [tool.poetry.dependencies] -python = ">=3.7,<3.11" +python = ">=3.9,<3.11" kfp = "^2.0.1" [tool.poetry.group.dev.dependencies] diff --git a/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py b/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py index 4846e448..7039b022 100644 --- a/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py +++ b/components/bigquery-components/src/bigquery_components/extract_bq_to_dataset.py @@ -16,7 +16,7 @@ @component( - base_image="python:3.7", + base_image="python:3.9", packages_to_install=["google-cloud-bigquery==2.30.0"], ) def extract_bq_to_dataset( diff --git a/components/vertex-components/.python-version b/components/vertex-components/.python-version index f7e5aa84..9f3d4c17 100644 --- a/components/vertex-components/.python-version +++ b/components/vertex-components/.python-version @@ -1 +1 @@ -3.7.12 +3.9.16 diff --git a/components/vertex-components/poetry.lock b/components/vertex-components/poetry.lock index c03e62bb..c5e25a3e 100644 --- a/components/vertex-components/poetry.lock +++ b/components/vertex-components/poetry.lock @@ -5,7 +5,7 @@ name = "cachetools" version = "5.3.1" description = "Extensible memoizing collections and decorators" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, @@ -27,7 +27,7 @@ name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.9.0" files = [ {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, @@ -111,7 +111,7 @@ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, @@ -148,7 +148,7 @@ name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, @@ -162,7 +162,7 @@ name = "google-api-core" version = "2.11.0" description = "Google API client core library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-api-core-2.11.0.tar.gz", hash = "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22"}, {file = "google_api_core-2.11.0-py3-none-any.whl", hash = "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e"}, @@ -211,7 +211,7 @@ name = "google-cloud-aiplatform" version = "1.24.1" description = "Vertex AI API client library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-cloud-aiplatform-1.24.1.tar.gz", hash = "sha256:0ce9e97bf5c977397e52b3b7c4dc78610c135fbde11a60a6c0b77a4fdf776400"}, {file = "google_cloud_aiplatform-1.24.1-py2.py3-none-any.whl", hash = "sha256:942765a6bad97e46e262dd6599dc5f171663ce952130e0b0b2eb97e0b1b98bfd"}, @@ -248,7 +248,7 @@ name = "google-cloud-bigquery" version = "3.10.0" description = "Google BigQuery API client library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-cloud-bigquery-3.10.0.tar.gz", hash = "sha256:4b02def076e2db8cec66f65fb627d13904a9fc3cf4fee315ede43dcb7038a8df"}, {file = "google_cloud_bigquery-3.10.0-py2.py3-none-any.whl", hash = "sha256:848a3cbce0ba7d4f1e9551400a7c99aa0eab72290d5a1bbbe69f18a24a10bd3a"}, @@ -280,7 +280,7 @@ name = "google-cloud-core" version = "2.3.2" description = "Google Cloud API client core library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-cloud-core-2.3.2.tar.gz", hash = "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a"}, {file = "google_cloud_core-2.3.2-py2.py3-none-any.whl", hash = "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe"}, @@ -298,7 +298,7 @@ name = "google-cloud-pipeline-components" version = "2.0.0" description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." optional = false -python-versions = ">=3.7.0,<3.12.0" +python-versions = ">=3.9.0,<3.12.0" files = [ {file = "google_cloud_pipeline_components-2.0.0-py3-none-any.whl", hash = "sha256:4a3708dda8d68989f37d6b4e184e783ab3dc2ff8f0ddf69f5cd4a160603ce252"}, ] @@ -317,7 +317,7 @@ name = "google-cloud-resource-manager" version = "1.10.1" description = "Google Cloud Resource Manager API client library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-cloud-resource-manager-1.10.1.tar.gz", hash = "sha256:c974fb6f9810476cf7b63ea89394c1a8df47f7f2dc2303e728bb74b500bcde67"}, {file = "google_cloud_resource_manager-1.10.1-py2.py3-none-any.whl", hash = "sha256:41a2204532f084c707fde0bc1a9bc95c7e0b739d7072dd0b8a25106667a56184"}, @@ -334,7 +334,7 @@ name = "google-cloud-storage" version = "2.9.0" description = "Google Cloud Storage API client library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-cloud-storage-2.9.0.tar.gz", hash = "sha256:9b6ae7b509fc294bdacb84d0f3ea8e20e2c54a8b4bbe39c5707635fec214eff3"}, {file = "google_cloud_storage-2.9.0-py2.py3-none-any.whl", hash = "sha256:83a90447f23d5edd045e0037982c270302e3aeb45fc1288d2c2ca713d27bad94"}, @@ -355,7 +355,7 @@ name = "google-crc32c" version = "1.5.0" description = "A python wrapper of the C library 'Google CRC32C'" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, @@ -435,7 +435,7 @@ name = "google-resumable-media" version = "2.5.0" description = "Utilities for Google Media Downloads and Resumable Uploads" optional = false -python-versions = ">= 3.7" +python-versions = ">= 3.9" files = [ {file = "google-resumable-media-2.5.0.tar.gz", hash = "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93"}, {file = "google_resumable_media-2.5.0-py2.py3-none-any.whl", hash = "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec"}, @@ -453,7 +453,7 @@ name = "googleapis-common-protos" version = "1.59.0" description = "Common protobufs used in Google APIs" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "googleapis-common-protos-1.59.0.tar.gz", hash = "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44"}, {file = "googleapis_common_protos-1.59.0-py2.py3-none-any.whl", hash = "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f"}, @@ -471,7 +471,7 @@ name = "grpc-google-iam-v1" version = "0.12.6" description = "IAM API client library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "grpc-google-iam-v1-0.12.6.tar.gz", hash = "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f"}, {file = "grpc_google_iam_v1-0.12.6-py2.py3-none-any.whl", hash = "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c"}, @@ -487,7 +487,7 @@ name = "grpcio" version = "1.54.2" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "grpcio-1.54.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c"}, {file = "grpcio-1.54.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea"}, @@ -571,7 +571,7 @@ name = "importlib-metadata" version = "6.6.0" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, @@ -584,14 +584,14 @@ zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.9)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -602,7 +602,7 @@ name = "kfp" version = "2.0.1" description = "Kubeflow Pipelines SDK" optional = false -python-versions = ">=3.7.0,<3.12.0" +python-versions = ">=3.9.0,<3.12.0" files = [ {file = "kfp-2.0.1.tar.gz", hash = "sha256:4a65811778c3a45892d8d11c0f95ba5e410476157ed63cacb1dd82316f60cd4d"}, ] @@ -620,7 +620,7 @@ protobuf = ">=3.13.0,<4" PyYAML = ">=5.3,<7" requests-toolbelt = ">=0.8.0,<1" tabulate = ">=0.8.6,<1" -typing-extensions = {version = ">=3.7.4,<5", markers = "python_version < \"3.9\""} +typing-extensions = {version = ">=3.9.4,<5", markers = "python_version < \"3.9\""} urllib3 = "<2.0.0" [package.extras] @@ -632,7 +632,7 @@ name = "kfp-pipeline-spec" version = "0.2.2" description = "Kubeflow Pipelines pipeline spec" optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.9.0" files = [ {file = "kfp_pipeline_spec-0.2.2-py3-none-any.whl", hash = "sha256:e83b58ffed3f7ca154d62ab20e14dc9a1a3c9dad589e856dd2b421e226f3b8d0"}, ] @@ -703,7 +703,7 @@ name = "packaging" version = "23.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, @@ -749,7 +749,7 @@ name = "protobuf" version = "3.20.3" description = "Protocol Buffers" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, @@ -805,7 +805,7 @@ name = "pytest" version = "7.3.1" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, @@ -880,7 +880,7 @@ name = "requests" version = "2.31.0" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, @@ -947,7 +947,7 @@ name = "setuptools" version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, @@ -955,7 +955,7 @@ files = [ [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.9)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -1030,7 +1030,7 @@ name = "tabulate" version = "0.9.0" description = "Pretty-print tabular data" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, @@ -1044,7 +1044,7 @@ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -1053,9 +1053,9 @@ files = [ [[package]] name = "typing-extensions" version = "4.6.2" -description = "Backported and Experimental Type Hints for Python 3.7+" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, @@ -1082,7 +1082,7 @@ name = "websocket-client" version = "1.5.2" description = "WebSocket client for Python with low level API options" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "websocket-client-1.5.2.tar.gz", hash = "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b"}, {file = "websocket_client-1.5.2-py3-none-any.whl", hash = "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1"}, @@ -1098,7 +1098,7 @@ name = "zipp" version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, @@ -1106,9 +1106,9 @@ files = [ [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.9)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "2.0" -python-versions = ">=3.7,<3.11" +python-versions = ">=3.9,<3.11" content-hash = "60358af42655b9cff7889a1d3d0fbcf4ca40982c4dd4d29784544c511a63cb96" diff --git a/components/vertex-components/pyproject.toml b/components/vertex-components/pyproject.toml index 9e628dc1..a12e1fdc 100644 --- a/components/vertex-components/pyproject.toml +++ b/components/vertex-components/pyproject.toml @@ -7,11 +7,11 @@ readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.9", ] [tool.poetry.dependencies] -python = ">=3.7,<3.11" +python = ">=3.9,<3.11" kfp = ">=2.0.1,<3.0.0" [tool.poetry.group.dev.dependencies] diff --git a/components/vertex-components/src/vertex_components/__init__.py b/components/vertex-components/src/vertex_components/__init__.py index 22ca7d04..4175abff 100644 --- a/components/vertex-components/src/vertex_components/__init__.py +++ b/components/vertex-components/src/vertex_components/__init__.py @@ -3,6 +3,7 @@ from .lookup_model import lookup_model from .model_batch_predict import model_batch_predict from .update_best_model import update_best_model +from .upload_model import upload_model __version__ = "0.0.1" @@ -12,4 +13,5 @@ "lookup_model", "model_batch_predict", "update_best_model", + "upload_model", ] diff --git a/components/vertex-components/src/vertex_components/custom_train_job.py b/components/vertex-components/src/vertex_components/custom_train_job.py deleted file mode 100644 index 8e2f220a..00000000 --- a/components/vertex-components/src/vertex_components/custom_train_job.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright 2022 Google LLC -from typing import List, Dict, NamedTuple - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# https://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from kfp.dsl import Input, component, Metrics, Output, Artifact, Dataset - - -@component( - base_image="python:3.7", - packages_to_install=[ - "google-cloud-aiplatform==1.24.1", - "google-cloud-pipeline-components==1.0.42", - ], -) -def custom_train_job( - train_script_uri: str, - train_data: Input[Dataset], - valid_data: Input[Dataset], - test_data: Input[Dataset], - project_id: str, - project_location: str, - model_display_name: str, - train_container_uri: str, - serving_container_uri: str, - model: Output[Artifact], - metrics: Output[Metrics], - staging_bucket: str, - requirements: List[str] = None, - job_name: str = None, - hparams: Dict[str, str] = None, - replica_count: int = 1, - machine_type: str = "n1-standard-4", - accelerator_type: str = "ACCELERATOR_TYPE_UNSPECIFIED", - accelerator_count: int = 0, - parent_model: str = None, -) -> NamedTuple("Outputs", [("gcp_resources", str)]): - """Run a custom training job using a training script. - - The provided script will be invoked by passing the following command-line arguments: - - ``` - train.py \ - --train_data \ - --valid_data \ - --test_data \ - --metrics \ - --hparams json.dumps() - ``` - - Ensure that your train script can read these arguments and outputs metrics - to the provided path and the model to the correct path based on: - https://cloud.google.com/vertex-ai/docs/training/code-requirements. - - Args: - train_script_uri (str): gs:// uri to python train script. See: - https://cloud.google.com/vertex-ai/docs/training/code-requirements. - train_data (Dataset): Training data (passed as an argument to train script) - valid_data (Dataset): Validation data (passed as an argument to train script) - test_data (Dataset): Test data (passed as an argument to train script). - staging_bucket (str): Staging bucket for CustomTrainingJob. - project_location (str): location of the Google Cloud project. - project_id (str): project id of the Google Cloud project. - model_display_name (str): Name of the new trained model version. - train_container_uri (str): Container URI for running train script. - serving_container_uri (str): Container URI for deploying the output model. - model (Model): Trained model output. - metrics (Metrics): Output metrics of trained model. - requirements (List[str]): Additional python dependencies for training script. - job_name (str): Name of training job. - hparams (Dict[str, str]): Hyperparameters (passed as a JSON serialised argument - to train script) - replica_count (int): Number of replicas (increase for distributed training). - machine_type (str): Machine type of compute. - accelerator_type (str): Accelerator type (change for GPU support). - accelerator_count (str): Accelerator count (increase for GPU cores). - parent_model (str): Resource URI of existing parent model (optional). If `None`, - a new model will be uploaded. Otherwise, a new model version for the parent - model will be uploaded. - Returns: - parent_model (str): Resource URI of the parent model (empty string if the - trained model is the first model version of its kind). - NamedTuple: gcp_resources for Vertex AI UI integration. - """ - import json - import logging - import os.path - import time - import google.cloud.aiplatform as aip - from google_cloud_pipeline_components.proto.gcp_resources_pb2 import GcpResources - from google.protobuf.json_format import MessageToJson - - logging.info(f"Using train script: {train_script_uri}") - script_path = "/gcs/" + train_script_uri[5:] - if not os.path.exists(script_path): - raise ValueError( - "Train script was not found. " - f"Check if the path is correct: {train_script_uri}" - ) - - job = aip.CustomTrainingJob( - project=project_id, - location=project_location, - staging_bucket=staging_bucket, - display_name=job_name if job_name else f"Custom job {int(time.time())}", - script_path=script_path, - container_uri=train_container_uri, - requirements=requirements, - model_serving_container_image_uri=serving_container_uri, - ) - cmd_args = [ - f"--train_data={train_data.path}", - f"--valid_data={valid_data.path}", - f"--test_data={test_data.path}", - f"--metrics={metrics.path}", - f"--hparams={json.dumps(hparams if hparams else {})}", - ] - uploaded_model = job.run( - model_display_name=model_display_name, - parent_model=parent_model, - is_default_version=(not parent_model), - args=cmd_args, - replica_count=replica_count, - machine_type=machine_type, - accelerator_type=accelerator_type, - accelerator_count=accelerator_count, - ) - - resource_name = f"{uploaded_model.resource_name}@{uploaded_model.version_id}" - model.metadata["resourceName"] = resource_name - model.metadata["containerSpec"] = {"imageUri": serving_container_uri} - model.uri = uploaded_model.uri - model.TYPE_NAME = "google.VertexModel" - - with open(metrics.path, "r") as fp: - parsed_metrics = json.load(fp) - - logging.info(parsed_metrics) - for k, v in parsed_metrics.items(): - if type(v) is float: - metrics.log_metric(k, v) - - # return GCP resource for Vertex AI UI integration - custom_job_name = job.to_dict()["trainingTaskMetadata"]["backingCustomJob"] - custom_train_job_resources = GcpResources() - ctr = custom_train_job_resources.resources.add() - ctr.resource_type = "CustomJob" - ctr.resource_uri = custom_job_name - gcp_resources = MessageToJson(custom_train_job_resources) - return (gcp_resources,) diff --git a/components/vertex-components/src/vertex_components/import_model_evaluation.py b/components/vertex-components/src/vertex_components/import_model_evaluation.py deleted file mode 100644 index acfec517..00000000 --- a/components/vertex-components/src/vertex_components/import_model_evaluation.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright 2022 Google LLC - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# https://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from kfp.dsl import Input, Model, Metrics, component, Dataset -from typing import NamedTuple - - -@component( - base_image="python:3.7", - packages_to_install=[ - "google-cloud-aiplatform==1.24.1", - ], -) -def import_model_evaluation( - model: Input[Model], - metrics: Input[Metrics], - test_dataset: Input[Dataset], - pipeline_job_id: str, - project_location: str, - evaluation_name: str = "Imported evaluation", -) -> NamedTuple("Outputs", [("model_evaluation", str)]): - """Import an evaluation result for a model version. - - Args: - model (Model): Input model version. - metrics (Metrics): Input metrics. The contents of the artifact are expected - to follow the evaluation schema linked in the docs: - https://cloud.google.com/vertex-ai/docs/evaluation/introduction#features. - test_dataset (Dataset): Input test dataset which will be linked to evaluation. - pipeline_job_id (str): Pipeline job id which will be linked to evaluation. - project_location (str): Location of the Google Cloud project. - evaluation_name (str): Display name of model evaluation. - Returns: - model_evaluation (str): Resource URI of imported model evaluation. - """ - import json - import logging - from google.cloud.aiplatform_v1 import ModelEvaluation, ModelServiceClient - from google.protobuf.json_format import ParseDict - - logging.info(f"Read metrics from: {metrics.path}") - with open(metrics.path) as fp: - parsed_metrics = json.load(fp) - logging.info(f"Parsed metrics: {parsed_metrics}") - - schema_template = ( - "gs://google-cloud-aiplatform/schema/modelevaluation/%s_metrics_1.0.0.yaml" - ) - schema = schema_template % parsed_metrics.pop("problemType") - evaluation = { - "displayName": evaluation_name, - "metricsSchemaUri": schema, - "metrics": parsed_metrics, - "metadata": { - "pipeline_job_id": pipeline_job_id, - "evaluation_dataset_type": "gcs", - "evaluation_dataset_path": [test_dataset.uri], - }, - } - - request = ParseDict(evaluation, ModelEvaluation()._pb) - logging.info(f"Request: {request}") - - model_name = model.metadata["resourceName"] - logging.info(model_name) - - client = ModelServiceClient( - client_options={"api_endpoint": project_location + "-aiplatform.googleapis.com"} - ) - response = client.import_model_evaluation( - parent=model_name, - model_evaluation=request, - ) - logging.info(f"Response: {response}") - return (response.name,) diff --git a/components/vertex-components/src/vertex_components/lookup_model.py b/components/vertex-components/src/vertex_components/lookup_model.py deleted file mode 100644 index a4d6f3db..00000000 --- a/components/vertex-components/src/vertex_components/lookup_model.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright 2022 Google LLC - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# https://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from kfp.dsl import component, Output, Model -from typing import NamedTuple - - -@component( - base_image="python:3.7", - packages_to_install=["google-cloud-aiplatform==1.24.1"], -) -def lookup_model( - model_name: str, - project_location: str, - project_id: str, - model: Output[Model], - order_models_by: str = "create_time desc", - fail_on_model_not_found: bool = False, -) -> NamedTuple("Outputs", [("model_resource_name", str), ("training_dataset", dict)]): - """ - Fetch a model given a model name (display name) and export to GCS. - - Args: - model_name (str): display name of the model - project_location (str): location of the Google Cloud project - project_id (str): project id of the Google Cloud project - model (Output[Model]): a Vertex AI model - order_models_by (str): if multiple models are found based on the display name, - use a filter clause: - A comma-separated list of fields to order by, sorted in - ascending order. Use "desc" after a field name for descending. - Supported fields: `display_name`, `create_time`, `update_time` - Defaults to "create_time desc". - fail_on_model_not_found (bool): if set to True, raise runtime error if - model is not found - - Returns: - str: Resource name of the found model. Empty string if model not found. - """ - - import json - import logging - import os - from pathlib import Path - from google.cloud.aiplatform import Model - - TRAINING_DATASET_INFO = "training_dataset.json" - - logging.info(f"listing models with display name {model_name}") - models = Model.list( - filter=f'display_name="{model_name}"', - order_by=order_models_by, - location=project_location, - project=project_id, - ) - logging.info(f"found {len(models)} models") - - training_dataset = {} - model_resource_name = "" - if len(models) == 0: - logging.error( - f"No model found with name {model_name}" - + f"(project: {project_id} location: {project_location})" - ) - if fail_on_model_not_found: - raise RuntimeError(f"Failed as model was not found") - elif len(models) == 1: - target_model = models[0] - model_resource_name = target_model.resource_name - logging.info(f"choosing model by order ({order_models_by})") - logging.info(f"model display name: {target_model.display_name}") - logging.info(f"model resource name: {target_model.resource_name}") - logging.info(f"model uri: {target_model.uri}") - model.uri = target_model.uri - model.metadata["resourceName"] = target_model.resource_name - - path = Path(model.path) / TRAINING_DATASET_INFO - logging.info(f"Reading training dataset metadata: {path}") - - if os.path.exists(path): - with open(path, "r") as fp: - training_dataset = json.load(fp) - else: - logging.warning("Training dataset metadata doesn't exist!") - else: - raise RuntimeError(f"Multiple models with name {model_name} were found.") - - return model_resource_name, training_dataset diff --git a/components/vertex-components/src/vertex_components/model_batch_predict.py b/components/vertex-components/src/vertex_components/model_batch_predict.py index 36916ec4..80605a32 100644 --- a/components/vertex-components/src/vertex_components/model_batch_predict.py +++ b/components/vertex-components/src/vertex_components/model_batch_predict.py @@ -17,7 +17,7 @@ @component( - base_image="python:3.7", + base_image="python:3.9", packages_to_install=[ "google-cloud-aiplatform==1.24.1", "google-cloud-pipeline-components==1.0.33", diff --git a/components/vertex-components/src/vertex_components/update_best_model.py b/components/vertex-components/src/vertex_components/update_best_model.py deleted file mode 100644 index 9ec4ff9b..00000000 --- a/components/vertex-components/src/vertex_components/update_best_model.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright 2022 Google LLC -from typing import NamedTuple - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# https://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from kfp.dsl import Input, Model, component - - -@component( - base_image="python:3.7", - packages_to_install=["google-cloud-aiplatform==1.24.1"], -) -def update_best_model( - challenger: Input[Model], - challenger_evaluation: str, - parent_model: str, - project_id: str, - project_location: str, - eval_metric: str, - eval_lower_is_better: bool, - model_alias: str = "default", -) -> NamedTuple("Outputs", [("challenger_wins", bool)]): - """ - Args: - challenger (Model): Challenger model. - challenger_evaluation (str): Resource URI of challenger model evaluation e.g. - `projects/.../locations/.../models/.../evaluations/...` - parent_model (str): Resource URI of parent model. - eval_metric (str): Metric to compare champion and challenger on. - eval_lower_is_better (bool): Usually True for losses and - False for classification metrics. - project_id (str): project id of the Google Cloud project. - project_location (str): location of the Google Cloud project. - model_alias (str): alias of the parent model. - """ - - import logging - import google.cloud.aiplatform as aip - from google.cloud.aiplatform.models import ModelRegistry - from google.protobuf.json_format import MessageToDict - - logging.info("Get models...") - if model_alias: - parent_model += "@" + model_alias - champion = aip.Model(parent_model) - challenger = aip.Model(challenger.metadata["resourceName"]) - logging.info( - f"Model default version {champion.version_id} " - f"is being challenged by version {challenger.version_id}!" - ) - - eval_champion = challenger.get_model_evaluation() - eval_challenger = aip.model_evaluation.ModelEvaluation(challenger_evaluation) - metrics_champion = MessageToDict(eval_champion._gca_resource._pb)["metrics"] - metrics_challenger = MessageToDict(eval_challenger._gca_resource._pb)["metrics"] - - logging.info(f"Comparing {eval_metric} of models") - logging.debug(f"Champion metrics: {metrics_champion}") - logging.debug(f"Challenger metrics: {metrics_challenger}") - - m_champ = metrics_champion[eval_metric] - m_chall = metrics_challenger[eval_metric] - logging.info(f"Champion={m_champ} Challenger={m_chall}") - - challenger_wins = ( - (m_chall < m_champ) if eval_lower_is_better else (m_chall > m_champ) - ) - if challenger_wins: - logging.info(f"Updating champion to version: {challenger.version_id}") - model_registry = ModelRegistry( - model=champion, project=project_id, location=project_location - ) - model_registry.add_version_aliases(["default"], challenger.version_id) - return (True,) - - logging.info(f"Keeping current champion!") - return (False,) diff --git a/components/vertex-components/src/vertex_components/upload_model.py b/components/vertex-components/src/vertex_components/upload_model.py new file mode 100644 index 00000000..be65b866 --- /dev/null +++ b/components/vertex-components/src/vertex_components/upload_model.py @@ -0,0 +1,216 @@ +# Copyright 2022 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# https://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from kfp.dsl import Dataset, Input, Metrics, Model, Output, component +from google_cloud_pipeline_components.types import artifact_types + + +@component( + base_image="python:3.9", + packages_to_install=[ + "google-cloud-aiplatform==1.28.1", + "google-cloud-pipeline-components==2.1.0", + ], +) +def upload_model( + model: Input[Model], + serving_container_image: str, + vertex_model: Output[artifact_types.VertexModel], + project: str, + location: str, + model_evaluation: Input[Metrics], + eval_metric: str, + eval_lower_is_better: bool, + model_name: str, + pipeline_job_id: str, + test_dataset: Input[Dataset], + evaluation_name: str = "Imported evaluation", + order_models_by: str = "create_time desc", +) -> None: + """ + Args: + challenger (Model): Challenger model. + challenger_evaluation (str): Resource URI of challenger model evaluation e.g. + `projects/.../locations/.../models/.../evaluations/...` + parent_model (str): Resource URI of parent model. + eval_metric (str): Metric to compare champion and challenger on. + eval_lower_is_better (bool): Usually True for losses and + False for classification metrics. + project_id (str): project id of the Google Cloud project. + project_location (str): location of the Google Cloud project. + model_alias (str): alias of the parent model. + """ + + import json + import logging + import google.cloud.aiplatform as aip + from google.protobuf.json_format import MessageToDict + + def lookup_model( + order_models_by: str, + location: str, + project: str, + model_name: str, + ) -> aip.Model: + + logging.info(f"listing models with display name {model_name}") + logging.info(f"choosing model by order ({order_models_by})") + models = aip.Model.list( + filter=f'display_name="{model_name}"', + order_by=order_models_by, + location=location, + project=project, + ) + logging.info(f"found {len(models)} models") + + if len(models) == 0: + logging.info( + f"No model found with name {model_name}" + + f"(project: {project} location: {location})" + ) + return None + elif len(models) == 1: + return models[0] + else: + raise RuntimeError(f"Multiple models with name {model_name} were found.") + + def compare_models( + champion_metrics: dict, + challenger_metrics: dict, + eval_metric: str, + eval_lower_is_better, + ) -> bool: + + logging.info(f"Comparing {eval_metric} of models") + logging.debug(f"Champion metrics: {champion_metrics}") + logging.debug(f"Challenger metrics: {challenger_metrics}") + + m_champ = champion_metrics[eval_metric] + m_chall = challenger_metrics[eval_metric] + logging.info(f"Champion={m_champ} Challenger={m_chall}") + + challenger_wins = ( + (m_chall < m_champ) if eval_lower_is_better else (m_chall > m_champ) + ) + + if challenger_wins: + logging.info("Challenger wins!") + else: + logging.info("Champion wins!") + + return challenger_wins + + def import_evaluation( + parsed_metrics: dict, + challenger_model: aip.Model, + location: str, + evaluation_name: str = "Imported evaluation", + ): + from google.cloud.aiplatform_v1 import ModelEvaluation, ModelServiceClient + from google.protobuf.json_format import ParseDict + + logging.info(f"Parsed metrics: {parsed_metrics}") + + schema_template = ( + "gs://google-cloud-aiplatform/schema/modelevaluation/%s_metrics_1.0.0.yaml" + ) + schema = schema_template % parsed_metrics.pop("problemType") + evaluation = { + "displayName": evaluation_name, + "metricsSchemaUri": schema, + "metrics": parsed_metrics, + "metadata": { + "pipeline_job_id": pipeline_job_id, + "evaluation_dataset_type": "gcs", + "evaluation_dataset_path": [test_dataset.uri], + }, + } + + request = ParseDict(evaluation, ModelEvaluation()._pb) + logging.info(f"Request: {request}") + + model_name = challenger_model.versioned_resource_name + logging.info(model_name) + + client = ModelServiceClient( + client_options={"api_endpoint": location + "-aiplatform.googleapis.com"} + ) + response = client.import_model_evaluation( + parent=model_name, + model_evaluation=request, + ) + logging.info(f"Response: {response}") + return (response.name,) + + # Parse metrics to dict + with open(model_evaluation.path, "r") as f: + metrics = json.load(f) + + champion_model = lookup_model( + order_models_by=order_models_by, + location=location, + project=project, + model_name=model_name, + ) + + if champion_model is not None: + # Compare models + logging.info( + f"Model default version {champion_model.version_id} " + "is being challenged by new model." + ) + # Look up Vertex model evaluation for champion model + eval_champion = champion_model.get_model_evaluation() + champion_metrics = MessageToDict(eval_champion._gca_resource._pb)["metrics"] + + # Take challenger metrics as input to this component + challenger_metrics = metrics + + challenger_wins = compare_models( + champion_metrics=champion_metrics, + challenger_metrics=challenger_metrics, + eval_metric=eval_metric, + eval_lower_is_better=eval_lower_is_better, + ) + + else: + logging.info("No champion model found, uploading new model.") + challenger_wins = True + + # Upload model to registry + uploaded_model = aip.Model.upload( + display_name=model_name, + artifact_uri=model.uri, + serving_container_image_uri=serving_container_image, + parent_model=( + champion_model.resource_name if champion_model is not None else None + ), + is_default_version=challenger_wins, + # TODO more model options + ) + + # Output google.VertexModel artifact + vertex_model.uri = ( + f"https://{location}-aiplatform.googleapis.com/v1/" + f"{uploaded_model.versioned_resource_name}" + ) + vertex_model.metadata["resourceName"] = uploaded_model.versioned_resource_name + + # Import evaluation to model registry + import_evaluation( + location=location, + parsed_metrics=metrics, + challenger_model=uploaded_model, + evaluation_name=evaluation_name, + ) diff --git a/env.sh.example b/env.sh.example index 3e747dfe..e57c7dbb 100644 --- a/env.sh.example +++ b/env.sh.example @@ -28,3 +28,4 @@ export RESOURCE_SUFFIX=default export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com export PIPELINE_FILES_GCS_PATH=gs://${VERTEX_PROJECT_ID}-pl-assets/${RESOURCE_SUFFIX} export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root +export TRAINING_CONTAINER_IMAGE=${VERTEX_LOCATION}-docker.pkg.dev/${VERTEX_PROJECT_ID}/vertex-images/training:${RESOURCE_SUFFIX} diff --git a/pipelines/.python-version b/pipelines/.python-version index f7e5aa84..9f3d4c17 100644 --- a/pipelines/.python-version +++ b/pipelines/.python-version @@ -1 +1 @@ -3.7.12 +3.9.16 diff --git a/pipelines/poetry.lock b/pipelines/poetry.lock index 5fc4247c..50d59be0 100644 --- a/pipelines/poetry.lock +++ b/pipelines/poetry.lock @@ -1,11 +1,11 @@ -# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "bigquery-components" version = "0.1.0" description = "KubeFlow components for BigQuery" optional = false -python-versions = ">=3.7,<3.11" +python-versions = ">=3.9,<3.11" files = [] develop = true @@ -146,7 +146,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" @@ -411,12 +410,12 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)"] [[package]] name = "google-cloud-pipeline-components" -version = "2.0.0" +version = "2.1.0" description = "This SDK enables a set of First Party (Google owned) pipeline components that allow users to take their experience from Vertex AI SDK and other Google Cloud services and create a corresponding pipeline using KFP or Managed Pipelines." optional = false python-versions = ">=3.7.0,<3.12.0" files = [ - {file = "google_cloud_pipeline_components-2.0.0-py3-none-any.whl", hash = "sha256:4a3708dda8d68989f37d6b4e184e783ab3dc2ff8f0ddf69f5cd4a160603ce252"}, + {file = "google_cloud_pipeline_components-2.1.0-py3-none-any.whl", hash = "sha256:ddd6afd8e1ab302a3176d60934f289fb609faeacea09292f62d7eb3576e3e321"}, ] [package.dependencies] @@ -696,26 +695,6 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] -[[package]] -name = "importlib-metadata" -version = "6.6.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, - {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, -] - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - [[package]] name = "iniconfig" version = "2.0.0" @@ -767,7 +746,6 @@ protobuf = ">=3.13.0,<4" PyYAML = ">=5.3,<7" requests-toolbelt = ">=0.8.0,<1" tabulate = ">=0.8.6,<1" -typing-extensions = {version = ">=3.7.4,<5", markers = "python_version < \"3.9\""} urllib3 = "<2.0.0" [package.extras] @@ -940,9 +918,6 @@ files = [ {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.5", markers = "python_version < \"3.8\""} - [package.extras] docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] @@ -958,9 +933,6 @@ files = [ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] @@ -979,7 +951,6 @@ files = [ [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" @@ -1071,7 +1042,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" @@ -1307,17 +1277,6 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "typing-extensions" -version = "4.6.3" -description = "Backported and Experimental Type Hints for Python 3.7+" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, - {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, -] - [[package]] name = "urllib3" version = "1.26.16" @@ -1339,7 +1298,7 @@ name = "vertex-components" version = "0.1.0" description = "KubeFlow components for Vertex AI" optional = false -python-versions = ">=3.7,<3.11" +python-versions = ">=3.9,<3.11" files = [] develop = true @@ -1364,7 +1323,6 @@ files = [ [package.dependencies] distlib = ">=0.3.6,<1" filelock = ">=3.11,<4" -importlib-metadata = {version = ">=6.4.1", markers = "python_version < \"3.8\""} platformdirs = ">=3.2,<4" [package.extras] @@ -1387,22 +1345,7 @@ docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] optional = ["python-socks", "wsaccel"] test = ["websockets"] -[[package]] -name = "zipp" -version = "3.15.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - [metadata] lock-version = "2.0" -python-versions = ">=3.7,<3.11" -content-hash = "6360ac432e14023430d2290fcbd782c7693b0365570fd50f4ecc865d04a5718d" +python-versions = ">=3.9,<3.11" +content-hash = "8430eb49df1dcdfd43c734f1850e97657a2f1b346cb7407778cf7f4cf1be46a0" diff --git a/pipelines/pyproject.toml b/pipelines/pyproject.toml index eb468200..08c07fad 100644 --- a/pipelines/pyproject.toml +++ b/pipelines/pyproject.toml @@ -7,17 +7,17 @@ readme = "README.md" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.9", ] packages = [ { include = "pipelines", from = "src" }, ] [tool.poetry.dependencies] -python = ">=3.7,<3.11" +python = ">=3.9,<3.11" Jinja2 = ">=3.0.1,<4.0.0" google-cloud-aiplatform = "1.24.1" -google-cloud-pipeline-components = "^2.0.0" +google-cloud-pipeline-components = "^2.1.0" bigquery-components = { path = "../components/bigquery-components", develop = true } vertex-components = { path = "../components/vertex-components", develop = true } kfp = "^2.0.1" diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 411bc69e..228d7afa 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -17,14 +17,45 @@ from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp from kfp import compiler, dsl +from kfp.dsl import Dataset, Input, Metrics, Model, Output, OutputPath from pipelines import generate_query from bigquery_components import extract_bq_to_dataset -from vertex_components import ( - lookup_model, - custom_train_job, - import_model_evaluation, - update_best_model, -) +from vertex_components import upload_model + +IMAGE = os.environ.get("TRAINING_CONTAINER_IMAGE") + + +@dsl.container_component +def train( + train_data: Input[Dataset], + valid_data: Input[Dataset], + test_data: Input[Dataset], + model: Output[Model], + model_output_uri: OutputPath(str), + metrics: Output[Metrics], # TODO could be a more specific type of metrics object + hparams: dict, +): + return dsl.ContainerSpec( + image=IMAGE, + command=["python"], + args=[ + "main.py", + "--train-data", + train_data.path, + "--valid-data", + valid_data.path, + "--test-data", + test_data.path, + "--model", + model.path, + "--model-output-uri", + model_output_uri, + "--metrics", + metrics.path, + "--hparams", + hparams, + ], + ) @dsl.pipeline(name="xgboost-train-pipeline") @@ -81,7 +112,6 @@ def xgboost_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" - train_script_uri = f"{pipeline_files_gcs_path}/assets/train_xgb_model.py" hparams = dict( n_estimators=200, early_stopping_rounds=10, @@ -157,52 +187,27 @@ def xgboost_pipeline( .set_caching_options(False) ).outputs["dataset"] - existing_model = ( - lookup_model( - model_name=model_name, - project_location=project_location, - project_id=project_id, - fail_on_model_not_found=False, - ) - .set_display_name("Lookup past model") - .set_caching_options(False) - .outputs["model_resource_name"] - ) - - train_model = custom_train_job( - train_script_uri=train_script_uri, + train_model = train( train_data=train_dataset, valid_data=valid_dataset, test_data=test_dataset, - project_id=project_id, - project_location=project_location, - model_display_name=model_name, - train_container_uri="europe-docker.pkg.dev/vertex-ai/training/scikit-learn-cpu.0-23:latest", # noqa: E501 - serving_container_uri="europe-docker.pkg.dev/vertex-ai/prediction/sklearn-cpu.0-24:latest", # noqa: E501 hparams=hparams, - requirements=["scikit-learn==0.24.0"], - staging_bucket=staging_bucket, - parent_model=existing_model, ).set_display_name("Train model") - evaluation = import_model_evaluation( + upload_model_op = upload_model( + project=project_id, + location=project_location, + model_evaluation=train_model.outputs["metrics"], + eval_metric=primary_metric, + eval_lower_is_better=True, model=train_model.outputs["model"], - metrics=train_model.outputs["metrics"], - test_dataset=test_dataset, + serving_container_image=( + "europe-docker.pkg.dev/vertex-ai/prediction/sklearn-cpu.0-24:latest" + ), + model_name=model_name, pipeline_job_id="{{$.pipeline_job_name}}", - project_location=project_location, - ).set_display_name("Import evaluation") - - with dsl.Condition(existing_model != "", "champion-exists"): - update_best_model( - challenger=train_model.outputs["model"], - challenger_evaluation=evaluation.outputs["model_evaluation"], - parent_model=existing_model, - eval_metric=primary_metric, - eval_lower_is_better=True, - project_id=project_id, - project_location=project_location, - ).set_display_name("Update best model") + test_dataset=test_dataset, + ).set_display_name("Upload model") if __name__ == "__main__": diff --git a/terraform/modules/vertex_deployment/main.tf b/terraform/modules/vertex_deployment/main.tf index 35498a20..b0a808ee 100644 --- a/terraform/modules/vertex_deployment/main.tf +++ b/terraform/modules/vertex_deployment/main.tf @@ -122,3 +122,13 @@ resource "google_vertex_ai_metadata_store" "default_metadata_store" { region = var.region depends_on = [google_project_service.gcp_services] } + +## Artifact Registry ## +resource "google_artifact_registry_repository" "vertex-images" { + repository_id = "vertex-images" + description = "Container image repository for training container images" + project = var.project_id + location = var.region + format = "DOCKER" + depends_on = [google_project_service.gcp_services] +} diff --git a/training/.gitignore b/training/.gitignore new file mode 100644 index 00000000..4414fc1e --- /dev/null +++ b/training/.gitignore @@ -0,0 +1 @@ +requirements.txt diff --git a/training/Dockerfile b/training/Dockerfile new file mode 100644 index 00000000..bf4587f2 --- /dev/null +++ b/training/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.9.16-slim +ENV PIP_NO_CACHE_DIR=off \ + PIP_DISABLE_PIP_VERSION_CHECK=on \ + PIP_DEFAULT_TIMEOUT=100 + +COPY requirements.txt requirements.txt +RUN pip install -r requirements.txt +COPY main.py main.py diff --git a/training/main.py b/training/main.py new file mode 100644 index 00000000..18486e2d --- /dev/null +++ b/training/main.py @@ -0,0 +1,147 @@ +import argparse +from pathlib import Path + +import joblib +import json +import os +import logging + +import numpy as np +import pandas as pd +from sklearn import metrics +from sklearn.compose import ColumnTransformer +from sklearn.pipeline import Pipeline +from sklearn.preprocessing import StandardScaler, OrdinalEncoder, OneHotEncoder +from xgboost import XGBRegressor + +logging.basicConfig(level=logging.DEBUG) + +# used for monitoring during prediction time +TRAINING_DATASET_INFO = "training_dataset.json" +# numeric/categorical features in Chicago trips dataset to be preprocessed +NUM_COLS = ["dayofweek", "hourofday", "trip_distance", "trip_miles", "trip_seconds"] +ORD_COLS = ["company"] +OHE_COLS = ["payment_type"] + + +def split_xy(df: pd.DataFrame, label: str) -> (pd.DataFrame, pd.Series): + """Split dataframe into X and y.""" + return df.drop(columns=[label]), df[label] + + +def indices_in_list(elements: list, base_list: list) -> list: + """Get indices of specific elements in a base list""" + return [idx for idx, elem in enumerate(base_list) if elem in elements] + + +parser = argparse.ArgumentParser() +parser.add_argument("--train-data", type=str, required=True) +parser.add_argument("--valid-data", type=str, required=True) +parser.add_argument("--test-data", type=str, required=True) +parser.add_argument("--model", default=os.getenv("AIP_MODEL_DIR"), type=str, help="") +parser.add_argument("--model-output-uri", type=str) +parser.add_argument("--metrics", type=str, required=True) +parser.add_argument("--hparams", default={}, type=json.loads) +args = parser.parse_args() + +if args.model.startswith("gs://"): + args.model = "/gcs/" + args.model[5:] + +logging.info("Read csv files into dataframes") +df_train = pd.read_csv(args.train_data) +df_valid = pd.read_csv(args.valid_data) +df_test = pd.read_csv(args.test_data) + +logging.info("Split dataframes") +label = args.hparams["label"] +X_train, y_train = split_xy(df_train, label) +X_valid, y_valid = split_xy(df_valid, label) +X_test, y_test = split_xy(df_test, label) + +logging.info("Get the number of unique categories for ordinal encoded columns") +ordinal_columns = X_train[ORD_COLS] +n_unique_cat = ordinal_columns.nunique() + +logging.info("Get indices of columns in base data") +col_list = X_train.columns.tolist() +num_indices = indices_in_list(NUM_COLS, col_list) +cat_indices_onehot = indices_in_list(OHE_COLS, col_list) +cat_indices_ordinal = indices_in_list(ORD_COLS, col_list) + +ordinal_transformers = [ + ( + f"ordinal encoding for {ord_col}", + OrdinalEncoder( + handle_unknown="use_encoded_value", unknown_value=n_unique_cat[ord_col] + ), + [ord_index], + ) + for ord_col in ORD_COLS + for ord_index in cat_indices_ordinal +] +all_transformers = [ + ("numeric_scaling", StandardScaler(), num_indices), + ( + "one_hot_encoding", + OneHotEncoder(handle_unknown="ignore"), + cat_indices_onehot, + ), +] + ordinal_transformers + +logging.info("Build sklearn preprocessing steps") +preprocesser = ColumnTransformer(transformers=all_transformers) +logging.info("Build sklearn pipeline with XGBoost model") +xgb_model = XGBRegressor(**args.hparams) + +pipeline = Pipeline( + steps=[("feature_engineering", preprocesser), ("train_model", xgb_model)] +) + +logging.info("Transform validation data") +valid_preprocesser = preprocesser.fit(X_train) +X_valid_transformed = valid_preprocesser.transform(X_valid) + +logging.info("Fit model") +pipeline.fit(X_train, y_train, train_model__eval_set=[(X_valid_transformed, y_valid)]) + +logging.info("Predict test data") +y_pred = pipeline.predict(X_test) +y_pred = y_pred.clip(0) + +metrics = { + "problemType": "regression", + "rootMeanSquaredError": np.sqrt(metrics.mean_squared_error(y_test, y_pred)), + "meanAbsoluteError": metrics.mean_absolute_error(y_test, y_pred), + "meanAbsolutePercentageError": metrics.mean_absolute_percentage_error( + y_test, y_pred + ), + "rSquared": metrics.r2_score(y_test, y_pred), + "rootMeanSquaredLogError": np.sqrt(metrics.mean_squared_log_error(y_test, y_pred)), +} + +logging.info(f"Save model to: {args.model}") +Path(args.model).mkdir(parents=True) +joblib.dump(pipeline, f"{args.model}/model.joblib") + +model_uri = "gs://" + args.model[5:] +with open(args.model_output_uri, "w") as f: + f.write(model_uri) + +logging.info(f"Metrics: {metrics}") +with open(args.metrics, "w") as fp: + json.dump(metrics, fp) + +# Persist URIs of training file(s) for model monitoring in batch predictions +# See https://cloud.google.com/python/docs/reference/aiplatform/latest/google.cloud.aiplatform_v1beta1.types.ModelMonitoringObjectiveConfig.TrainingDataset # noqa: E501 +# for the expected schema. +path = f"{args.model}/{TRAINING_DATASET_INFO}" +training_dataset_for_monitoring = { + "gcsSource": {"uris": [args.train_data]}, + "dataFormat": "csv", + "targetField": label, +} +logging.info(f"Training dataset info: {training_dataset_for_monitoring}") + +with open(path, "w") as fp: + logging.info(f"Save training dataset info for model monitoring: {path}") + json.dump(training_dataset_for_monitoring, fp) diff --git a/training/poetry.lock b/training/poetry.lock new file mode 100644 index 00000000..611c8c73 --- /dev/null +++ b/training/poetry.lock @@ -0,0 +1,283 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "joblib" +version = "1.3.1" +description = "Lightweight pipelining with Python functions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "joblib-1.3.1-py3-none-any.whl", hash = "sha256:89cf0529520e01b3de7ac7b74a8102c90d16d54c64b5dd98cafcd14307fdf915"}, + {file = "joblib-1.3.1.tar.gz", hash = "sha256:1f937906df65329ba98013dc9692fe22a4c5e4a648112de500508b18a21b41e3"}, +] + +[[package]] +name = "numpy" +version = "1.25.1" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77d339465dff3eb33c701430bcb9c325b60354698340229e1dff97745e6b3efa"}, + {file = "numpy-1.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d736b75c3f2cb96843a5c7f8d8ccc414768d34b0a75f466c05f3a739b406f10b"}, + {file = "numpy-1.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a90725800caeaa160732d6b31f3f843ebd45d6b5f3eec9e8cc287e30f2805bf"}, + {file = "numpy-1.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c6c9261d21e617c6dc5eacba35cb68ec36bb72adcff0dee63f8fbc899362588"}, + {file = "numpy-1.25.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0def91f8af6ec4bb94c370e38c575855bf1d0be8a8fbfba42ef9c073faf2cf19"}, + {file = "numpy-1.25.1-cp310-cp310-win32.whl", hash = "sha256:fd67b306320dcadea700a8f79b9e671e607f8696e98ec255915c0c6d6b818503"}, + {file = "numpy-1.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:c1516db588987450b85595586605742879e50dcce923e8973f79529651545b57"}, + {file = "numpy-1.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b82655dd8efeea69dbf85d00fca40013d7f503212bc5259056244961268b66e"}, + {file = "numpy-1.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e8f6049c4878cb16960fbbfb22105e49d13d752d4d8371b55110941fb3b17800"}, + {file = "numpy-1.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41a56b70e8139884eccb2f733c2f7378af06c82304959e174f8e7370af112e09"}, + {file = "numpy-1.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5154b1a25ec796b1aee12ac1b22f414f94752c5f94832f14d8d6c9ac40bcca6"}, + {file = "numpy-1.25.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38eb6548bb91c421261b4805dc44def9ca1a6eef6444ce35ad1669c0f1a3fc5d"}, + {file = "numpy-1.25.1-cp311-cp311-win32.whl", hash = "sha256:791f409064d0a69dd20579345d852c59822c6aa087f23b07b1b4e28ff5880fcb"}, + {file = "numpy-1.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:c40571fe966393b212689aa17e32ed905924120737194b5d5c1b20b9ed0fb171"}, + {file = "numpy-1.25.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d7abcdd85aea3e6cdddb59af2350c7ab1ed764397f8eec97a038ad244d2d105"}, + {file = "numpy-1.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a180429394f81c7933634ae49b37b472d343cccb5bb0c4a575ac8bbc433722f"}, + {file = "numpy-1.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d412c1697c3853c6fc3cb9751b4915859c7afe6a277c2bf00acf287d56c4e625"}, + {file = "numpy-1.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20e1266411120a4f16fad8efa8e0454d21d00b8c7cee5b5ccad7565d95eb42dd"}, + {file = "numpy-1.25.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f76aebc3358ade9eacf9bc2bb8ae589863a4f911611694103af05346637df1b7"}, + {file = "numpy-1.25.1-cp39-cp39-win32.whl", hash = "sha256:247d3ffdd7775bdf191f848be8d49100495114c82c2bd134e8d5d075fb386a1c"}, + {file = "numpy-1.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:1d5d3c68e443c90b38fdf8ef40e60e2538a27548b39b12b73132456847f4b631"}, + {file = "numpy-1.25.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:35a9527c977b924042170a0887de727cd84ff179e478481404c5dc66b4170009"}, + {file = "numpy-1.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d3fe3dd0506a28493d82dc3cf254be8cd0d26f4008a417385cbf1ae95b54004"}, + {file = "numpy-1.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:012097b5b0d00a11070e8f2e261128c44157a8689f7dedcf35576e525893f4fe"}, + {file = "numpy-1.25.1.tar.gz", hash = "sha256:9a3a9f3a61480cc086117b426a8bd86869c213fc4072e606f01c4e4b66eb92bf"}, +] + +[[package]] +name = "pandas" +version = "2.0.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, + {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0"}, + {file = "pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210"}, + {file = "pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df"}, + {file = "pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd"}, + {file = "pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0"}, + {file = "pandas-2.0.3-cp38-cp38-win32.whl", hash = "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02"}, + {file = "pandas-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641"}, + {file = "pandas-2.0.3-cp39-cp39-win32.whl", hash = "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682"}, + {file = "pandas-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc"}, + {file = "pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.20.3", markers = "python_version < \"3.10\""}, + {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.1" + +[package.extras] +all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] +aws = ["s3fs (>=2021.08.0)"] +clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"] +compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"] +computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"] +feather = ["pyarrow (>=7.0.0)"] +fss = ["fsspec (>=2021.07.0)"] +gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"] +hdf5 = ["tables (>=3.6.1)"] +html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"] +mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"] +parquet = ["pyarrow (>=7.0.0)"] +performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"] +plot = ["matplotlib (>=3.6.1)"] +postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"] +spss = ["pyreadstat (>=1.1.2)"] +sql-other = ["SQLAlchemy (>=1.4.16)"] +test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.6.3)"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2023.3" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, + {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, +] + +[[package]] +name = "scikit-learn" +version = "1.3.0" +description = "A set of python modules for machine learning and data mining" +optional = false +python-versions = ">=3.8" +files = [ + {file = "scikit-learn-1.3.0.tar.gz", hash = "sha256:8be549886f5eda46436b6e555b0e4873b4f10aa21c07df45c4bc1735afbccd7a"}, + {file = "scikit_learn-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:981287869e576d42c682cf7ca96af0c6ac544ed9316328fd0d9292795c742cf5"}, + {file = "scikit_learn-1.3.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:436aaaae2c916ad16631142488e4c82f4296af2404f480e031d866863425d2a2"}, + {file = "scikit_learn-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7e28d8fa47a0b30ae1bd7a079519dd852764e31708a7804da6cb6f8b36e3630"}, + {file = "scikit_learn-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae80c08834a473d08a204d966982a62e11c976228d306a2648c575e3ead12111"}, + {file = "scikit_learn-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:552fd1b6ee22900cf1780d7386a554bb96949e9a359999177cf30211e6b20df6"}, + {file = "scikit_learn-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79970a6d759eb00a62266a31e2637d07d2d28446fca8079cf9afa7c07b0427f8"}, + {file = "scikit_learn-1.3.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:850a00b559e636b23901aabbe79b73dc604b4e4248ba9e2d6e72f95063765603"}, + {file = "scikit_learn-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee04835fb016e8062ee9fe9074aef9b82e430504e420bff51e3e5fffe72750ca"}, + {file = "scikit_learn-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d953531f5d9f00c90c34fa3b7d7cfb43ecff4c605dac9e4255a20b114a27369"}, + {file = "scikit_learn-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:151ac2bf65ccf363664a689b8beafc9e6aae36263db114b4ca06fbbbf827444a"}, + {file = "scikit_learn-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6a885a9edc9c0a341cab27ec4f8a6c58b35f3d449c9d2503a6fd23e06bbd4f6a"}, + {file = "scikit_learn-1.3.0-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:9877af9c6d1b15486e18a94101b742e9d0d2f343d35a634e337411ddb57783f3"}, + {file = "scikit_learn-1.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c470f53cea065ff3d588050955c492793bb50c19a92923490d18fcb637f6383a"}, + {file = "scikit_learn-1.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd6e2d7389542eae01077a1ee0318c4fec20c66c957f45c7aac0c6eb0fe3c612"}, + {file = "scikit_learn-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:3a11936adbc379a6061ea32fa03338d4ca7248d86dd507c81e13af428a5bc1db"}, + {file = "scikit_learn-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:998d38fcec96584deee1e79cd127469b3ad6fefd1ea6c2dfc54e8db367eb396b"}, + {file = "scikit_learn-1.3.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ded35e810438a527e17623ac6deae3b360134345b7c598175ab7741720d7ffa7"}, + {file = "scikit_learn-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e8102d5036e28d08ab47166b48c8d5e5810704daecf3a476a4282d562be9a28"}, + {file = "scikit_learn-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7617164951c422747e7c32be4afa15d75ad8044f42e7d70d3e2e0429a50e6718"}, + {file = "scikit_learn-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:1d54fb9e6038284548072df22fd34777e434153f7ffac72c8596f2d6987110dd"}, +] + +[package.dependencies] +joblib = ">=1.1.1" +numpy = ">=1.17.3" +scipy = ">=1.5.0" +threadpoolctl = ">=2.0.0" + +[package.extras] +benchmark = ["matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "pandas (>=1.0.5)"] +docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)", "sphinx (>=6.0.0)", "sphinx-copybutton (>=0.5.2)", "sphinx-gallery (>=0.10.1)", "sphinx-prompt (>=1.3.0)", "sphinxext-opengraph (>=0.4.2)"] +examples = ["matplotlib (>=3.1.3)", "pandas (>=1.0.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)"] +tests = ["black (>=23.3.0)", "matplotlib (>=3.1.3)", "mypy (>=1.3)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.0.272)", "scikit-image (>=0.16.2)"] + +[[package]] +name = "scipy" +version = "1.9.3" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "scipy-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1884b66a54887e21addf9c16fb588720a8309a57b2e258ae1c7986d4444d3bc0"}, + {file = "scipy-1.9.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:83b89e9586c62e787f5012e8475fbb12185bafb996a03257e9675cd73d3736dd"}, + {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a72d885fa44247f92743fc20732ae55564ff2a519e8302fb7e18717c5355a8b"}, + {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01e1dd7b15bd2449c8bfc6b7cc67d630700ed655654f0dfcf121600bad205c9"}, + {file = "scipy-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:68239b6aa6f9c593da8be1509a05cb7f9efe98b80f43a5861cd24c7557e98523"}, + {file = "scipy-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b41bc822679ad1c9a5f023bc93f6d0543129ca0f37c1ce294dd9d386f0a21096"}, + {file = "scipy-1.9.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:90453d2b93ea82a9f434e4e1cba043e779ff67b92f7a0e85d05d286a3625df3c"}, + {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c06e62a390a9167da60bedd4575a14c1f58ca9dfde59830fc42e5197283dab"}, + {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abaf921531b5aeaafced90157db505e10345e45038c39e5d9b6c7922d68085cb"}, + {file = "scipy-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:06d2e1b4c491dc7d8eacea139a1b0b295f74e1a1a0f704c375028f8320d16e31"}, + {file = "scipy-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a04cd7d0d3eff6ea4719371cbc44df31411862b9646db617c99718ff68d4840"}, + {file = "scipy-1.9.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:545c83ffb518094d8c9d83cce216c0c32f8c04aaf28b92cc8283eda0685162d5"}, + {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d54222d7a3ba6022fdf5773931b5d7c56efe41ede7f7128c7b1637700409108"}, + {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cff3a5295234037e39500d35316a4c5794739433528310e117b8a9a0c76d20fc"}, + {file = "scipy-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:2318bef588acc7a574f5bfdff9c172d0b1bf2c8143d9582e05f878e580a3781e"}, + {file = "scipy-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d644a64e174c16cb4b2e41dfea6af722053e83d066da7343f333a54dae9bc31c"}, + {file = "scipy-1.9.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:da8245491d73ed0a994ed9c2e380fd058ce2fa8a18da204681f2fe1f57f98f95"}, + {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4db5b30849606a95dcf519763dd3ab6fe9bd91df49eba517359e450a7d80ce2e"}, + {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c68db6b290cbd4049012990d7fe71a2abd9ffbe82c0056ebe0f01df8be5436b0"}, + {file = "scipy-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:5b88e6d91ad9d59478fafe92a7c757d00c59e3bdc3331be8ada76a4f8d683f58"}, + {file = "scipy-1.9.3.tar.gz", hash = "sha256:fbc5c05c85c1a02be77b1ff591087c83bc44579c6d2bd9fb798bb64ea5e1a027"}, +] + +[package.dependencies] +numpy = ">=1.18.5,<1.26.0" + +[package.extras] +dev = ["flake8", "mypy", "pycodestyle", "typing_extensions"] +doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-panels (>=0.5.2)", "sphinx-tabs"] +test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "threadpoolctl" +version = "3.1.0" +description = "threadpoolctl" +optional = false +python-versions = ">=3.6" +files = [ + {file = "threadpoolctl-3.1.0-py3-none-any.whl", hash = "sha256:8b99adda265feb6773280df41eece7b2e6561b772d21ffd52e372f999024907b"}, + {file = "threadpoolctl-3.1.0.tar.gz", hash = "sha256:a335baacfaa4400ae1f0d8e3a58d6674d2f8828e3716bb2802c44955ad391380"}, +] + +[[package]] +name = "tzdata" +version = "2023.3" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + +[[package]] +name = "xgboost" +version = "1.7.6" +description = "XGBoost Python Package" +optional = false +python-versions = ">=3.8" +files = [ + {file = "xgboost-1.7.6-py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.macosx_12_0_x86_64.whl", hash = "sha256:4c34675b4d2678c624ddde5d45361e7e16046923e362e4e609b88353e6b87124"}, + {file = "xgboost-1.7.6-py3-none-macosx_12_0_arm64.whl", hash = "sha256:59b4b366d2cafc7f645e87d897983a5b59be02876194b1d213bd8d8b811d8ce8"}, + {file = "xgboost-1.7.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:281c3c6f4fbed2d36bf95cd02a641afa95e72e9abde70064056da5e76233e8df"}, + {file = "xgboost-1.7.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:b1d5db49b199152d62bd9217c98760207d3de86d2b9d243260c573ffe638f80a"}, + {file = "xgboost-1.7.6-py3-none-win_amd64.whl", hash = "sha256:127cf1f5e2ec25cd41429394c6719b87af1456ce583e89f0bffd35d02ad18bcb"}, + {file = "xgboost-1.7.6.tar.gz", hash = "sha256:1c527554a400445e0c38186039ba1a00425dcdb4e40b37eed0e74cb39a159c47"}, +] + +[package.dependencies] +numpy = "*" +scipy = "*" + +[package.extras] +dask = ["dask", "distributed", "pandas"] +datatable = ["datatable"] +pandas = ["pandas"] +plotting = ["graphviz", "matplotlib"] +pyspark = ["cloudpickle", "pyspark", "scikit-learn"] +scikit-learn = ["scikit-learn"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "5bcbe70fcb0085df96641192352231b73948cafcdaa211acffd4b4091e70c874" diff --git a/training/pyproject.toml b/training/pyproject.toml new file mode 100644 index 00000000..ad6df515 --- /dev/null +++ b/training/pyproject.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "training" +version = "0.1.0" +description = "" +authors = ["Your Name "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.9" +scikit-learn = "^1.3.0" +xgboost = "^1.7.6" +pandas = "^2.0.3" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" From 0a2ffebdf120edb2ea1c283757fe9d35f33dc512 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Tue, 25 Jul 2023 13:42:36 +0100 Subject: [PATCH 126/238] feat: replace docker build with gcloud build --- Makefile | 7 +++++-- terraform/modules/vertex_deployment/main.tf | 8 ++++---- terraform/modules/vertex_deployment/outputs.tf | 4 ++-- training/.gcloudignore | 2 ++ 4 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 training/.gcloudignore diff --git a/Makefile b/Makefile index d441b7f7..eb62931e 100644 --- a/Makefile +++ b/Makefile @@ -109,5 +109,8 @@ destroy-infra: ## DESTROY the Terraform infrastructure in your project. Requires build-training-container: ## Build and push training container image using Docker @ cd training && \ poetry export -f requirements.txt -o requirements.txt && \ - docker build -t ${TRAINING_CONTAINER_IMAGE} . && \ - docker push ${TRAINING_CONTAINER_IMAGE} + gcloud builds submit . \ + --tag=${TRAINING_CONTAINER_IMAGE} \ + --region=${VERTEX_LOCATION} \ + --project=${VERTEX_PROJECT_ID} \ + --gcs-source-staging-dir=gs://${VERTEX_PROJECT_ID}-staging/source diff --git a/terraform/modules/vertex_deployment/main.tf b/terraform/modules/vertex_deployment/main.tf index b0a808ee..335ca64f 100644 --- a/terraform/modules/vertex_deployment/main.tf +++ b/terraform/modules/vertex_deployment/main.tf @@ -75,9 +75,9 @@ resource "google_pubsub_topic" "pipeline_trigger_topic" { depends_on = [google_project_service.gcp_services] } -# Cloud Function staging bucket -resource "google_storage_bucket" "cf_staging_bucket" { - name = "${var.project_id}-cf-staging" +# Cloud Function / Cloud Build staging bucket +resource "google_storage_bucket" "staging_bucket" { + name = "${var.project_id}-staging" location = local.cloudfunction_region project = var.project_id uniform_bucket_level_access = true @@ -93,7 +93,7 @@ module "cloudfunction" { function_name = var.cloudfunction_name description = var.cloudfunction_description source_dir = "../../../pipelines/src/pipelines/trigger" - source_code_bucket_name = google_storage_bucket.cf_staging_bucket.name + source_code_bucket_name = google_storage_bucket.staging_bucket.name runtime = "python39" entry_point = "cf_handler" cf_service_account = google_service_account.vertex_cloudfunction_sa.email diff --git a/terraform/modules/vertex_deployment/outputs.tf b/terraform/modules/vertex_deployment/outputs.tf index 80bd3462..608e9bcc 100644 --- a/terraform/modules/vertex_deployment/outputs.tf +++ b/terraform/modules/vertex_deployment/outputs.tf @@ -18,8 +18,8 @@ output "pubsub_topic_id" { value = google_pubsub_topic.pipeline_trigger_topic.id } -output "cf_staging_bucket_name" { - value = google_storage_bucket.cf_staging_bucket.name +output "staging_bucket_name" { + value = google_storage_bucket.staging_bucket.name } output "pipeline_root_bucket_name" { diff --git a/training/.gcloudignore b/training/.gcloudignore new file mode 100644 index 00000000..510bf9ce --- /dev/null +++ b/training/.gcloudignore @@ -0,0 +1,2 @@ +.venv +.DS_Store From e612b31d8f0db7876e73c5d9fe377f0acc394f18 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Tue, 25 Jul 2023 17:27:11 +0100 Subject: [PATCH 127/238] feat: replace GCS buckets with KFP AR for compiled pipelines and remove assets bucket --- .gitignore | 1 + CONTRIBUTING.md | 9 - Makefile | 29 +- README.md | 25 +- cloudbuild/README.md | 8 +- cloudbuild/e2e-test.yaml | 17 +- cloudbuild/release.yaml | 32 +- components/vertex-components/README.md | 7 +- .../src/vertex_components/__init__.py | 8 - .../src/vertex_components/upload_model.py | 1 - .../tests/test_custom_training_job.py | 103 ------ docs/PRODUCTION.md | 34 +- docs/TESTING_SETUP.md | 17 +- docs/images/cf_view.png | Bin 79756 -> 80012 bytes env.sh.example | 1 - pipelines/assets/.gitkeep | 0 pipelines/assets/train_tf_model.py | 299 ------------------ pipelines/assets/train_xgb_model.py | 142 --------- .../tensorflow/prediction/pipeline.py | 12 +- .../pipelines/tensorflow/training/pipeline.py | 15 +- .../src/pipelines/utils/upload_pipeline.py | 19 ++ .../pipelines/xgboost/prediction/pipeline.py | 12 +- .../pipelines/xgboost/training/pipeline.py | 18 +- .../scheduled_jobs.auto.tfvars.example | 20 +- terraform/modules/vertex_deployment/iam.tf | 22 -- terraform/modules/vertex_deployment/main.tf | 21 +- .../modules/vertex_deployment/outputs.tf | 4 - 27 files changed, 97 insertions(+), 779 deletions(-) delete mode 100644 components/vertex-components/tests/test_custom_training_job.py delete mode 100644 pipelines/assets/.gitkeep delete mode 100644 pipelines/assets/train_tf_model.py delete mode 100644 pipelines/assets/train_xgb_model.py create mode 100644 pipelines/src/pipelines/utils/upload_pipeline.py diff --git a/.gitignore b/.gitignore index 930b7889..e879a6d6 100644 --- a/.gitignore +++ b/.gitignore @@ -165,3 +165,4 @@ env.sh # Compiled pipelines **/*pipeline.json +**/*pipeline.yaml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 39ee273e..905de864 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -271,12 +271,3 @@ make help Some of these rules use the environment variables specified in [`env.sh`](env.sh). **It is not expected that you will need to change the Makefile or create a new one.** - -## Assets folder - -For a brief description of the Assets folder, please refer to our [general documentation](README.md#Assets). -To make sure that assets are available while running the ML pipelines, `make run` ensure that these will be uploaded automatically to the respective Google Cloud Storage locations. - -### Common assets - -Within the [assets](./assets/) folder, there are common files stored which need to be uploaded to Google Cloud Storage so that the pipelines running Vertex AI can consume such assets, namely: diff --git a/Makefile b/Makefile index eb62931e..179d929c 100644 --- a/Makefile +++ b/Makefile @@ -33,9 +33,9 @@ test-trigger: ## Runs unit tests for the pipeline trigger code @cd pipelines && \ poetry run python -m pytest tests/trigger -compile-pipeline: ## Compile the pipeline to training.json or prediction.json. Must specify pipeline= +compile-pipeline: ## Compile the pipeline to pipeline.yaml. Must specify pipeline= @cd pipelines/src && \ - poetry run python -m pipelines.${PIPELINE_TEMPLATE}.${pipeline}.pipeline + poetry run kfp dsl compile --py pipelines/${PIPELINE_TEMPLATE}/${pipeline}/pipeline.py --output pipelines/${PIPELINE_TEMPLATE}/${pipeline}/pipeline.yaml --function pipeline setup-components: ## Run unit tests for a component group @cd "components/${GROUP}" && \ @@ -71,28 +71,13 @@ test-all-components-coverage: ## Run tests with coverage $(MAKE) test-components-coverage GROUP=$$(basename $$component_group) ; \ done -sync-assets: ## Sync assets folder to GCS. - @if [ -d "./pipelines/assets/" ]; then \ - echo "Syncing assets to GCS"; \ - gsutil -m rsync -r -d ./pipelines/assets $(PIPELINE_FILES_GCS_PATH)/assets ; \ - else \ - echo "No assets folder found"; \ - fi; - -run: ## Compile pipeline, copy assets to GCS, and run pipeline in sandbox environment. Must specify pipeline=. Optionally specify enable_pipeline_caching= (defaults to default Vertex caching behaviour) +run: ## Compile pipeline and run pipeline in sandbox environment. Must specify pipeline=. Optionally specify enable_pipeline_caching= (defaults to default Vertex caching behaviour) @ $(MAKE) compile-pipeline && \ - $(MAKE) sync-assets && \ cd pipelines/src && \ - poetry run python -m pipelines.trigger --template_path=./$(pipeline).json --enable_caching=$(enable_pipeline_caching) - -sync_assets ?= true -e2e-tests: ## (Optionally) copy assets to GCS, and perform end-to-end (E2E) pipeline tests. Must specify pipeline=. Optionally specify enable_pipeline_caching= (defaults to default Vertex caching behaviour). Optionally specify sync_assets= (defaults to true) - @if [ $$sync_assets = true ] ; then \ - $(MAKE) sync-assets; \ - else \ - echo "Skipping syncing assets to GCS"; \ - fi && \ - cd pipelines && \ + poetry run python -m pipelines.trigger --template_path=pipelines/${PIPELINE_TEMPLATE}/${pipeline}/pipeline.yaml --enable_caching=$(enable_pipeline_caching) + +e2e-tests: ## Perform end-to-end (E2E) pipeline tests. Must specify pipeline=. Optionally specify enable_pipeline_caching= (defaults to default Vertex caching behaviour). + @ cd pipelines && \ poetry run pytest --log-cli-level=INFO tests/${PIPELINE_TEMPLATE}/$(pipeline) --enable_caching=$(enable_pipeline_caching) env ?= dev diff --git a/README.md b/README.md index 6fcd6069..efc9196c 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ The cloud infrastructure is managed using Terraform and is defined in the [`terr - `cloudfunction` - deploys a (Pub/Sub-triggered) Cloud Function from local source code - `scheduled_pipelines` - deploys Cloud Scheduler jobs that will trigger Vertex Pipeline runs (via the above Cloud Function) -- `vertex_deployment` - deploys Cloud infrastructure required for running Vertex Pipelines, including enabling APIs, creating buckets, service accounts, and IAM permissions. +- `vertex_deployment` - deploys Cloud infrastructure required for running Vertex Pipelines, including enabling APIs, creating buckets, Artifact Registry repos, service accounts, and IAM permissions. There is a Terraform configuration for each environment (dev/test/prod) under [`terraform/envs`](terraform/envs/). @@ -158,6 +158,14 @@ bq mk --transfer_config \ --params='{"source_dataset_id":"'"chicago_taxi_trips"'","source_project_id":"'"bigquery-public-data"'"}' ``` +#### Building the training container image + +Build the container image and push it to Artifact Registry with: + +```bash +make build-training-container +``` + #### Running Pipelines You can run the XGBoost training pipeline (for example) with: @@ -175,7 +183,6 @@ make run pipeline= This will execute the pipeline using the chosen template on Vertex AI, namely it will: 1. Compile the pipeline using the Kubeflow Pipelines SDK -1. Copy the `assets` folders to Cloud Storage 1. Trigger the pipeline with the help of `pipelines/trigger/main.py` To avoid resource conflicts when running pipelines concurrently in the same Google Cloud project with multiple developers, populate the `RESOURCE_SUFFIX` environment variable in your `env.sh` file. This will append your defined suffix to Google resources and ensure each developer's resources remain unique and separate, preventing unintentional overwriting. @@ -186,12 +193,6 @@ The ML pipelines have input parameters. As you can see in the pipeline definitio When triggering ad hoc runs in your dev/sandbox environment, or when running the E2E tests in CI, these default values are used. For the test and production deployments, the pipeline parameters are defined in the Terraform code for the Cloud Scheduler jobs (`terraform/envs//variables.auto.tfvars`). -### Assets - -The folder `pipelines/assets/` can be used for any additional files that may be needed during execution of the pipelines. -Most importantly this can include your training scripts. -This directory is rsync'd to Google Cloud Storage when running a pipeline in the sandbox environment or as part of the CD pipeline (see [CI/CD setup](cloudbuild/README.md)). - ## Testing Unit tests and end-to-end (E2E) pipeline tests are performed using [pytest](https://docs.pytest.org). @@ -211,7 +212,7 @@ make test-components GROUP=vertex-components To run end-to-end tests of a single pipeline, you can use: ``` -make e2e-tests pipeline= [ enable_caching= ] [ sync_assets= ] +make e2e-tests pipeline= [ enable_caching= ] ``` There are also unit tests for the pipeline triggering code. @@ -245,8 +246,8 @@ To schedule pipelines into an environment, you will need to provide the `cloud_s There are five CI/CD pipelines located under the [cloudbuild](cloudbuild) directory: 1. `pr-checks.yaml` - runs pre-commit checks and unit tests on the custom KFP components, and checks that the ML pipelines (training and prediction) can compile. -2. `e2e-test.yaml` - copies the "assets" folders to the chosen GCS destination (versioned by git commit hash - see below) and runs end-to-end tests of the training and prediction pipeline. -3. `release.yaml` - Compiles the training and prediction pipelines, and copies the compiled pipelines, along with their respective `assets` directories, to Google Cloud Storage in the build / CI/CD environment. The Google Cloud Storage destination is namespaced using the git tag (see below). Following this, the E2E tests are run on the new compiled pipelines. +2. `e2e-test.yaml` - runs end-to-end tests of the training and prediction pipeline. +3. `release.yaml` - Compiles the training and prediction pipelines, and copies the compiled pipelines to Google Cloud Storage in the build / CI/CD environment. The Google Cloud Storage destination is namespaced using the git tag (see below). Following this, the E2E tests are run on the new compiled pipelines. Below is a diagram of how the files are published in each environment in the `e2e-test.yaml` and `release.yaml` pipelines: @@ -255,8 +256,6 @@ Below is a diagram of how the files are published in each environment in the `e2 └── TAG_NAME or GIT COMMIT HASH <-- Git tag used for the release (release.yaml) OR git commit hash (e2e-test.yaml) ├── training.json ├── prediction.json - ├── assets - │ └── some_useful_file.json ``` 4. `terraform-plan.yaml` - Checks the Terraform configuration under `terraform/envs/` (e.g. `terraform/envs/test`), and produces a summary of any proposed changes that will be applied on merge to the main branch. diff --git a/cloudbuild/README.md b/cloudbuild/README.md index d541b034..7adc610f 100644 --- a/cloudbuild/README.md +++ b/cloudbuild/README.md @@ -21,8 +21,8 @@ limitations under the License. There are five CI/CD pipelines 1. `pr-checks.yaml` - runs pre-commit checks and unit tests on the custom KFP components, and checks that the ML pipelines (training and prediction) can compile. -1. `e2e-test.yaml` - copies the "assets" folder to the chosen GCS destination (versioned by git commit hash) and runs end-to-end tests of the training and prediction pipeline. -1. `release.yaml` - compiles training and prediction pipelines, then copies the compiled pipelines and "assets" folder to the chosen GCS destination (versioned by git tag). +1. `e2e-test.yaml` - runs end-to-end tests of the training and prediction pipeline. +1. `release.yaml` - compiles training and prediction pipelines, then copies the compiled pipelines to the chosen GCS destination (versioned by git tag). 1. `terraform-plan.yaml` - Checks the Terraform configuration under `terraform/envs/` (e.g. `terraform/envs/test`), and produces a summary of any proposed changes that will be applied on merge to the main branch. 1. `terraform-apply.yaml` - Applies the Terraform configuration under `terraform/envs/` (e.g. `terraform/envs/test`). @@ -61,7 +61,6 @@ Set up a trigger for the `e2e-test.yaml` pipeline, and provide substitution valu | Variable | Description | Suggested value | |---|---|---| -| `_PIPELINE_PUBLISH_GCS_PATH` | The GCS folder (i.e. path prefix) where the pipeline files will be copied to. See the [Assets](../README.md#assets) section of the main README for more information. | `gs://-pl-assets/e2e-tests` | | `_PIPELINE_TEMPLATE` | The set of pipelines in the repo that you would like to use - i.e. the subfolder under `pipelines` where your pipelines live. | Currently, can be either `xgboost` or `tensorflow`. | | `_TEST_VERTEX_CMEK_IDENTIFIER` | Optional. ID of the CMEK (Customer Managed Encryption Key) that you want to use for the ML pipeline runs in the E2E tests as part of the CI/CD pipeline with the format `projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key` | Leave blank | | `_TEST_VERTEX_LOCATION` | The Google Cloud region where you want to run the ML pipelines in the E2E tests as part of the CI/CD pipeline. | Your chosen Google Cloud region | @@ -69,7 +68,6 @@ Set up a trigger for the `e2e-test.yaml` pipeline, and provide substitution valu | `_TEST_VERTEX_PIPELINE_ROOT` | The GCS folder (i.e. path prefix) that you want to use for the pipeline artifacts and for passing data between stages in the pipeline. Used during the pipeline runs in the E2E tests as part of the CI/CD pipeline. | `gs://-pl-root` | | `_TEST_VERTEX_PROJECT_ID` | Google Cloud project ID in which you want to run the ML pipelines in the E2E tests as part of the CI/CD pipeline. | Project ID for the dev environment | | `_TEST_VERTEX_SA_EMAIL` | Email address of the service account you want to use to run the ML pipelines in the E2E tests as part of the CI/CD pipeline. | `vertex-pipelines@.iam.gserviceaccount.com` | -| `_TEST_TRAIN_STATS_GCS_PATH` | GCS path to use for storing the statistics computed about the training dataset used in the training pipeline. | `gs://-pl-root/train_stats/train.stats` | | `_TEST_ENABLE_PIPELINE_CACHING` | Override the default caching behaviour of the ML pipelines. Leave blank to use the default caching behaviour. | `False` | We recommend to enable comment control for this trigger (select `Required` under `Comment Control`). This will mean that the end-to-end tests will only run once a repository collaborator or owner comments `/gcbrun` on the pull request. @@ -89,7 +87,7 @@ Set up a trigger for the `release.yaml` pipeline, and provide substitution value | Variable | Description | Suggested value | |---|---|---| -| `_PIPELINE_PUBLISH_GCS_PATHS` | The (space separated) GCS folders (plural!) where the pipeline files (compiled pipelines + pipeline assets) will be copied to. See the [Assets](../README.md#assets) section of the main README for more information. | `gs://-pl-assets gs://-pl-assets gs://-pl-assets` | +| `_PIPELINE_PUBLISH_AR_PATHS` | The (space separated) Artifact Registry repositories (plural!) where the compiled pipelines will be copied to. | `gs://europe-west2-kfp.pkg.dev//vertex-pipelines gs://europe-west2-kfp.pkg.dev//vertex-pipelines gs://europe-west2-kfp.pkg.dev//vertex-pipelines` | | `_PIPELINE_TEMPLATE` | The set of pipelines in the repo that you would like to use - i.e. the subfolder under `pipelines` where your pipelines live. | Currently, can be either `xgboost` or `tensorflow`. | ### On merge to `main` / `master` branch diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index b3d054da..2a9af102 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -14,18 +14,6 @@ --- steps: - # Copy assets files to a new directory in GCS - # (via a new local directory) - # Directory name = git commit hash - - name: gcr.io/cloud-builders/gsutil - entrypoint: bash - args: - - -c - - | - mkdir -p ${COMMIT_SHA}/assets && \ - cp -r pipelines/assets ${COMMIT_SHA} && \ - gsutil cp -r ${COMMIT_SHA} ${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} - # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines - name: python:3.9 @@ -34,8 +22,8 @@ steps: - -c - | make setup && \ - make e2e-tests pipeline=training enable_pipeline_caching=False sync_assets=false && \ - make e2e-tests pipeline=prediction enable_pipeline_caching=False sync_assets=false + make e2e-tests pipeline=training enable_pipeline_caching=False && \ + make e2e-tests pipeline=prediction enable_pipeline_caching=False env: - VERTEX_LOCATION=${_TEST_VERTEX_LOCATION} - VERTEX_PROJECT_ID=${_TEST_VERTEX_PROJECT_ID} @@ -44,7 +32,6 @@ steps: - VERTEX_NETWORK=${_TEST_VERTEX_NETWORK} - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - - PIPELINE_FILES_GCS_PATH=${_PIPELINE_PUBLISH_GCS_PATH}/${COMMIT_SHA} - RESOURCE_SUFFIX=${COMMIT_SHA} options: diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index 0c22abdb..f2ac361c 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -22,26 +22,24 @@ steps: - | make setup && \ make compile-pipeline pipeline=training && \ - make compile-pipeline pipeline=prediction + make compile-pipeline pipeline=prediction && \ + cd pipelines && \ + for dest in ${_PIPELINE_PUBLISH_AR_PATHS} ; do \ + poetry run python -m pipelines.utils.upload_pipeline \ + --dest=$$dest \ + --yaml=src/pipelines/${_PIPELINE_TEMPLATE}/training/pipeline.yaml \ + --tag=latest \ + --tag=${TAG_NAME} && \ + poetry run python -m pipelines.utils.upload_pipeline \ + --dest=$$dest \ + --yaml=src/pipelines/${_PIPELINE_TEMPLATE}/prediction/pipeline.yaml \ + --tag=latest \ + --tag=${TAG_NAME}; \ + done + env: - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - # Copy pipelines and files to a new directory in GCS - # (via a new local directory) - # Directory name = git commit hash - - name: gcr.io/cloud-builders/gsutil - entrypoint: bash - args: - - -c - - | - mkdir -p ${TAG_NAME}/assets && \ - cp pipelines/src/training.json ${TAG_NAME}/training.json && \ - cp pipelines/src/prediction.json ${TAG_NAME}/prediction.json && \ - cp -r pipelines/assets/* ${TAG_NAME}/assets/ && \ - for dest in ${_PIPELINE_PUBLISH_GCS_PATHS} ; do \ - gsutil cp -r ${TAG_NAME} $$dest ; \ - done - options: logging: CLOUD_LOGGING_ONLY diff --git a/components/vertex-components/README.md b/components/vertex-components/README.md index bd674ccc..8252c9f0 100644 --- a/components/vertex-components/README.md +++ b/components/vertex-components/README.md @@ -16,13 +16,10 @@ limitations under the License. # Vertex Components -A python package which provides common KubeFlow components for interacting with Vertex AI. +A Python package which provides common KubeFlow components for interacting with Vertex AI. Currently, the following components are implemented: -- `custom_train_job`: Train a model in a [Custom Training Job](https://cloud.google.com/vertex-ai/docs/training/create-custom-job). -- `import_model_evaluation`: Import model evaluation results to a model in the model registry. -- `lookup_model`: Look up a model which was previously uploaded to the model registry. +- `upload_model`: Uploads a new model version to the Vertex Model Registry, importing a model evaluation, and updating the "default" tag on the model if the new version (challenger) is superior to the previous (champion) model. - `model_batch_predict`: Run a [Batch Prediction Job](https://cloud.google.com/ai-platform/prediction/docs/batch-predict). -- `update_best_model`: Using two model evaluations and a comparison metric, update the better model to the default model. These components either augment, extend, or add new functionalities that aren't found in [Google Cloud Pipeline Components list](https://cloud.google.com/vertex-ai/docs/pipelines/gcpc-list). diff --git a/components/vertex-components/src/vertex_components/__init__.py b/components/vertex-components/src/vertex_components/__init__.py index 4175abff..040039b3 100644 --- a/components/vertex-components/src/vertex_components/__init__.py +++ b/components/vertex-components/src/vertex_components/__init__.py @@ -1,17 +1,9 @@ -from .custom_train_job import custom_train_job -from .import_model_evaluation import import_model_evaluation -from .lookup_model import lookup_model from .model_batch_predict import model_batch_predict -from .update_best_model import update_best_model from .upload_model import upload_model __version__ = "0.0.1" __all__ = [ - "custom_train_job", - "import_model_evaluation", - "lookup_model", "model_batch_predict", - "update_best_model", "upload_model", ] diff --git a/components/vertex-components/src/vertex_components/upload_model.py b/components/vertex-components/src/vertex_components/upload_model.py index be65b866..59838d37 100644 --- a/components/vertex-components/src/vertex_components/upload_model.py +++ b/components/vertex-components/src/vertex_components/upload_model.py @@ -197,7 +197,6 @@ def import_evaluation( champion_model.resource_name if champion_model is not None else None ), is_default_version=challenger_wins, - # TODO more model options ) # Output google.VertexModel artifact diff --git a/components/vertex-components/tests/test_custom_training_job.py b/components/vertex-components/tests/test_custom_training_job.py deleted file mode 100644 index 3cd43b6e..00000000 --- a/components/vertex-components/tests/test_custom_training_job.py +++ /dev/null @@ -1,103 +0,0 @@ -import google.cloud.aiplatform as aip # noqa -from kfp.dsl import Dataset, Metrics, Artifact -from unittest import mock -import pytest -import json - -import vertex_components - -custom_train_job = vertex_components.custom_train_job.python_func - - -@mock.patch("google.cloud.aiplatform.CustomTrainingJob") -@mock.patch("os.path.exists") -@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="{}") -def test_custom_train_job(mock_open, mock_exists, mock_job, tmpdir): - """ - Checks that the custom job method is called - """ - mock_exists.return_value = True - - mock_train_data = Dataset(uri=tmpdir) - mock_valid_data = Dataset(uri=tmpdir) - mock_test_data = Dataset(uri=tmpdir) - mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) - mock_metrics = Metrics(uri=tmpdir) - - mock_job_instance = mock_job.return_value - mock_job_instance.to_dict.return_value = { - "trainingTaskMetadata": {"backingCustomJob": "mock_custom_job_name"} - } - - (gcp_resources,) = custom_train_job( - train_script_uri="gs://my-bucket/train_script.py", - train_data=mock_train_data, - valid_data=mock_valid_data, - test_data=mock_test_data, - project_id="my-project-id", - project_location="europe-west4", - model_display_name="my-model", - train_container_uri="gcr.io/my-project/my-image:latest", - serving_container_uri="gcr.io/my-project/my-serving-image:latest", - model=mock_model, - metrics=mock_metrics, - staging_bucket="gs://my-bucket", - job_name="my-job", - ) - - mock_job.assert_called_once_with( - project="my-project-id", - location="europe-west4", - staging_bucket="gs://my-bucket", - display_name="my-job", - script_path="/gcs/my-bucket/train_script.py", - container_uri="gcr.io/my-project/my-image:latest", - requirements=None, - model_serving_container_image_uri="gcr.io/my-project/my-serving-image:latest", # noqa: E501 - ) - - # Assert metrics loading - mock_open.assert_called_once_with(tmpdir, "r") - # Assert gcp_resources contains the expected value - assert ( - json.loads(gcp_resources)["resources"][0]["resourceUri"] - == "mock_custom_job_name" - ) - - -@mock.patch("google.cloud.aiplatform.CustomTrainingJob") -@mock.patch("os.path.exists") -@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="{}") -def test_custom_train_script_not_found(mock_open, mock_exists, mock_job, tmpdir): - """ - Checks that when the training script is not found - the method fails - """ - mock_exists.return_value = False - - mock_train_data = Dataset(uri=tmpdir) - mock_valid_data = Dataset(uri=tmpdir) - mock_test_data = Dataset(uri=tmpdir) - mock_model = Artifact(uri=tmpdir, metadata={"resourceName": ""}) - mock_metrics = Metrics(uri=tmpdir) - - with pytest.raises(ValueError): - (gcp_resources,) = custom_train_job( - train_script_uri="gs://my-bucket/train_script.py", - train_data=mock_train_data, - valid_data=mock_valid_data, - test_data=mock_test_data, - project_id="my-project-id", - project_location="europe-west4", - model_display_name="my-model", - train_container_uri="gcr.io/my-project/my-image:latest", - serving_container_uri="gcr.io/my-project/my-serving-image:latest", - model=mock_model, - metrics=mock_metrics, - staging_bucket="gs://my-bucket", - job_name="my-job", - ) - - # Assert the custom training job is not executed - mock_job.assert_not_called() - mock_open.assert_not_called() diff --git a/docs/PRODUCTION.md b/docs/PRODUCTION.md index db4a0f5d..47fb4a7e 100644 --- a/docs/PRODUCTION.md +++ b/docs/PRODUCTION.md @@ -54,24 +54,14 @@ When the new tag is created, the `release.yaml` pipeline should be triggered. It - `_PIPELINE_PUBLISH_GCS_PATHS` = `gs://-pl-assets gs://-pl-assets gs://-pl-assets` Assuming your end-to-end tests pass, your compiled training pipeline will be published to: -- `gs://-pl-assets/v1.2/training/training.json` -- `gs://-pl-assets/v1.2/training/training.json` -- `gs://-pl-assets/v1.2/training/training.json` - -The contents of your assets folder for your training pipeline will be published to: -- `gs://-pl-assets/v1.2/training/assets/` -- `gs://-pl-assets/v1.2/training/assets/` -- `gs://-pl-assets/v1.2/training/assets/` +- `https://-kfp.pkg.dev//vertex-pipelines/xgboost-train-pipeline/v1.2` +- `https://-kfp.pkg.dev//vertex-pipelines/xgboost-train-pipeline/v1.2` +- `https://-kfp.pkg.dev//vertex-pipelines/xgboost-train-pipeline/v1.2` Similarly, your compiled prediction pipeline will be published in this location: -- `gs://-pl-assets/v1.2/prediction/prediction.json` -- `gs://-pl-assets/v1.2/prediction/prediction.json` -- `gs://-pl-assets/v1.2/prediction/prediction.json` - -The contents of your assets folder for your prediction pipeline will be published in this location: -- `gs://-pl-assets/v1.2/prediction/assets/` -- `gs://-pl-assets/v1.2/prediction/assets/` -- `gs://-pl-assets/v1.2/prediction/assets/` +- `https://-kfp.pkg.dev//vertex-pipelines/xgboost-prediction-pipeline/v1.2` +- `https://-kfp.pkg.dev//vertex-pipelines/xgboost-prediction-pipeline/v1.2` +- `https://-kfp.pkg.dev//vertex-pipelines/xgboost-prediction-pipeline/v1.2` | :exclamation: IMPORTANT | |:---------------------------| @@ -79,7 +69,7 @@ The contents of your assets folder for your prediction pipeline will be publishe ## Deploying a release to the test environment -Now that you have created a release, and the compiled pipelines (and their `assets` files) have been copied to the test and prod environments, you can now schedule your pipelines to run in those environments. +Now that you have created a release, and the compiled pipelines have been copied to the test and prod environments, you can now schedule your pipelines to run in those environments. Of course, we will begin by scheduling the pipelines to run in the test environment. @@ -97,17 +87,14 @@ cloud_schedulers_config = { description = "Trigger my XGBoost training pipeline in Vertex" schedule = "0 0 1 * *" time_zone = "UTC" - template_path = "gs://-pl-assets/v1.2/training/training.json" + template_path = "https://-kfp.pkg.dev//vertex-pipelines/xgboost-train-pipeline/" enable_caching = null pipeline_parameters = { project_id = project_location = "europe-west2" - pipeline_files_gcs_path = "gs://-pl-assets/v1.2/training/assets" ingestion_project_id = model_name = "xgboost-with-preprocessing" model_label = "label_name" - tfdv_schema_filename = "tfdv_schema_training.pbtxt" - tfdv_train_stats_path = "gs://-pl-root/train_stats/train.stats" dataset_id = "preprocessing" dataset_location = "europe-west2" ingestion_dataset_id = "chicago_taxi_trips" @@ -120,17 +107,14 @@ cloud_schedulers_config = { description = "Trigger my XGBoost prediction pipeline in Vertex" schedule = "0 0 * * *" time_zone = "UTC" - template_path = "gs://-pl-assets/v1.2/prediction/prediction.json" + template_path = "https://-kfp.pkg.dev//vertex-pipelines/xgboost-prediction-pipeline/" enable_caching = null pipeline_parameters = { project_id = project_location = "europe-west2" - pipeline_files_gcs_path = "gs://-pl-assets/v1.2/prediction/assets" ingestion_project_id = model_name = "xgboost-with-preprocessing" model_label = "label_name" - tfdv_schema_filename = "tfdv_schema_prediction.pbtxt" - tfdv_train_stats_path = "gs://-pl-root/train_stats/train.stats" dataset_id = "preprocessing" dataset_location = "europe-west2" ingestion_dataset_id = "chicago_taxi_trips" diff --git a/docs/TESTING_SETUP.md b/docs/TESTING_SETUP.md index cdf9ca00..ef358f09 100644 --- a/docs/TESTING_SETUP.md +++ b/docs/TESTING_SETUP.md @@ -47,15 +47,6 @@ storage.googleapis.com \ --project $GCP_PROJECT_ID ``` -### Google Cloud Storage buckets - -Two buckets will need to be created - one for publishing the compiled JSON pipelines (and any other files required for running the pipelines), and one for the pipeline root. - -``` -gsutil mb -l ${GCP_REGION} -p ${GCP_PROJECT_ID} gs://${GCP_PROJECT_ID}-pl-root -gsutil mb -l ${GCP_REGION} -p ${GCP_PROJECT_ID} gs://${GCP_PROJECT_ID}-assets -``` - ### BigQuery Create a new BigQuery dataset for the Chicago Taxi data: @@ -137,15 +128,11 @@ gcloud projects add-iam-policy-binding $GCP_PROJECT_ID --member="serviceAccount: gcloud projects add-iam-policy-binding $GCP_PROJECT_ID --member="serviceAccount:vertex-pipelines@${GCP_PROJECT_ID}.iam.gserviceaccount.com" --role="roles/bigquery.jobUser" --condition=None ``` -The Vertex Pipelines service account requires read access to the assets bucket, read/write access to the pipeline root bucket, and access to list both buckets: +The Vertex Pipelines service account requires read/write/list access to the pipeline root bucket: ``` -gsutil iam ch serviceAccount:vertex-pipelines@${GCP_PROJECT_ID}.iam.gserviceaccount.com:objectViewer gs://${GCP_PROJECT_ID}-assets - gsutil iam ch serviceAccount:vertex-pipelines@${GCP_PROJECT_ID}.iam.gserviceaccount.com:objectAdmin gs://${GCP_PROJECT_ID}-pl-root -gsutil iam ch serviceAccount:vertex-pipelines@${GCP_PROJECT_ID}.iam.gserviceaccount.com:legacyBucketReader gs://${GCP_PROJECT_ID}-assets - gsutil iam ch serviceAccount:vertex-pipelines@${GCP_PROJECT_ID}.iam.gserviceaccount.com:legacyBucketReader gs://${GCP_PROJECT_ID}-pl-root ``` @@ -181,4 +168,4 @@ For each of the above, create a Cloud Build trigger with the following settings: | `pr-checks.yaml` (xgboost) | _PIPELINE_TEMPLATE = `xgboost` | | `trigger-tests.yaml` | | | `e2e-test.yaml` (tensorflow) | _PIPELINE_TEMPLATE = `tensorflow`
_PIPELINE_PUBLISH_GCS_PATH = `gs://-assets/e2e-test-tensorflow`
_TEST_ENABLE_PIPELINE_CACHING = `False`
_TEST_TRAIN_STATS_GCS_PATH = `gs://-pl-root/e2e-test-tensorflow/train-stats/train.stats`
_TEST_VERTEX_LOCATION = ``
_TEST_VERTEX_PIPELINE_ROOT = `gs://-pl-root`
_TEST_VERTEX_PROJECT_ID = ``
_TEST_VERTEX_SA_EMAIL = `vertex-pipelines@.iam.gserviceaccount.com` | -| `e2e-test.yaml` (xgboost) | _PIPELINE_TEMPLATE = `xgboost`
_PIPELINE_PUBLISH_GCS_PATH = `gs://-assets/e2e-test-xgboost`
_TEST_ENABLE_PIPELINE_CACHING = `False`
_TEST_TRAIN_STATS_GCS_PATH = `gs://-pl-root/e2e-test-xgboost/train-stats/train.stats`
_TEST_VERTEX_LOCATION = ``
_TEST_VERTEX_PIPELINE_ROOT = `gs://-pl-root`
_TEST_VERTEX_PROJECT_ID = ``
_TEST_VERTEX_SA_EMAIL = `vertex-pipelines@.iam.gserviceaccount.com` | +| `e2e-test.yaml` (xgboost) | _PIPELINE_TEMPLATE = `xgboost`
_TEST_ENABLE_PIPELINE_CACHING = `False`
_TEST_VERTEX_LOCATION = ``
_TEST_VERTEX_PIPELINE_ROOT = `gs://-pl-root`
_TEST_VERTEX_PROJECT_ID = ``
_TEST_VERTEX_SA_EMAIL = `vertex-pipelines@.iam.gserviceaccount.com` | diff --git a/docs/images/cf_view.png b/docs/images/cf_view.png index 7a81234d9a3045e3860fc5360ec008b3659c42e6..06ba46262b680a84e636064cb049736f0f0c9c43 100644 GIT binary patch literal 80012 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 z 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 Dataset: - """Create a TF Dataset from input csv files. - Args: - input_data (Input[Dataset]): Train/Valid data in CSV format - label_name (str): Name of column containing the labels - model_params (dict): model parameters - file_pattern (str): Read data from one or more files. If empty, then - training and validation data is read from single file respectively. - For multiple files, use a pattern e.g. "files-*.csv". - Returns: - dataset (TF Dataset): TF dataset where each element is a (features, labels) - tuple that corresponds to a batch of CSV rows - """ - - # shuffle & shuffle_buffer_size added to rearrange input data - # passed into model training - # num_rows_for_inference is for auto detection of datatypes - # while creating the dataset. - # If a float column has a high proportion of integer values (0/1 etc), - # the method wrongly detects it as a tf.int32 which fails during training time, - # hence the high hardcoded value (default is 100) - - # Apply data sharding: Sharded elements are produced by the dataset - # Each worker will process the whole dataset and discard the portion that is - # not for itself. Note that for this mode to correctly partitions the dataset - # elements, the dataset needs to produce elements in a deterministic order. - data_options = tf.data.Options() - data_options.experimental_distribute.auto_shard_policy = ( - tf.data.experimental.AutoShardPolicy.DATA - ) - - logging.info(f"Creating dataset from CSV file(s) at {input_data}...") - created_dataset = tf.data.experimental.make_csv_dataset( - file_pattern=str(input_data), - batch_size=model_params["batch_size"], - label_name=label_name, - num_epochs=model_params["epochs"], - shuffle=True, - shuffle_buffer_size=1000, - num_rows_for_inference=20000, - ) - return created_dataset.with_options(data_options) - - -def get_distribution_strategy(distribute_strategy: str) -> tf.distribute.Strategy: - """Set distribute strategy based on input string. - Args: - distribute_strategy (str): single, mirror or multi - Returns: - strategy (tf.distribute.Strategy): distribution strategy - """ - logging.info(f"Distribution strategy: {distribute_strategy}") - - # Single machine, single compute device - if distribute_strategy == "single": - if len(tf.config.list_physical_devices("GPU")): - strategy = tf.distribute.OneDeviceStrategy(device="/gpu:0") - else: - strategy = tf.distribute.OneDeviceStrategy(device="/cpu:0") - # Single machine, multiple compute device - elif distribute_strategy == "mirror": - strategy = tf.distribute.MirroredStrategy() - # Multiple machine, multiple compute device - elif distribute_strategy == "multi": - strategy = tf.distribute.MultiWorkerMirroredStrategy() - else: - raise RuntimeError(f"Distribute strategy: {distribute_strategy} not supported") - return strategy - - -def normalization(name: str, dataset: Dataset) -> Normalization: - """Create a Normalization layer for a feature. - Args: - name (str): name of feature to be normalized - dataset (Dataset): dataset to adapt layer - Returns: - normalization layer (Normalization): adapted normalization layer - of shape (?,1) - """ - logging.info(f"Normalizing numerical input '{name}'...") - x = Normalization(axis=None, name=f"normalize_{name}") - x.adapt(dataset.map(lambda y, _: y[name])) - return x - - -def str_lookup(name: str, dataset: Dataset, output_mode: str) -> StringLookup: - """Create a StringLookup layer for a feature. - Args: - name (str): name of feature to be encoded - dataset (Dataset): dataset to adapt layer - output_mode (str): argument for StringLookup layer (e.g. 'one_hot', 'int') - Returns: - StringLookup layer (StringLookup): adapted StringLookup layer of shape (?,X) - """ - logging.info(f"Encoding categorical input '{name}' ({output_mode})...") - x = StringLookup(output_mode=output_mode, name=f"str_lookup_{output_mode}_{name}") - x.adapt(dataset.map(lambda y, _: y[name])) - logging.info(f"Vocabulary: {x.get_vocabulary()}") - return x - - -def build_and_compile_model(dataset: Dataset, model_params: dict) -> Model: - """Build and compile model. - Args: - dataset (Dataset): training dataset - model_params (dict): model parameters - Returns: - model (Model): built and compiled model - """ - - # create inputs (scalars with shape `()`) - num_ins = {name: Input(shape=(), name=name, dtype=tf.float32) for name in NUM_COLS} - ord_ins = {name: Input(shape=(), name=name, dtype=tf.string) for name in ORD_COLS} - cat_ins = {name: Input(shape=(), name=name, dtype=tf.string) for name in OHE_COLS} - - # join all inputs and expand by 1 dimension. NOTE: this is useful when passing - # in scalar inputs to a model in Vertex AI batch predictions or endpoints e.g. - # `{"instances": {"input1": 1.0, "input2": "str"}}` instead of - # `{"instances": {"input1": [1.0], "input2": ["str"]}` - all_ins = {**num_ins, **ord_ins, **cat_ins} - exp_ins = {n: tf.expand_dims(i, axis=-1) for n, i in all_ins.items()} - - # preprocess expanded inputs - num_encoded = [normalization(n, dataset)(exp_ins[n]) for n in NUM_COLS] - ord_encoded = [str_lookup(n, dataset, "int")(exp_ins[n]) for n in ORD_COLS] - ohe_encoded = [str_lookup(n, dataset, "one_hot")(exp_ins[n]) for n in OHE_COLS] - - # ensure ordinal encoded layers is of type float32 (like the other layers) - ord_encoded = [tf.cast(x, tf.float32) for x in ord_encoded] - - # concat encoded inputs and add dense layers including output layer - x = num_encoded + ord_encoded + ohe_encoded - x = Concatenate()(x) - for units, activation in model_params["hidden_units"]: - x = Dense(units, activation=activation)(x) - x = Dense(1, name="output", activation="linear")(x) - - model = Model(inputs=all_ins, outputs=x, name="nn_model") - model.summary() - - logging.info(f"Use optimizer {model_params['optimizer']}") - optimizer = optimizers.get(model_params["optimizer"]) - optimizer.learning_rate = model_params["learning_rate"] - - model.compile( - loss=model_params["loss_fn"], - optimizer=optimizer, - metrics=model_params["metrics"], - ) - - return model - - -def _is_chief(strategy: tf.distribute.Strategy) -> bool: - """Determine whether current worker is the chief (master). See more info: - - https://www.tensorflow.org/tutorials/distribute/multi_worker_with_keras - - https://www.tensorflow.org/api_docs/python/tf/distribute/cluster_resolver/ClusterResolver # noqa: E501 - Args: - strategy (tf.distribute.Strategy): strategy - Returns: - is_chief (bool): True if worker is chief, otherwise False - """ - cr = strategy.cluster_resolver - return (cr is None) or (cr.task_type == "chief" and cr.task_id == 0) - - -def _get_temp_dir(dirpath, task_id): - base_dirpath = "workertemp_" + str(task_id) - temp_dir = os.path.join(dirpath, base_dirpath) - tf.io.gfile.makedirs(temp_dir) - return temp_dir - - -parser = argparse.ArgumentParser() -parser.add_argument("--train_data", type=str, required=True) -parser.add_argument("--valid_data", type=str, required=True) -parser.add_argument("--test_data", type=str, required=True) -parser.add_argument("--model", default=os.getenv("AIP_MODEL_DIR"), type=str, help="") -parser.add_argument("--metrics", type=str, required=True) -parser.add_argument("--hparams", default={}, type=json.loads) -args = parser.parse_args() - -if args.model.startswith("gs://"): - args.model = Path("/gcs/" + args.model[5:]) - -# merge dictionaries by overwriting default_model_params if provided in model_params -hparams = {**DEFAULT_HPARAMS, **args.hparams} -logging.info(f"Using model hyper-parameters: {hparams}") -label = hparams["label"] - -# Set distribute strategy before any TF operations -strategy = get_distribution_strategy(hparams["distribute_strategy"]) - -train_ds = create_dataset(Path(args.train_data), label, hparams) -valid_ds = create_dataset(Path(args.valid_data), label, hparams) -test_ds = create_dataset(Path(args.test_data), label, hparams) - -train_features = list(train_ds.element_spec[0].keys()) -valid_features = list(valid_ds.element_spec[0].keys()) -logging.info(f"Training feature names: {train_features}") -logging.info(f"Validation feature names: {valid_features}") - -if len(train_features) != len(valid_features): - raise RuntimeError(f"No. of training features != # validation features") - -with strategy.scope(): - tf_model = build_and_compile_model(train_ds, hparams) - -logging.info("Use early stopping") -callback = tf.keras.callbacks.EarlyStopping( - monitor="loss", mode="min", patience=hparams["early_stopping_epochs"] -) - -logging.info("Fit model...") -history = tf_model.fit( - train_ds, - batch_size=hparams["batch_size"], - epochs=hparams["epochs"], - validation_data=valid_ds, - callbacks=[callback], -) - -# only persist output files if current worker is chief -if not _is_chief(strategy): - logging.info("not chief node, exiting now") - sys.exit() - -logging.info(f"Save model to: {args.model}") -args.model.mkdir(parents=True) -tf_model.save(str(args.model), save_format="tf") - -logging.info(f"Save metrics to: {args.metrics}") -eval_metrics = dict(zip(tf_model.metrics_names, tf_model.evaluate(test_ds))) - -metrics = { - "problemType": "regression", - "rootMeanSquaredError": eval_metrics["root_mean_squared_error"], - "meanAbsoluteError": eval_metrics["mean_absolute_error"], - "meanAbsolutePercentageError": eval_metrics["mean_absolute_percentage_error"], - "rSquared": None, - "rootMeanSquaredLogError": eval_metrics["mean_squared_logarithmic_error"], -} - -with open(args.metrics, "w") as fp: - json.dump(metrics, fp) - -# Persist URIs of training file(s) for model monitoring in batch predictions -# See https://cloud.google.com/python/docs/reference/aiplatform/latest/google.cloud.aiplatform_v1beta1.types.ModelMonitoringObjectiveConfig.TrainingDataset # noqa: E501 -# for the expected schema. -path = args.model / TRAINING_DATASET_INFO -training_dataset_for_monitoring = { - "gcsSource": {"uris": [args.train_data]}, - "dataFormat": "csv", - "targetField": label, -} -logging.info(f"Save training dataset info for model monitoring: {path}") -logging.info(f"Training dataset: {training_dataset_for_monitoring}") - -with open(path, "w") as fp: - json.dump(training_dataset_for_monitoring, fp) diff --git a/pipelines/assets/train_xgb_model.py b/pipelines/assets/train_xgb_model.py deleted file mode 100644 index 71cc65b5..00000000 --- a/pipelines/assets/train_xgb_model.py +++ /dev/null @@ -1,142 +0,0 @@ -import argparse -from pathlib import Path - -import joblib -import json -import os -import logging - -import numpy as np -import pandas as pd -from sklearn import metrics -from sklearn.compose import ColumnTransformer -from sklearn.pipeline import Pipeline -from sklearn.preprocessing import StandardScaler, OrdinalEncoder, OneHotEncoder -from xgboost import XGBRegressor - -logging.basicConfig(level=logging.DEBUG) - -# used for monitoring during prediction time -TRAINING_DATASET_INFO = "training_dataset.json" -# numeric/categorical features in Chicago trips dataset to be preprocessed -NUM_COLS = ["dayofweek", "hourofday", "trip_distance", "trip_miles", "trip_seconds"] -ORD_COLS = ["company"] -OHE_COLS = ["payment_type"] - - -def split_xy(df: pd.DataFrame, label: str) -> (pd.DataFrame, pd.Series): - """Split dataframe into X and y.""" - return df.drop(columns=[label]), df[label] - - -def indices_in_list(elements: list, base_list: list) -> list: - """Get indices of specific elements in a base list""" - return [idx for idx, elem in enumerate(base_list) if elem in elements] - - -parser = argparse.ArgumentParser() -parser.add_argument("--train_data", type=str, required=True) -parser.add_argument("--valid_data", type=str, required=True) -parser.add_argument("--test_data", type=str, required=True) -parser.add_argument("--model", default=os.getenv("AIP_MODEL_DIR"), type=str, help="") -parser.add_argument("--metrics", type=str, required=True) -parser.add_argument("--hparams", default={}, type=json.loads) -args = parser.parse_args() - -if args.model.startswith("gs://"): - args.model = Path("/gcs/" + args.model[5:]) - -logging.info("Read csv files into dataframes") -df_train = pd.read_csv(args.train_data) -df_valid = pd.read_csv(args.valid_data) -df_test = pd.read_csv(args.test_data) - -logging.info("Split dataframes") -label = args.hparams["label"] -X_train, y_train = split_xy(df_train, label) -X_valid, y_valid = split_xy(df_valid, label) -X_test, y_test = split_xy(df_test, label) - -logging.info("Get the number of unique categories for ordinal encoded columns") -ordinal_columns = X_train[ORD_COLS] -n_unique_cat = ordinal_columns.nunique() - -logging.info("Get indices of columns in base data") -col_list = X_train.columns.tolist() -num_indices = indices_in_list(NUM_COLS, col_list) -cat_indices_onehot = indices_in_list(OHE_COLS, col_list) -cat_indices_ordinal = indices_in_list(ORD_COLS, col_list) - -ordinal_transformers = [ - ( - f"ordinal encoding for {ord_col}", - OrdinalEncoder( - handle_unknown="use_encoded_value", unknown_value=n_unique_cat[ord_col] - ), - [ord_index], - ) - for ord_col in ORD_COLS - for ord_index in cat_indices_ordinal -] -all_transformers = [ - ("numeric_scaling", StandardScaler(), num_indices), - ( - "one_hot_encoding", - OneHotEncoder(handle_unknown="ignore"), - cat_indices_onehot, - ), -] + ordinal_transformers - -logging.info("Build sklearn preprocessing steps") -preprocesser = ColumnTransformer(transformers=all_transformers) -logging.info("Build sklearn pipeline with XGBoost model") -xgb_model = XGBRegressor(**args.hparams) - -pipeline = Pipeline( - steps=[("feature_engineering", preprocesser), ("train_model", xgb_model)] -) - -logging.info("Transform validation data") -valid_preprocesser = preprocesser.fit(X_train) -X_valid_transformed = valid_preprocesser.transform(X_valid) - -logging.info("Fit model") -pipeline.fit(X_train, y_train, train_model__eval_set=[(X_valid_transformed, y_valid)]) - -logging.info("Predict test data") -y_pred = pipeline.predict(X_test) -y_pred = y_pred.clip(0) - -metrics = { - "problemType": "regression", - "rootMeanSquaredError": np.sqrt(metrics.mean_squared_error(y_test, y_pred)), - "meanAbsoluteError": metrics.mean_absolute_error(y_test, y_pred), - "meanAbsolutePercentageError": metrics.mean_absolute_percentage_error( - y_test, y_pred - ), - "rSquared": metrics.r2_score(y_test, y_pred), - "rootMeanSquaredLogError": np.sqrt(metrics.mean_squared_log_error(y_test, y_pred)), -} - -logging.info(f"Save model to: {args.model}") -args.model.mkdir(parents=True) -joblib.dump(pipeline, str(args.model / "model.joblib")) - -logging.info(f"Metrics: {metrics}") -with open(args.metrics, "w") as fp: - json.dump(metrics, fp) - -# Persist URIs of training file(s) for model monitoring in batch predictions -# See https://cloud.google.com/python/docs/reference/aiplatform/latest/google.cloud.aiplatform_v1beta1.types.ModelMonitoringObjectiveConfig.TrainingDataset # noqa: E501 -# for the expected schema. -path = args.model / TRAINING_DATASET_INFO -training_dataset_for_monitoring = { - "gcsSource": {"uris": [args.train_data]}, - "dataFormat": "csv", - "targetField": label, -} -logging.info(f"Training dataset info: {training_dataset_for_monitoring}") - -with open(path, "w") as fp: - logging.info(f"Save training dataset info for model monitoring: {path}") - json.dump(training_dataset_for_monitoring, fp) diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py index 20deca08..03f5e18f 100644 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py @@ -16,14 +16,14 @@ import pathlib from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp -from kfp import compiler, dsl +from kfp import dsl from pipelines import generate_query from vertex_components import lookup_model, model_batch_predict @dsl.pipeline(name="tensorflow-prediction-pipeline") -def tensorflow_pipeline( +def pipeline( project_id: str = os.environ.get("VERTEX_PROJECT_ID"), project_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_project_id: str = os.environ.get("VERTEX_PROJECT_ID"), @@ -149,11 +149,3 @@ def tensorflow_pipeline( .after(preprocessing) .set_display_name("Batch prediction job") ) - - -if __name__ == "__main__": - compiler.Compiler().compile( - pipeline_func=tensorflow_pipeline, - package_path="prediction.json", - type_check=False, - ) diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py index 13ccb817..67f267b1 100644 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ b/pipelines/src/pipelines/tensorflow/training/pipeline.py @@ -16,7 +16,7 @@ import pathlib from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp -from kfp import compiler, dsl +from kfp import dsl from pipelines import generate_query from bigquery_components import extract_bq_to_dataset from vertex_components import ( @@ -28,7 +28,7 @@ @dsl.pipeline(name="tensorflow-train-pipeline") -def tensorflow_pipeline( +def pipeline( project_id: str = os.environ.get("VERTEX_PROJECT_ID"), project_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_project_id: str = os.environ.get("VERTEX_PROJECT_ID"), @@ -38,7 +38,6 @@ def tensorflow_pipeline( ingestion_dataset_id: str = "chicago_taxi_trips", timestamp: str = "2022-12-01 00:00:00", staging_bucket: str = os.environ.get("VERTEX_PIPELINE_ROOT"), - pipeline_files_gcs_path: str = os.environ.get("PIPELINE_FILES_GCS_PATH"), resource_suffix: str = os.environ.get("RESOURCE_SUFFIX"), test_dataset_uri: str = "", ): @@ -64,7 +63,6 @@ def tensorflow_pipeline( (YYYY-MM-DDThh:mm:ss.sss±hh:mm or YYYY-MM-DDThh:mm:ss). If any time part is missing, it will be regarded as zero. staging_bucket (str): Staging bucket for pipeline artifacts. - pipeline_files_gcs_path (str): GCS path where the pipeline files are located resource_suffix (str): Optional. Additional suffix to append GCS resources that get overwritten. test_dataset_uri (str): Optional. GCS URI of statis held-out test dataset. @@ -82,6 +80,7 @@ def tensorflow_pipeline( valid_table = "valid_data" + table_suffix test_table = "test_data" + table_suffix primary_metric = "rootMeanSquaredError" + pipeline_files_gcs_path = "TODO - remove" train_script_uri = f"{pipeline_files_gcs_path}/assets/train_tf_model.py" hparams = dict( batch_size=100, @@ -203,11 +202,3 @@ def tensorflow_pipeline( project_id=project_id, project_location=project_location, ).set_display_name("Update best model") - - -if __name__ == "__main__": - compiler.Compiler().compile( - pipeline_func=tensorflow_pipeline, - package_path="training.json", - type_check=False, - ) diff --git a/pipelines/src/pipelines/utils/upload_pipeline.py b/pipelines/src/pipelines/utils/upload_pipeline.py new file mode 100644 index 00000000..d5137f41 --- /dev/null +++ b/pipelines/src/pipelines/utils/upload_pipeline.py @@ -0,0 +1,19 @@ +import argparse +from kfp.registry import RegistryClient + +if __name__ == "__main__": + + parser = argparse.ArgumentParser() + parser.add_argument("--dest", type=str, required=True) + parser.add_argument("--yaml", type=str, required=True) + parser.add_argument("--tag", type=str, action="append") + args = parser.parse_args() + + client = RegistryClient( + host=args.dest, + ) + + templateName, versionName = client.upload_pipeline( + file_name=args.yaml, + tags=args.tag, + ) diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/xgboost/prediction/pipeline.py index c7225e10..6d618511 100644 --- a/pipelines/src/pipelines/xgboost/prediction/pipeline.py +++ b/pipelines/src/pipelines/xgboost/prediction/pipeline.py @@ -16,14 +16,14 @@ import pathlib from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp -from kfp import compiler, dsl +from kfp import dsl from pipelines import generate_query from vertex_components import lookup_model, model_batch_predict @dsl.pipeline(name="xgboost-prediction-pipeline") -def xgboost_pipeline( +def pipeline( project_id: str = os.environ.get("VERTEX_PROJECT_ID"), project_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_project_id: str = os.environ.get("VERTEX_PROJECT_ID"), @@ -141,11 +141,3 @@ def xgboost_pipeline( .after(preprocessing) .set_display_name("Batch prediction job") ) - - -if __name__ == "__main__": - compiler.Compiler().compile( - pipeline_func=xgboost_pipeline, - package_path="prediction.json", - type_check=False, - ) diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/xgboost/training/pipeline.py index 228d7afa..93c83228 100644 --- a/pipelines/src/pipelines/xgboost/training/pipeline.py +++ b/pipelines/src/pipelines/xgboost/training/pipeline.py @@ -16,7 +16,7 @@ import pathlib from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp -from kfp import compiler, dsl +from kfp import dsl from kfp.dsl import Dataset, Input, Metrics, Model, Output, OutputPath from pipelines import generate_query from bigquery_components import extract_bq_to_dataset @@ -32,7 +32,7 @@ def train( test_data: Input[Dataset], model: Output[Model], model_output_uri: OutputPath(str), - metrics: Output[Metrics], # TODO could be a more specific type of metrics object + metrics: Output[Metrics], hparams: dict, ): return dsl.ContainerSpec( @@ -59,7 +59,7 @@ def train( @dsl.pipeline(name="xgboost-train-pipeline") -def xgboost_pipeline( +def pipeline( project_id: str = os.environ.get("VERTEX_PROJECT_ID"), project_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_project_id: str = os.environ.get("VERTEX_PROJECT_ID"), @@ -68,8 +68,6 @@ def xgboost_pipeline( dataset_location: str = os.environ.get("VERTEX_LOCATION"), ingestion_dataset_id: str = "chicago_taxi_trips", timestamp: str = "2022-12-01 00:00:00", - staging_bucket: str = os.environ.get("VERTEX_PIPELINE_ROOT"), - pipeline_files_gcs_path: str = os.environ.get("PIPELINE_FILES_GCS_PATH"), resource_suffix: str = os.environ.get("RESOURCE_SUFFIX"), test_dataset_uri: str = "", ): @@ -93,8 +91,6 @@ def xgboost_pipeline( timestamp (str): Optional. Empty or a specific timestamp in ISO 8601 format (YYYY-MM-DDThh:mm:ss.sss±hh:mm or YYYY-MM-DDThh:mm:ss). If any time part is missing, it will be regarded as zero. - staging_bucket (str): Staging bucket for pipeline artifacts. - pipeline_files_gcs_path (str): GCS path where the pipeline files are located. resource_suffix (str): Optional. Additional suffix to append GCS resources that get overwritten. test_dataset_uri (str): Optional. GCS URI of statis held-out test dataset. @@ -208,11 +204,3 @@ def xgboost_pipeline( pipeline_job_id="{{$.pipeline_job_name}}", test_dataset=test_dataset, ).set_display_name("Upload model") - - -if __name__ == "__main__": - compiler.Compiler().compile( - pipeline_func=xgboost_pipeline, - package_path="training.json", - type_check=False, - ) diff --git a/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example b/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example index a92fd2df..beb66051 100644 --- a/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example +++ b/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example @@ -21,17 +21,14 @@ cloud_schedulers_config = { description = "Trigger my XGBoost training pipeline in Vertex" schedule = "0 0 * * 0" time_zone = "UTC" - template_path = "gs://my-assets-bucket//training/training.json" + template_path = "https://-kfp.pkg.dev//vertex-pipelines/xgboost-train-pipeline/" enable_caching = null pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west2" - pipeline_files_gcs_path = "gs://my-assets-bucket/" ingestion_project_id = "my-project-id" model_name = "xgboost_with_preprocessing" model_label = "label_name" - tfdv_schema_filename = "tfdv_schema_training.pbtxt" - tfdv_train_stats_path = "gs://my-pipeline-root-bucket/train_stats/train.stats" dataset_id = "preprocessing" dataset_location = "europe-west2" ingestion_dataset_id = "chicago_taxi_trips" @@ -44,17 +41,14 @@ cloud_schedulers_config = { description = "Trigger my XGBoost prediction pipeline in Vertex" schedule = "0 0 * * 0" time_zone = "UTC" - template_path = "gs://my-assets-bucket//prediction/prediction.json" + template_path = "https://-kfp.pkg.dev//vertex-pipelines/xgboost-prediction-pipeline/" enable_caching = null pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west4" - pipeline_files_gcs_path = "gs://my-assets-bucket/" ingestion_project_id = "my-project-id" model_name = "xgboost_with_preprocessing" model_label = "label_name" - tfdv_schema_filename = "tfdv_schema_training.pbtxt" - tfdv_train_stats_path = "gs://my-pipeline-root-bucket/train_stats/train.stats" dataset_id = "preprocessing" dataset_location = "europe-west2" ingestion_dataset_id = "chicago_taxi_trips" @@ -70,17 +64,14 @@ cloud_schedulers_config = { description = "Trigger my TensorFlow training pipeline in Vertex" schedule = "0 0 * * 0" time_zone = "UTC" - template_path = "gs://my-assets-bucket//training/training.json" + template_path = "https://-kfp.pkg.dev//vertex-pipelines/tensorflow-train-pipeline/" enable_caching = null pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west4" - pipeline_files_gcs_path = "gs://my-assets-bucket/" ingestion_project_id = "my-project-id" model_name = "tensorflow_with_preprocessing" model_label = "label_name" - tfdv_schema_filename = "tfdv_schema_training.pbtxt" - tfdv_train_stats_path = "gs://my-pipeline-root-bucket/train_stats/train.stats" dataset_id = "preprocessing" dataset_location = "europe-west2" ingestion_dataset_id = "chicago_taxi_trips" @@ -93,17 +84,14 @@ cloud_schedulers_config = { description = "Trigger my TensorFlow prediction pipeline in Vertex" schedule = "0 0 * * 0" time_zone = "UTC" - template_path = "gs://my-assets-bucket//prediction/prediction.json" + template_path = "https://-kfp.pkg.dev//vertex-pipelines/tensorflow-prediction-pipeline/" enable_caching = null pipeline_parameters = { project_id = "my-project-id" project_location = "europe-west4" - pipeline_files_gcs_path = "gs://my-assets-bucket/" ingestion_project_id = "my-project-id" model_name = "tensorflow_with_preprocessing" model_label = "label_name" - tfdv_schema_filename = "tfdv_schema_training.pbtxt" - tfdv_train_stats_path = "gs://my-pipeline-root-bucket/train_stats/train.stats" dataset_id = "preprocessing" dataset_location = "europe-west2" ingestion_dataset_id = "chicago_taxi_trips" diff --git a/terraform/modules/vertex_deployment/iam.tf b/terraform/modules/vertex_deployment/iam.tf index 8cacdcbe..2d58475a 100644 --- a/terraform/modules/vertex_deployment/iam.tf +++ b/terraform/modules/vertex_deployment/iam.tf @@ -26,28 +26,6 @@ resource "google_storage_bucket_iam_member" "pipelines_sa_pipeline_root_bucket_i role = each.key } -# Give pipelines SA read-only access to objects in "assets" bucket -resource "google_storage_bucket_iam_member" "pipelines_sa_pipeline_assets_bucket_iam" { - for_each = toset([ - "roles/storage.objectViewer", - "roles/storage.legacyBucketReader", - ]) - bucket = google_storage_bucket.pipeline_assets_bucket.name - member = google_service_account.pipelines_sa.member - role = each.key -} - -# Give cloud functions SA access to read compiled pipelines from bucket -resource "google_storage_bucket_iam_member" "cloudfunction_sa_pipeline_assets_bucket_iam" { - for_each = toset([ - "roles/storage.objectViewer", - "roles/storage.legacyBucketReader", - ]) - bucket = google_storage_bucket.pipeline_assets_bucket.name - member = google_service_account.vertex_cloudfunction_sa.member - role = each.key -} - # Give cloud functions SA access to use the pipelines SA for triggering pipelines resource "google_service_account_iam_member" "cloudfunction_sa_can_use_pipelines_sa" { service_account_id = google_service_account.pipelines_sa.name diff --git a/terraform/modules/vertex_deployment/main.tf b/terraform/modules/vertex_deployment/main.tf index 335ca64f..73c6a4fa 100644 --- a/terraform/modules/vertex_deployment/main.tf +++ b/terraform/modules/vertex_deployment/main.tf @@ -50,15 +50,6 @@ resource "google_storage_bucket" "pipeline_root_bucket" { depends_on = [google_project_service.gcp_services] } -resource "google_storage_bucket" "pipeline_assets_bucket" { - name = "${var.project_id}-pl-assets" - location = var.region - project = var.project_id - uniform_bucket_level_access = true - public_access_prevention = "enforced" - depends_on = [google_project_service.gcp_services] -} - ## Cloud Function - used to trigger pipelines ## locals { @@ -123,7 +114,7 @@ resource "google_vertex_ai_metadata_store" "default_metadata_store" { depends_on = [google_project_service.gcp_services] } -## Artifact Registry ## +## Artifact Registry - container images ## resource "google_artifact_registry_repository" "vertex-images" { repository_id = "vertex-images" description = "Container image repository for training container images" @@ -132,3 +123,13 @@ resource "google_artifact_registry_repository" "vertex-images" { format = "DOCKER" depends_on = [google_project_service.gcp_services] } + +## Artifact Registry - KFP pipelines ## +resource "google_artifact_registry_repository" "vertex-pipelines" { + repository_id = "vertex-pipelines" + description = "KFP repository for Vertex Pipelines" + project = var.project_id + location = var.region + format = "KFP" + depends_on = [google_project_service.gcp_services] +} diff --git a/terraform/modules/vertex_deployment/outputs.tf b/terraform/modules/vertex_deployment/outputs.tf index 608e9bcc..e77014e6 100644 --- a/terraform/modules/vertex_deployment/outputs.tf +++ b/terraform/modules/vertex_deployment/outputs.tf @@ -26,10 +26,6 @@ output "pipeline_root_bucket_name" { value = google_storage_bucket.pipeline_root_bucket.name } -output "pipeline_assets_bucket_name" { - value = google_storage_bucket.pipeline_assets_bucket.name -} - output "vertex_pipelines_sa_email" { value = google_service_account.pipelines_sa.email } From 496cf159f48637d8fea91e61a314b8b04f3f7f43 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Wed, 26 Jul 2023 13:11:31 +0100 Subject: [PATCH 128/238] remove tensorflow pipelines --- Makefile | 6 +- cloudbuild/e2e-test.yaml | 1 - cloudbuild/pr-checks.yaml | 1 - cloudbuild/release.yaml | 7 +- .../tests/test_import_model_evaluation.py | 57 ----- .../tests/test_lookup_model.py | 96 --------- .../tests/test_update_best_model.py | 51 ----- env.sh.example | 3 - .../{xgboost => }/prediction/pipeline.py | 0 .../prediction/queries/preprocessing.sql | 0 pipelines/src/pipelines/tensorflow/README.md | 89 -------- .../tensorflow/prediction/pipeline.py | 151 ------------- .../prediction/queries/preprocessing.sql | 69 ------ .../pipelines/tensorflow/training/pipeline.py | 204 ------------------ .../training/queries/preprocessing.sql | 88 -------- .../{xgboost => }/training/pipeline.py | 0 .../training/queries/preprocessing.sql | 0 pipelines/src/pipelines/xgboost/README.md | 49 ----- .../{xgboost => }/prediction/test_e2e.py | 4 +- .../tests/tensorflow/prediction/test_e2e.py | 37 ---- .../tests/tensorflow/training/test_e2e.py | 37 ---- .../tests/{xgboost => }/training/test_e2e.py | 4 +- .../scheduled_jobs.auto.tfvars.example | 53 +---- 23 files changed, 14 insertions(+), 993 deletions(-) delete mode 100644 components/vertex-components/tests/test_import_model_evaluation.py delete mode 100644 components/vertex-components/tests/test_lookup_model.py delete mode 100644 components/vertex-components/tests/test_update_best_model.py rename pipelines/src/pipelines/{xgboost => }/prediction/pipeline.py (100%) rename pipelines/src/pipelines/{xgboost => }/prediction/queries/preprocessing.sql (100%) delete mode 100644 pipelines/src/pipelines/tensorflow/README.md delete mode 100644 pipelines/src/pipelines/tensorflow/prediction/pipeline.py delete mode 100644 pipelines/src/pipelines/tensorflow/prediction/queries/preprocessing.sql delete mode 100644 pipelines/src/pipelines/tensorflow/training/pipeline.py delete mode 100644 pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql rename pipelines/src/pipelines/{xgboost => }/training/pipeline.py (100%) rename pipelines/src/pipelines/{xgboost => }/training/queries/preprocessing.sql (100%) delete mode 100644 pipelines/src/pipelines/xgboost/README.md rename pipelines/tests/{xgboost => }/prediction/test_e2e.py (92%) delete mode 100644 pipelines/tests/tensorflow/prediction/test_e2e.py delete mode 100644 pipelines/tests/tensorflow/training/test_e2e.py rename pipelines/tests/{xgboost => }/training/test_e2e.py (92%) diff --git a/Makefile b/Makefile index 179d929c..f7ec8a3f 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ test-trigger: ## Runs unit tests for the pipeline trigger code compile-pipeline: ## Compile the pipeline to pipeline.yaml. Must specify pipeline= @cd pipelines/src && \ - poetry run kfp dsl compile --py pipelines/${PIPELINE_TEMPLATE}/${pipeline}/pipeline.py --output pipelines/${PIPELINE_TEMPLATE}/${pipeline}/pipeline.yaml --function pipeline + poetry run kfp dsl compile --py pipelines/${pipeline}/pipeline.py --output pipelines/${pipeline}/pipeline.yaml --function pipeline setup-components: ## Run unit tests for a component group @cd "components/${GROUP}" && \ @@ -74,11 +74,11 @@ test-all-components-coverage: ## Run tests with coverage run: ## Compile pipeline and run pipeline in sandbox environment. Must specify pipeline=. Optionally specify enable_pipeline_caching= (defaults to default Vertex caching behaviour) @ $(MAKE) compile-pipeline && \ cd pipelines/src && \ - poetry run python -m pipelines.trigger --template_path=pipelines/${PIPELINE_TEMPLATE}/${pipeline}/pipeline.yaml --enable_caching=$(enable_pipeline_caching) + poetry run python -m pipelines.trigger --template_path=pipelines/${pipeline}/pipeline.yaml --enable_caching=$(enable_pipeline_caching) e2e-tests: ## Perform end-to-end (E2E) pipeline tests. Must specify pipeline=. Optionally specify enable_pipeline_caching= (defaults to default Vertex caching behaviour). @ cd pipelines && \ - poetry run pytest --log-cli-level=INFO tests/${PIPELINE_TEMPLATE}/$(pipeline) --enable_caching=$(enable_pipeline_caching) + poetry run pytest --log-cli-level=INFO tests/$(pipeline) --enable_caching=$(enable_pipeline_caching) env ?= dev deploy-infra: ## Deploy the Terraform infrastructure to your project. Requires VERTEX_PROJECT_ID and VERTEX_LOCATION env variables to be set in env.sh. Optionally specify env= (default = dev) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 2a9af102..7c6494ee 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -30,7 +30,6 @@ steps: - VERTEX_SA_EMAIL=${_TEST_VERTEX_SA_EMAIL} - VERTEX_CMEK_IDENTIFIER=${_TEST_VERTEX_CMEK_IDENTIFIER} - VERTEX_NETWORK=${_TEST_VERTEX_NETWORK} - - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - RESOURCE_SUFFIX=${COMMIT_SHA} diff --git a/cloudbuild/pr-checks.yaml b/cloudbuild/pr-checks.yaml index 969e7bbf..93934970 100644 --- a/cloudbuild/pr-checks.yaml +++ b/cloudbuild/pr-checks.yaml @@ -32,7 +32,6 @@ steps: make test-all-components env: - SKIP=terraform-fmt,git-dirty - - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} options: logging: CLOUD_LOGGING_ONLY diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index f2ac361c..5852e0bd 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -27,19 +27,16 @@ steps: for dest in ${_PIPELINE_PUBLISH_AR_PATHS} ; do \ poetry run python -m pipelines.utils.upload_pipeline \ --dest=$$dest \ - --yaml=src/pipelines/${_PIPELINE_TEMPLATE}/training/pipeline.yaml \ + --yaml=src/pipelines/training/pipeline.yaml \ --tag=latest \ --tag=${TAG_NAME} && \ poetry run python -m pipelines.utils.upload_pipeline \ --dest=$$dest \ - --yaml=src/pipelines/${_PIPELINE_TEMPLATE}/prediction/pipeline.yaml \ + --yaml=src/pipelines/prediction/pipeline.yaml \ --tag=latest \ --tag=${TAG_NAME}; \ done - env: - - PIPELINE_TEMPLATE=${_PIPELINE_TEMPLATE} - options: logging: CLOUD_LOGGING_ONLY diff --git a/components/vertex-components/tests/test_import_model_evaluation.py b/components/vertex-components/tests/test_import_model_evaluation.py deleted file mode 100644 index cfe8c5ab..00000000 --- a/components/vertex-components/tests/test_import_model_evaluation.py +++ /dev/null @@ -1,57 +0,0 @@ -from unittest import mock -from kfp.dsl import Model, Metrics, Dataset - -import vertex_components -from google.cloud.aiplatform_v1 import ModelEvaluation - - -import_model_evaluation = vertex_components.import_model_evaluation.python_func - - -@mock.patch("google.cloud.aiplatform_v1.ModelServiceClient") -@mock.patch( - "builtins.open", - new_callable=mock.mock_open, - read_data='{"accuracy": 0.85, "problemType": "classification"}', -) -@mock.patch("google.protobuf.json_format.ParseDict") -def test_import_model_evaluation( - mock_parse_dict, mock_open, mock_service_client, tmpdir -): - """ - Checks that when the model evaluation is running and it is writing the metrics - """ - mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) - mock_metrics = Metrics(uri=tmpdir) - mock_dataset = Dataset(uri=tmpdir) - - # Create an instance of the mocked ModelServiceClient. - service_client_instance = mock.MagicMock() - mock_service_client.return_value = service_client_instance - # When import_model_evaluation is called during the test, - # it will return a new ModelEvaluation with the specified name. - service_client_instance.import_model_evaluation.return_value = ModelEvaluation( - name="model_evaluation_name" - ) - - # Set the return value for ParseDict to be a mock ModelEvaluation - mock_parse_dict.return_value = mock.MagicMock(spec=ModelEvaluation) - - model_evaluation_name = import_model_evaluation( - model=mock_model, - metrics=mock_metrics, - test_dataset=mock_dataset, - pipeline_job_id="1234", - project_location="my-location", - evaluation_name="Imported evaluation", - ) - - service_client_instance.import_model_evaluation.assert_called_once_with( - parent=mock_model.metadata["resourceName"], - model_evaluation=mock_parse_dict.return_value, - ) - - # Check that open was called with the correct path - mock_open.assert_called_once_with(mock_metrics.uri) - - assert model_evaluation_name[0] == "model_evaluation_name" diff --git a/components/vertex-components/tests/test_lookup_model.py b/components/vertex-components/tests/test_lookup_model.py deleted file mode 100644 index a869f7cd..00000000 --- a/components/vertex-components/tests/test_lookup_model.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import google.cloud.aiplatform # noqa -from kfp.dsl import Model -from unittest import mock -import pytest - -import vertex_components - -lookup_model = vertex_components.lookup_model.python_func - - -@mock.patch("google.cloud.aiplatform.Model") -def test_lookup_model(mock_model, tmpdir): - """ - Assert lookup_model produces expected resource name, and that list method is - called with the correct arguemnts - """ - - # Mock attribute and method - mock_path = tmpdir - mock_model.resource_name = "my-model-resource-name" - mock_model.uri = mock_path - mock_model.list.return_value = [mock_model] - - # Invoke the model look up - found_model_resource_name, _ = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - model=Model(uri=mock_path), - ) - - assert found_model_resource_name == "my-model-resource-name" - - # Check the list method was called once with the correct arguments - mock_model.list.assert_called_once_with( - filter='display_name="my-model"', - order_by="create_time desc", - location="europe-west4", - project="my-project-id", - ) - - -@mock.patch("google.cloud.aiplatform.Model") -def test_lookup_model_when_no_models(mock_model, tmpdir): - """ - Checks that when there are no models and fail_on_model_found = False, - lookup_model returns an empty string. - """ - mock_model.list.return_value = [] - exported_model_resource_name, _ = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - model=Model(uri=str(tmpdir)), - ) - - print(exported_model_resource_name) - assert exported_model_resource_name == "" - - -@mock.patch("google.cloud.aiplatform.Model") -def test_lookup_model_when_no_models_fail(mock_model, tmpdir): - """ - Checks that when there are no models and fail_on_model_found = True, - lookup_model raises a RuntimeError. - """ - mock_model.list.return_value = [] - - # Verify that a ValueError is raised - with pytest.raises(RuntimeError): - lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=True, - model=Model(uri=str(tmpdir)), - ) diff --git a/components/vertex-components/tests/test_update_best_model.py b/components/vertex-components/tests/test_update_best_model.py deleted file mode 100644 index 481cd605..00000000 --- a/components/vertex-components/tests/test_update_best_model.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from unittest import mock - -from kfp.dsl import Model - - -import vertex_components - -update_best_model = vertex_components.update_best_model.python_func - - -@mock.patch("google.cloud.aiplatform.Model") -@mock.patch("google.cloud.aiplatform.model_evaluation.ModelEvaluation") -@mock.patch("google.cloud.aiplatform.models.ModelRegistry") -@mock.patch("google.protobuf.json_format.MessageToDict") -def test_model_batch_predict( - mock_message_to_dict, - mock_model_registry, - mock_model_evaluation, - mock_model_class, - tmpdir, -): - """ - Asserts model_batch_predict successfully creates requests given different arguments. - """ - mock_model = Model(uri=tmpdir, metadata={"resourceName": ""}) - mock_message = {"metrics": {"rmse": 0.01}} - mock_message_to_dict.return_value = mock_message - - (challenger_wins,) = update_best_model( - challenger=mock_model, - challenger_evaluation="", - parent_model="", - project_id="", - project_location="", - eval_metric="rmse", - eval_lower_is_better=True, - ) - assert not challenger_wins diff --git a/env.sh.example b/env.sh.example index 3a84eb80..528c3ae7 100644 --- a/env.sh.example +++ b/env.sh.example @@ -13,9 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# pipeline template - update to any pipelines under the pipelines folder -# tensorflow or xgboost -export PIPELINE_TEMPLATE=tensorflow export VERTEX_CMEK_IDENTIFIER= # optional export VERTEX_LOCATION=europe-west2 export VERTEX_NETWORK= # optional diff --git a/pipelines/src/pipelines/xgboost/prediction/pipeline.py b/pipelines/src/pipelines/prediction/pipeline.py similarity index 100% rename from pipelines/src/pipelines/xgboost/prediction/pipeline.py rename to pipelines/src/pipelines/prediction/pipeline.py diff --git a/pipelines/src/pipelines/xgboost/prediction/queries/preprocessing.sql b/pipelines/src/pipelines/prediction/queries/preprocessing.sql similarity index 100% rename from pipelines/src/pipelines/xgboost/prediction/queries/preprocessing.sql rename to pipelines/src/pipelines/prediction/queries/preprocessing.sql diff --git a/pipelines/src/pipelines/tensorflow/README.md b/pipelines/src/pipelines/tensorflow/README.md deleted file mode 100644 index a5a6bfec..00000000 --- a/pipelines/src/pipelines/tensorflow/README.md +++ /dev/null @@ -1,89 +0,0 @@ - -# TensorFlow Pipelines - -## Training pipeline -The TensorFlow training pipeline can be found in [`training/pipeline.py`](training/pipeline.py). The training component [`train_tensorflow_model`](../../../pipeline_components/_tensorflow/_tensorflow/train/component.py) is the main training component which contains the implementation of a TensorFlow Keras model. -This component can then be wrapped in a custom kfp ContainerOp from [`google-cloud-pipeline-components`](https://github.com/kubeflow/pipelines/blob/master/components/google-cloud/google_cloud_pipeline_components/experimental/custom_job/utils.py) which submits a Vertex Training job with added flexibility for `machine_type`, `replica_count`, `accelerator_type` among other machine configurations. - -### Data -The input data is split into three parts in BigQuery and stored in Google Cloud Storage: -- 80% of the input data is used for model training -- 10% of the input data is used for model validation -- 10% of the input data is used for model testing/evaluation - -### Model Architecture -The architecture of the example TensorFlow Keras model is shown below: - -![TensorFlow Model Architecture](../../docs/images/tf_model_architecture.png) - -- **Input layer**: there is one input node for each of the 7 features used in the example: - - `dayofweek` - - `hourofday` - - `trip_distance` - - `trip_miles` - - `trip_seconds` - - `payment_type` - - `company` -- **Pre-processing layers** - - Categorical encoding for categorical features is done using Tensorflow's `StringLookup` layer. New/unknown values are handled using this layer's default parameters. (https://www.tensorflow.org/api_docs/python/tf/keras/layers/StringLookup). - - The feature `payment_type` is one-hot encoded. New/unknown categories are assigned to a one-hot encoded array with zeroes everywhere. - - The feature `company` is ordinal encoded. New/unknown categories are assigned to zero. - - Normalization for the numerical features (`dayofweek`, `hourofday`, `trip_distance`, `trip_miles`, `trip_seconds`) -- **Dense layers** - - One `Dense` layer with 64 units whose activation function is ReLU. - - One `Dense` layer with 32 units whose activation function is ReLU. -- **Output layer** - - One `Dense` layer with 1 unit where no activation is applied (this is because the example is a regression problem) - - -### Model hyperparameters -You can specify different hyperparameters through the `model_params` argument of `train_tensorflow_model`, including: -- Batch size -- No. of epochs to check for early stopping -- Learning rate -- Number of hidden units and type of activation function in each layer -- Loss function -- Optimization method -- Evaluation metrics -- Whether you want early stopping - -For a comprehensive list of options for the above hyperparameters, see the docstring in [`train.py`](../../../pipeline_components/_tensorflow/_tensorflow/train/component.py). - -### Model artifacts -A number of different model artifacts/objects are created by the training of the TensorFlow model. With these files, you can load the model into a new script (without any of the original training code) and run it or resume training from exactly where you left off. For more information, see [this](https://www.tensorflow.org/api_docs/python/tf/keras/models/save_model). - - -![tensorflow_component_model&metrics_artifact](../../docs/images/tensorflow_component_model&metrics_artifact.png) -### Model test/evaluation -Once the model is trained, it will be used to get challenger predictions for evaluation purposes. In general, the component [`predict_tensorflow_model`](../kfp_components/tensorflow/predict.py) -which expects a single CSV file to create predictions for test data is implemented in the pipeline, However, if you are working working with larger test data, it is more efficient to -replace it with a prebuilt component provided by Google, [`ModelBatchPredictOp`](https://google-cloud-pipeline-components.readthedocs.io/en/google-cloud-pipeline-components-0.2.1/google_cloud_pipeline_components.aiplatform.html), -to avoid crash caused by insufficent memory usage. - -### Distribution strategy -In deep learning, it is common to use GPUs, which utilise a large number of simple cores allowing parallel computing though thousands of threads at a time, to train complicated neural networks fed by massive datasets. -For optimisation tasks, it is often better to use CPUs. - - There is a variable, `distribute_strategy`, in tensorflow training pipeline that allows you to set up distribution strategy. You have three options: -|Value| description | -|---|---| -|`single` | This strategy use GPU is a GPU device of the requested kind is available, otherwise, it uses CPU | -|`mirror` | This strategy is typically used for training on one machine with multiple GPUs. | -|`multi`|This strategy implements synchronous distributed training across multiple machines, each with potentially multiple GPUs| - -## Prediction pipeline -The TensorFlow prediction pipeline can be found in [prediction/pipeline.py](prediction/pipeline.py). diff --git a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py b/pipelines/src/pipelines/tensorflow/prediction/pipeline.py deleted file mode 100644 index 03f5e18f..00000000 --- a/pipelines/src/pipelines/tensorflow/prediction/pipeline.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import pathlib - -from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp -from kfp import dsl - -from pipelines import generate_query -from vertex_components import lookup_model, model_batch_predict - - -@dsl.pipeline(name="tensorflow-prediction-pipeline") -def pipeline( - project_id: str = os.environ.get("VERTEX_PROJECT_ID"), - project_location: str = os.environ.get("VERTEX_LOCATION"), - ingestion_project_id: str = os.environ.get("VERTEX_PROJECT_ID"), - model_name: str = "simple_tensorflow", - preprocessing_dataset_id: str = "preprocessing", - dataset_location: str = os.environ.get("VERTEX_LOCATION"), - ingestion_dataset_id: str = "chicago_taxi_trips", - prediction_dataset_id: str = "prediction", - timestamp: str = "2022-12-01 00:00:00", - batch_prediction_machine_type: str = "n1-standard-4", - batch_prediction_min_replicas: int = 3, - batch_prediction_max_replicas: int = 10, - resource_suffix: str = os.environ.get("RESOURCE_SUFFIX"), -): - """ - Tensorflow prediction pipeline which: - 1. Extracts a dataset from BQ - 2. Looks up the default model version (champion) and - dataset which was used to the train model. - 3. Runs a BatchPredictionJob with optional training-serving skew detection. - 4. Post-processes predictions - 5. Loads predictions into BQ - - Args: - project_id (str): project id of the Google Cloud project - project_location (str): location of the Google Cloud project - ingestion_project_id (str): project id containing the source bigquery data - for ingestion. This can be the same as `project_id` if the source data is - in the same project where the ML pipeline is executed. - model_name (str): name of model - preprocessing_dataset_id (str): id of BQ dataset used to - store all staging data . - prediction_dataset_id (str): id of BQ dataset used to - store all predictions. - dataset_location (str): location of dataset - ingestion_dataset_id (str): dataset id of ingestion data - timestamp (str): Optional. Empty or a specific timestamp in ISO 8601 format - (YYYY-MM-DDThh:mm:ss.sss±hh:mm or YYYY-MM-DDThh:mm:ss). - If any time part is missing, it will be regarded as zero. - batch_prediction_machine_type (str): Machine type to be used for Vertex Batch - Prediction. Example machine_types - n1-standard-4, n1-standard-16 etc - batch_prediction_min_replicas (int): Minimum no of machines to distribute the - Vertex Batch Prediction job for horizontal scalability - batch_prediction_max_replicas (int): Maximum no of machines to distribute the - Vertex Batch Prediction job for horizontal scalability. - resource_suffix (str): Optional. Additional suffix to append GCS resources - that get overwritten. - - Returns: - None - - """ - - # Create variables to ensure the same arguments are passed - # into different components of the pipeline - file_pattern = "" # e.g. "files-*.csv", used as file pattern on storage - time_column = "trip_start_timestamp" - ingestion_table = "taxi_trips" - table_suffix = "_tf_prediction" + str(resource_suffix) # suffix to table names - ingested_table = "ingested_data" + table_suffix - monitoring_alert_email_addresses = [] - monitoring_skew_config = {"defaultSkewThreshold": {"value": 0.001}} - - # generate sql queries which are used in ingestion and preprocessing - # operations - queries_folder = pathlib.Path(__file__).parent / "queries" - - preprocessing_query = generate_query( - queries_folder / "preprocessing.sql", - source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", - source_table=ingestion_table, - prediction_dataset=f"{ingestion_project_id}.{prediction_dataset_id}", - preprocessing_dataset=f"{ingestion_project_id}.{preprocessing_dataset_id}", - ingested_table=ingested_table, - dataset_region=project_location, - filter_column=time_column, - filter_start_value=timestamp, - ) - - # data ingestion and preprocessing operations - preprocessing = BigqueryQueryJobOp( - project=project_id, location=dataset_location, query=preprocessing_query - ).set_display_name("Ingest data") - - # lookup champion model - champion_model = ( - lookup_model( - model_name=model_name, - project_location=project_location, - project_id=project_id, - fail_on_model_not_found=True, - ) - .set_display_name("Look up champion model") - .set_caching_options(False) - ) - - # batch predict from BigQuery to BigQuery - bigquery_source_input_uri = ( - f"bq://{project_id}.{preprocessing_dataset_id}.{ingested_table}" - ) - bigquery_destination_output_uri = f"bq://{project_id}.{prediction_dataset_id}" - instance_config = {"instanceType": "object"} - - # predict data - batch_prediction = ( - model_batch_predict( - model=champion_model.outputs["model"], - job_display_name="my-tensorflow-batch-prediction-job", - project_location=project_location, - project_id=project_id, - source_uri=bigquery_source_input_uri, - destination_uri=bigquery_destination_output_uri, - source_format="bigquery", - destination_format="bigquery", - machine_type=batch_prediction_machine_type, - starting_replica_count=batch_prediction_min_replicas, - max_replica_count=batch_prediction_max_replicas, - monitoring_training_dataset=champion_model.outputs["training_dataset"], - monitoring_alert_email_addresses=monitoring_alert_email_addresses, - monitoring_skew_config=monitoring_skew_config, - instance_config=instance_config, - ) - .after(preprocessing) - .set_display_name("Batch prediction job") - ) diff --git a/pipelines/src/pipelines/tensorflow/prediction/queries/preprocessing.sql b/pipelines/src/pipelines/tensorflow/prediction/queries/preprocessing.sql deleted file mode 100644 index 3e25a603..00000000 --- a/pipelines/src/pipelines/tensorflow/prediction/queries/preprocessing.sql +++ /dev/null @@ -1,69 +0,0 @@ --- Copyright 2022 Google LLC - --- Licensed under the Apache License, Version 2.0 (the 'License'); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at - --- https://www.apache.org/licenses/LICENSE-2.0 - --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an 'AS IS' BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - --- Treat 'filter_start_value' as the current time, unless it is empty then use CURRENT_DATETIME() instead --- This allows us to set the filter_start_value to a specific time for testing or for backfill - --- If prediction dataset don't exist, create it -CREATE SCHEMA IF NOT EXISTS `{{ prediction_dataset }}` - OPTIONS ( - description = 'Prediction Dataset', - location = '{{ dataset_region }}'); - --- We recreate the ingestion table every time the pipeline run, --- so we need to drop the generated in the previous run -DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; - -CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( -with filter_start_values as ( - SELECT - IF('{{ filter_start_value }}' = '', CURRENT_DATETIME(), CAST('{{ filter_start_value }}' AS DATETIME)) as filter_start_value -) --- Ingest data between 2 and 3 months ago -,filtered_data as ( - SELECT - * - FROM `{{ source_dataset }}.{{ source_table }}`, filter_start_values - WHERE - DATE({{ filter_column }}) BETWEEN - DATE_SUB(DATE(CAST(filter_start_values.filter_start_value as DATETIME)), INTERVAL 3 MONTH) AND - DATE_SUB(DATE(filter_start_value), INTERVAL 2 MONTH) -) --- Use the average trip_seconds as a replacement for NULL or 0 values -,mean_time as ( - SELECT CAST(avg(trip_seconds) AS INT64) as avg_trip_seconds - FROM filtered_data -) - -SELECT - CAST(EXTRACT(DAYOFWEEK FROM trip_start_timestamp) AS FLOAT64) AS dayofweek, - CAST(EXTRACT(HOUR FROM trip_start_timestamp) AS FLOAT64) AS hourofday, - ST_DISTANCE( - ST_GEOGPOINT(pickup_longitude, pickup_latitude), - ST_GEOGPOINT(dropoff_longitude, dropoff_latitude)) AS trip_distance, - trip_miles, - CAST( CASE WHEN trip_seconds is NULL then m.avg_trip_seconds - WHEN trip_seconds <= 0 then m.avg_trip_seconds - ELSE trip_seconds - END AS FLOAT64) AS trip_seconds, - payment_type, - company, -FROM filtered_data as t, mean_time as m -WHERE - trip_miles > 0 AND fare > 0 AND fare < 1500 - {% for field in ['fare', 'trip_start_timestamp', 'pickup_longitude', - 'pickup_latitude', 'dropoff_longitude', 'dropoff_latitude','payment_type','company'] %} - AND `{{ field }}` IS NOT NULL - {% endfor %} - ); diff --git a/pipelines/src/pipelines/tensorflow/training/pipeline.py b/pipelines/src/pipelines/tensorflow/training/pipeline.py deleted file mode 100644 index 67f267b1..00000000 --- a/pipelines/src/pipelines/tensorflow/training/pipeline.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import pathlib - -from google_cloud_pipeline_components.v1.bigquery import BigqueryQueryJobOp -from kfp import dsl -from pipelines import generate_query -from bigquery_components import extract_bq_to_dataset -from vertex_components import ( - lookup_model, - custom_train_job, - import_model_evaluation, - update_best_model, -) - - -@dsl.pipeline(name="tensorflow-train-pipeline") -def pipeline( - project_id: str = os.environ.get("VERTEX_PROJECT_ID"), - project_location: str = os.environ.get("VERTEX_LOCATION"), - ingestion_project_id: str = os.environ.get("VERTEX_PROJECT_ID"), - model_name: str = "simple_tensorflow", - dataset_id: str = "preprocessing", - dataset_location: str = os.environ.get("VERTEX_LOCATION"), - ingestion_dataset_id: str = "chicago_taxi_trips", - timestamp: str = "2022-12-01 00:00:00", - staging_bucket: str = os.environ.get("VERTEX_PIPELINE_ROOT"), - resource_suffix: str = os.environ.get("RESOURCE_SUFFIX"), - test_dataset_uri: str = "", -): - """ - Tensorflow Keras training pipeline which: - 1. Splits and extracts a dataset from BQ to GCS - 2. Trains a model via Vertex AI CustomTrainingJob - 3. Evaluates the model against the current champion model - 4. If better the model becomes the new default model - - Args: - project_id (str): project id of the Google Cloud project - project_location (str): location of the Google Cloud project - ingestion_project_id (str): project id containing the source bigquery data - for ingestion. This can be the same as `project_id` if the source data is - in the same project where the ML pipeline is executed. - model_name (str): name of model - model_label (str): label of model - dataset_id (str): id of BQ dataset used to store all staging data & predictions - dataset_location (str): location of dataset - ingestion_dataset_id (str): dataset id of ingestion data - timestamp (str): Optional. Empty or a specific timestamp in ISO 8601 format - (YYYY-MM-DDThh:mm:ss.sss±hh:mm or YYYY-MM-DDThh:mm:ss). - If any time part is missing, it will be regarded as zero. - staging_bucket (str): Staging bucket for pipeline artifacts. - resource_suffix (str): Optional. Additional suffix to append GCS resources - that get overwritten. - test_dataset_uri (str): Optional. GCS URI of statis held-out test dataset. - """ - - # Create variables to ensure the same arguments are passed - # into different components of the pipeline - label_column_name = "total_fare" - time_column = "trip_start_timestamp" - ingestion_table = "taxi_trips" - table_suffix = f"_tf_training_{resource_suffix}" # suffix to table names - ingested_table = "ingested_data" + table_suffix - preprocessed_table = "preprocessed_data" + table_suffix - train_table = "train_data" + table_suffix - valid_table = "valid_data" + table_suffix - test_table = "test_data" + table_suffix - primary_metric = "rootMeanSquaredError" - pipeline_files_gcs_path = "TODO - remove" - train_script_uri = f"{pipeline_files_gcs_path}/assets/train_tf_model.py" - hparams = dict( - batch_size=100, - epochs=5, - loss_fn="MeanSquaredError", - optimizer="Adam", - learning_rate=0.01, - hidden_units=[(64, "relu"), (32, "relu")], - distribute_strategy="single", - early_stopping_epochs=5, - ) - - # generate sql queries which are used in ingestion and preprocessing - # operations - - queries_folder = pathlib.Path(__file__).parent / "queries" - - preprocessing_query = generate_query( - queries_folder / "preprocessing.sql", - source_dataset=f"{ingestion_project_id}.{ingestion_dataset_id}", - source_table=ingestion_table, - preprocessing_dataset=f"{ingestion_project_id}.{dataset_id}", - ingested_table=ingested_table, - dataset_region=project_location, - filter_column=time_column, - target_column=label_column_name, - filter_start_value=timestamp, - train_table=train_table, - validation_table=valid_table, - test_table=test_table, - ) - - preprocessing = BigqueryQueryJobOp( - project=project_id, location=dataset_location, query=preprocessing_query - ).set_display_name("Ingest & preprocess data") - - # data extraction to gcs - - train_dataset = ( - extract_bq_to_dataset( - bq_client_project_id=project_id, - source_project_id=project_id, - dataset_id=dataset_id, - table_name=train_table, - dataset_location=dataset_location, - ) - .after(preprocessing) - .set_display_name("Extract train") - .set_caching_options(False) - ).outputs["dataset"] - valid_dataset = ( - extract_bq_to_dataset( - bq_client_project_id=project_id, - source_project_id=project_id, - dataset_id=dataset_id, - table_name=valid_table, - dataset_location=dataset_location, - ) - .after(preprocessing) - .set_display_name("Extract validation data") - .set_caching_options(False) - ).outputs["dataset"] - test_dataset = ( - extract_bq_to_dataset( - bq_client_project_id=project_id, - source_project_id=project_id, - dataset_id=dataset_id, - table_name=test_table, - dataset_location=dataset_location, - destination_gcs_uri=test_dataset_uri, - ) - .after(preprocessing) - .set_display_name("Extract test data") - .set_caching_options(False) - ).outputs["dataset"] - - existing_model = ( - lookup_model( - model_name=model_name, - project_location=project_location, - project_id=project_id, - fail_on_model_not_found=False, - ) - .set_display_name("Lookup past model") - .set_caching_options(False) - .outputs["model_resource_name"] - ) - - train_model = custom_train_job( - train_script_uri=train_script_uri, - train_data=train_dataset, - valid_data=valid_dataset, - test_data=test_dataset, - project_id=project_id, - project_location=project_location, - model_display_name=model_name, - train_container_uri="europe-docker.pkg.dev/vertex-ai/training/tf-cpu.2-6:latest", # noqa: E501 - serving_container_uri="europe-docker.pkg.dev/vertex-ai/prediction/tf2-cpu.2-6:latest", # noqa: E501 - hparams=hparams, - staging_bucket=staging_bucket, - parent_model=existing_model, - ).set_display_name("Train model") - - evaluation = import_model_evaluation( - model=train_model.outputs["model"], - metrics=train_model.outputs["metrics"], - test_dataset=test_dataset, - pipeline_job_id="{{$.pipeline_job_name}}", - project_location=project_location, - ).set_display_name("Import evaluation") - - with dsl.Condition(existing_model != "", "champion-exists"): - update_best_model( - challenger=train_model.outputs["model"], - challenger_evaluation=evaluation.outputs["model_evaluation"], - parent_model=existing_model, - eval_metric=primary_metric, - eval_lower_is_better=True, - project_id=project_id, - project_location=project_location, - ).set_display_name("Update best model") diff --git a/pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql b/pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql deleted file mode 100644 index 0182aac2..00000000 --- a/pipelines/src/pipelines/tensorflow/training/queries/preprocessing.sql +++ /dev/null @@ -1,88 +0,0 @@ --- If preprocessing dataset don't exist, create it -CREATE SCHEMA IF NOT EXISTS `{{ preprocessing_dataset }}` - OPTIONS ( - description = 'Preprocessing Dataset', - location = '{{ dataset_region }}'); - --- We recreate the ingestion table every time the pipeline run, --- so we need to drop the generated in the previous run -DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ ingested_table }}`; - -CREATE TABLE `{{ preprocessing_dataset }}.{{ ingested_table }}` AS ( -WITH filter_start_values AS ( -SELECT - IF('{{ filter_start_value }}' = '', - CURRENT_DATETIME(), - CAST('{{ filter_start_value }}' AS DATETIME)) AS filter_start_value -) --- Ingest data between 2 and 3 months ago -,filtered_data AS ( - SELECT - * - FROM `{{ source_dataset }}.{{ source_table }}`, filter_start_values - WHERE - DATE({{ filter_column }}) BETWEEN - DATE_SUB(DATE(CAST(filter_start_values.filter_start_value AS DATETIME)), INTERVAL 3 MONTH) AND - DATE_SUB(DATE(filter_start_value), INTERVAL 2 MONTH) -) --- Use the average trip_seconds as a replacement for NULL or 0 values -,mean_time AS ( - SELECT CAST(avg(trip_seconds) AS INT64) as avg_trip_seconds - FROM filtered_data -) -SELECT - CAST(EXTRACT(DAYOFWEEK FROM trip_start_timestamp) AS FLOAT64) AS dayofweek, - CAST(EXTRACT(HOUR FROM trip_start_timestamp) AS FLOAT64) AS hourofday, - ST_DISTANCE( - ST_GEOGPOINT(pickup_longitude, pickup_latitude), - ST_GEOGPOINT(dropoff_longitude, dropoff_latitude)) AS trip_distance, - trip_miles, - CAST( CASE WHEN trip_seconds is NULL then m.avg_trip_seconds - WHEN trip_seconds <= 0 then m.avg_trip_seconds - ELSE trip_seconds - END AS FLOAT64) AS trip_seconds, - payment_type, - company, - (fare + tips + tolls + extras) AS `{{ target_column }}`, -FROM filtered_data AS t, mean_time AS m -WHERE - trip_miles > 0 AND fare > 0 AND fare < 1500 - {% for field in ['fare', 'trip_start_timestamp', 'pickup_longitude', - 'pickup_latitude', 'dropoff_longitude', 'dropoff_latitude','payment_type','company'] %} - AND `{{ field }}` IS NOT NULL - {% endfor %} -); - --- Drop and creation of train, testing and validations tables -DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ train_table }}`; - -CREATE TABLE `{{ preprocessing_dataset }}.{{ train_table }}` AS ( -SELECT - * -FROM - `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t -WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - 10) IN (0, 1, 2, 3, 4, 5, 6, 7)); - -DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ validation_table }}`; - -CREATE TABLE `{{ preprocessing_dataset }}.{{ validation_table }}` AS ( -SELECT - * -FROM - `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t -WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - 10) IN (8)); - -DROP TABLE IF EXISTS `{{ preprocessing_dataset }}.{{ test_table }}`; - -CREATE TABLE `{{ preprocessing_dataset }}.{{ test_table }}` AS ( -SELECT - * -FROM - `{{ preprocessing_dataset }}.{{ ingested_table }}` AS t -WHERE - MOD(ABS(FARM_FINGERPRINT(TO_JSON_STRING(t))), - 10) IN (9)); diff --git a/pipelines/src/pipelines/xgboost/training/pipeline.py b/pipelines/src/pipelines/training/pipeline.py similarity index 100% rename from pipelines/src/pipelines/xgboost/training/pipeline.py rename to pipelines/src/pipelines/training/pipeline.py diff --git a/pipelines/src/pipelines/xgboost/training/queries/preprocessing.sql b/pipelines/src/pipelines/training/queries/preprocessing.sql similarity index 100% rename from pipelines/src/pipelines/xgboost/training/queries/preprocessing.sql rename to pipelines/src/pipelines/training/queries/preprocessing.sql diff --git a/pipelines/src/pipelines/xgboost/README.md b/pipelines/src/pipelines/xgboost/README.md deleted file mode 100644 index b40ec332..00000000 --- a/pipelines/src/pipelines/xgboost/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# XGBoost Pipelines - -## Training pipeline - -The XGBoost training pipeline can be found in [`training/pipeline.py`](training/pipeline.py) . Within the kubeflow pipeline, [`train_xgboost_model`](../kfp_components/xgboost/train.py) is the main training component which contains the implementation of an XGB model with `scikit-learn` preprocessing.This component can then be wrapped in a custom kfp ContainerOp from [`google-cloud-pipeline-components`](https://github.com/kubeflow/pipelines/blob/master/components/google-cloud/google_cloud_pipeline_components/experimental/custom_job/utils.py) which submits a Vertex Training job with added flexibility for `machine_type`, `replica_count`, `accelerator_type` among other machine configurations. - -The training phase is preceded by a preprocessing phase where different transformations are applied to the training and evaluation data using scikit-learn preprocessing functions. The **preprocessing** step and the **training** step define the two components of the Scikit-Learn pipeline as shown in the diagram below. - -![Training process](../../docs/images/xgboost_architecture.png) - -## Preprocessing with Scikit-learn -The 3 data transformation steps considered in the `train.py` script are: - -|Encoder|Description|Features| -|:----|:----|:----| -|[StandardScaler()](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html)|Centering and scaling numerical values| `dayofweek`, `hourofday`, `trip_distance`, `trip_miles`, `trip_seconds`| -|[OneHotEncoder()](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html)|Encoding a chosen subset of categorical features as a one-hot numeric array|`payment_type`, new/unknown values in categorial features are represented as zeroes everywhere in the one-hot numeric array| -|[OrdinalEncoder()](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OrdinalEncoder.html)|Encoding a chosen subset of categorical features as an integer array|`company`, new/unknown values in categorical features are assigned to an integer equal to the number of categories for that feature in the training set| - -More processing steps can be included to the pipeline. For more details, see the [official documentation](https://scikit-learn.org/stable/modules/preprocessing.html). Ensure that these additional pre-processing steps can handle new/unknown values in test data. - -## The XGBoost Model - -In our example implementation, we have a regression problem of predicting the total fare of a taxi trip in Chicago. Thus, we use XGBRegressor whose hyperparameteres are defined in the variable `model_params` in the file [training/pipeline.py](training/pipeline.py). - -### Model Hyperparameters - -You can specify different hyperparameters through the `model_params` argument of `train_xgboost_model`, including: - - `Booster`: the type of booster (`gbtree` is a tree based booster used by default). - - `max_depth`: the depth of each tree. - - `Objective`: equivalent to the loss function (squared loss, `reg:squarederror`, is the default). - - `min_split_loss`: the minimum loss reduction required to make a further partition on a leaf node of the tree. - -More hyperparameters can be used to customize your training. For more details consult the [XGBoost documentation](https://xgboost.readthedocs.io/en/stable/parameter.html) - -### Model artifacts -Two model artifacts are generated when we run the training job: - - `Model.joblib` : The model is exported to GCS file as a [joblib](https://joblib.readthedocs.io/en/latest/why.html#benefits-of-pipelines) object. - - `Eval_result` : The evaluation metrics are exported to GCS as JSON file. - -![xgboost_component_model&metrics_artifact](../../docs/images/xgboost_component_model&metrics_artifact.png) -### Model test/evaluation -Once the model is trained, it will be used to get challenger predictions for evaluation purposes. In general, the component [`predict_tensorflow_model`](../kfp_components/tensorflow/predict.py) -which expects a single CSV file to create predictions for test data is implemented in the pipeline. However, if you are working with larger test data, it is more efficient to -replace it with a Google prebuilt component, [`ModelBatchPredictOp`](https://google-cloud-pipeline-components.readthedocs.io/en/google-cloud-pipeline-components-0.2.1/google_cloud_pipeline_components.aiplatform.html), -to avoid crash caused by memory overload. - -## Prediction pipeline -The XGBoost prediction pipeline can be found in [prediction/pipeline.py](prediction/pipeline.py). diff --git a/pipelines/tests/xgboost/prediction/test_e2e.py b/pipelines/tests/prediction/test_e2e.py similarity index 92% rename from pipelines/tests/xgboost/prediction/test_e2e.py rename to pipelines/tests/prediction/test_e2e.py index 635fdd36..4dd903cd 100644 --- a/pipelines/tests/xgboost/prediction/test_e2e.py +++ b/pipelines/tests/prediction/test_e2e.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pipelines.xgboost.prediction.pipeline import xgboost_pipeline +from pipelines.prediction.pipeline import pipeline from tests.e2e.test_e2e import pipeline_e2e_test @@ -35,7 +35,7 @@ def test_pipeline_run(enable_caching) -> None: # tasks (components) and outputs for tasks which occur unconditionally pipeline_e2e_test( - xgboost_pipeline, + pipeline, enable_caching=enable_caching, common_tasks={}, ) diff --git a/pipelines/tests/tensorflow/prediction/test_e2e.py b/pipelines/tests/tensorflow/prediction/test_e2e.py deleted file mode 100644 index c0e56e8e..00000000 --- a/pipelines/tests/tensorflow/prediction/test_e2e.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from pipelines.tensorflow.prediction.pipeline import tensorflow_pipeline -from tests.e2e.test_e2e import pipeline_e2e_test - - -def test_pipeline_run(enable_caching) -> None: - """ - Tests if pipeline is run successfully - Triggers pipeline synchronously. - Tests will fail if: - - Any errors are thrown during execution - - Any of the expected component outputs are empty (size == 0kb) - - Arguments: - None - - Returns: - None - """ - pipeline_e2e_test( - tensorflow_pipeline, - enable_caching=enable_caching, - common_tasks={}, - ) diff --git a/pipelines/tests/tensorflow/training/test_e2e.py b/pipelines/tests/tensorflow/training/test_e2e.py deleted file mode 100644 index fa306ec7..00000000 --- a/pipelines/tests/tensorflow/training/test_e2e.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from pipelines.tensorflow.training.pipeline import tensorflow_pipeline -from tests.e2e.test_e2e import pipeline_e2e_test - - -def test_pipeline_run(enable_caching) -> None: - """ - Tests if pipeline is run successfully - Triggers pipeline synchronously. - Tests will fail if: - - Any errors are thrown during execution - - Any of the expected component outputs are empty (size == 0kb) - - Arguments: - None - - Returns: - None - """ - pipeline_e2e_test( - tensorflow_pipeline, - common_tasks={}, - enable_caching=enable_caching, - ) diff --git a/pipelines/tests/xgboost/training/test_e2e.py b/pipelines/tests/training/test_e2e.py similarity index 92% rename from pipelines/tests/xgboost/training/test_e2e.py rename to pipelines/tests/training/test_e2e.py index b494fe9e..9886c6db 100644 --- a/pipelines/tests/xgboost/training/test_e2e.py +++ b/pipelines/tests/training/test_e2e.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pipelines.xgboost.training.pipeline import xgboost_pipeline +from pipelines.training.pipeline import pipeline from tests.e2e.test_e2e import pipeline_e2e_test @@ -31,7 +31,7 @@ def test_pipeline_run(enable_caching) -> None: None """ pipeline_e2e_test( - xgboost_pipeline, + pipeline, enable_caching=enable_caching, common_tasks={}, ) diff --git a/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example b/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example index beb66051..0fefcd87 100644 --- a/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example +++ b/terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example @@ -18,7 +18,7 @@ cloud_schedulers_config = { xgboost_training = { name = "xgboost-training-pipeline-trigger" - description = "Trigger my XGBoost training pipeline in Vertex" + description = "Trigger my training pipeline in Vertex" schedule = "0 0 * * 0" time_zone = "UTC" template_path = "https://-kfp.pkg.dev//vertex-pipelines/xgboost-train-pipeline/" @@ -33,12 +33,12 @@ cloud_schedulers_config = { dataset_location = "europe-west2" ingestion_dataset_id = "chicago_taxi_trips" timestamp = "2022-12-01 00:00:00" - }, + } }, xgboost_prediction = { name = "xgboost-prediction-pipeline-trigger" - description = "Trigger my XGBoost prediction pipeline in Vertex" + description = "Trigger my prediction pipeline in Vertex" schedule = "0 0 * * 0" time_zone = "UTC" template_path = "https://-kfp.pkg.dev//vertex-pipelines/xgboost-prediction-pipeline/" @@ -56,49 +56,6 @@ cloud_schedulers_config = { batch_prediction_machine_type = "n1-standard-4" batch_prediction_min_replicas = 3 batch_prediction_max_replicas = 5 - }, - }, - - tensorflow_training = { - name = "tensorflow-training-pipeline-trigger" - description = "Trigger my TensorFlow training pipeline in Vertex" - schedule = "0 0 * * 0" - time_zone = "UTC" - template_path = "https://-kfp.pkg.dev//vertex-pipelines/tensorflow-train-pipeline/" - enable_caching = null - pipeline_parameters = { - project_id = "my-project-id" - project_location = "europe-west4" - ingestion_project_id = "my-project-id" - model_name = "tensorflow_with_preprocessing" - model_label = "label_name" - dataset_id = "preprocessing" - dataset_location = "europe-west2" - ingestion_dataset_id = "chicago_taxi_trips" - timestamp = "2022-12-01 00:00:00" - }, - }, - - tensorflow_prediction = { - name = "tensorflow-prediction-pipeline-trigger" - description = "Trigger my TensorFlow prediction pipeline in Vertex" - schedule = "0 0 * * 0" - time_zone = "UTC" - template_path = "https://-kfp.pkg.dev//vertex-pipelines/tensorflow-prediction-pipeline/" - enable_caching = null - pipeline_parameters = { - project_id = "my-project-id" - project_location = "europe-west4" - ingestion_project_id = "my-project-id" - model_name = "tensorflow_with_preprocessing" - model_label = "label_name" - dataset_id = "preprocessing" - dataset_location = "europe-west2" - ingestion_dataset_id = "chicago_taxi_trips" - timestamp = "2022-12-01 00:00:00" - batch_prediction_machine_type = "n1-standard-4" - batch_prediction_min_replicas = 3 - batch_prediction_max_replicas = 5 - }, - }, + } + } } From 5abfa36b14645275fb782dd54d88851ebf6d73c6 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Wed, 26 Jul 2023 13:17:34 +0100 Subject: [PATCH 129/238] fix: restore lookup_model component for prediction pipeline --- .../src/vertex_components/__init__.py | 2 + .../src/vertex_components/lookup_model.py | 99 +++++++++++++++ .../tests/test_lookup_model.py | 114 ++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 components/vertex-components/src/vertex_components/lookup_model.py create mode 100644 components/vertex-components/tests/test_lookup_model.py diff --git a/components/vertex-components/src/vertex_components/__init__.py b/components/vertex-components/src/vertex_components/__init__.py index 040039b3..3e112c57 100644 --- a/components/vertex-components/src/vertex_components/__init__.py +++ b/components/vertex-components/src/vertex_components/__init__.py @@ -1,9 +1,11 @@ +from .lookup_model import lookup_model from .model_batch_predict import model_batch_predict from .upload_model import upload_model __version__ = "0.0.1" __all__ = [ + "lookup_model", "model_batch_predict", "upload_model", ] diff --git a/components/vertex-components/src/vertex_components/lookup_model.py b/components/vertex-components/src/vertex_components/lookup_model.py new file mode 100644 index 00000000..76b24ac1 --- /dev/null +++ b/components/vertex-components/src/vertex_components/lookup_model.py @@ -0,0 +1,99 @@ +# Copyright 2022 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# https://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from kfp.v2.dsl import component, Output, Model +from typing import NamedTuple + + +@component( + base_image="python:3.9", + packages_to_install=["google-cloud-aiplatform==1.24.1"], +) +def lookup_model( + model_name: str, + project_location: str, + project_id: str, + model: Output[Model], + order_models_by: str = "create_time desc", + fail_on_model_not_found: bool = False, +) -> NamedTuple("Outputs", [("model_resource_name", str), ("training_dataset", dict)]): + """ + Fetch a model given a model name (display name) and export to GCS. + + Args: + model_name (str): display name of the model + project_location (str): location of the Google Cloud project + project_id (str): project id of the Google Cloud project + model (Output[Model]): a Vertex AI model + order_models_by (str): if multiple models are found based on the display name, + use a filter clause: + A comma-separated list of fields to order by, sorted in + ascending order. Use "desc" after a field name for descending. + Supported fields: `display_name`, `create_time`, `update_time` + Defaults to "create_time desc". + fail_on_model_not_found (bool): if set to True, raise runtime error if + model is not found + + Returns: + str: Resource name of the found model. Empty string if model not found. + """ + + import json + import logging + import os + from pathlib import Path + from google.cloud.aiplatform import Model + + TRAINING_DATASET_INFO = "training_dataset.json" + + logging.info(f"listing models with display name {model_name}") + models = Model.list( + filter=f'display_name="{model_name}"', + order_by=order_models_by, + location=project_location, + project=project_id, + ) + logging.info(f"found {len(models)} models") + + training_dataset = {} + model_resource_name = "" + if len(models) == 0: + logging.error( + f"No model found with name {model_name}" + + f"(project: {project_id} location: {project_location})" + ) + if fail_on_model_not_found: + raise RuntimeError(f"Failed as model was not found") + elif len(models) == 1: + target_model = models[0] + model_resource_name = target_model.resource_name + logging.info(f"choosing model by order ({order_models_by})") + logging.info(f"model display name: {target_model.display_name}") + logging.info(f"model resource name: {target_model.resource_name}") + logging.info(f"model uri: {target_model.uri}") + model.uri = target_model.uri + model.metadata["resourceName"] = target_model.resource_name + + path = Path(model.path) / TRAINING_DATASET_INFO + logging.info(f"Reading training dataset metadata: {path}") + + if os.path.exists(path): + with open(path, "r") as fp: + training_dataset = json.load(fp) + else: + logging.warning("Training dataset metadata doesn't exist!") + else: + raise RuntimeError(f"Multiple models with name {model_name} were found.") + + return model_resource_name, training_dataset diff --git a/components/vertex-components/tests/test_lookup_model.py b/components/vertex-components/tests/test_lookup_model.py new file mode 100644 index 00000000..1ba31f81 --- /dev/null +++ b/components/vertex-components/tests/test_lookup_model.py @@ -0,0 +1,114 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import google.cloud.aiplatform # noqa +from kfp.v2.dsl import Model +from unittest import mock +import pytest + +import vertex_components + +lookup_model = vertex_components.lookup_model.python_func + + +def test_lookup_model(tmpdir): + """ + Assert lookup_model produces expected resource name, and that list method is + called with the correct arguemnts + + Args: + tmpdir: built-in pytest tmpdir fixture + + Returns: + None + """ + with mock.patch("google.cloud.aiplatform.Model") as mock_model: + + # Mock attribute and method + + mock_path = tmpdir + mock_model.resource_name = "my-model-resource-name" + mock_model.uri = mock_path + mock_model.list.return_value = [mock_model] + + # Invoke the model look up + found_model_resource_name, _ = lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=False, + model=Model(uri=mock_path), + ) + + assert found_model_resource_name == "my-model-resource-name" + + # Check the list method was called once with the correct arguments + mock_model.list.assert_called_once_with( + filter='display_name="my-model"', + order_by="create_time desc", + location="europe-west4", + project="my-project-id", + ) + + +def test_lookup_model_when_no_models(tmpdir): + """ + Checks that when there are no models and fail_on_model_found = False, + lookup_model returns an empty string. + + Args: + tmpdir: built-in pytest tmpdir fixture + + Returns: + None + """ + with mock.patch("google.cloud.aiplatform.Model") as mock_model: + mock_model.list.return_value = [] + exported_model_resource_name, _ = lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=False, + model=Model(uri=str(tmpdir)), + ) + print(exported_model_resource_name) + assert exported_model_resource_name == "" + + +def test_lookup_model_when_no_models_fail(tmpdir): + """ + Checks that when there are no models and fail_on_model_found = True, + lookup_model raises a RuntimeError. + + Args: + tmpdir: built-in pytest tmpdir fixture + + Returns: + None + """ + with mock.patch("google.cloud.aiplatform.Model") as mock_model: + mock_model.list.return_value = [] + + # Verify that a ValueError is raised + with pytest.raises(RuntimeError): + lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=True, + model=Model(uri=str(tmpdir)), + ) From 15b4c98f8e9d9da280c2b691861902dc77b90f43 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Thu, 27 Jul 2023 09:08:19 +0100 Subject: [PATCH 130/238] feat: add custom serving container --- Makefile | 15 +- .../src/vertex_components/upload_model.py | 2 + env.sh.example | 1 + {training => model}/.gitignore | 0 model/poetry.lock | 991 ++++++++++++++++++ {training => model}/pyproject.toml | 7 +- {training => model/serving}/.gcloudignore | 0 model/serving/Dockerfile | 10 + model/serving/main.py | 29 + model/training/.gcloudignore | 2 + {training => model/training}/Dockerfile | 2 +- training/main.py => model/training/train.py | 0 .../src/pipelines/prediction/pipeline.py | 7 +- pipelines/src/pipelines/training/pipeline.py | 11 +- training/poetry.lock | 283 ----- 15 files changed, 1065 insertions(+), 295 deletions(-) rename {training => model}/.gitignore (100%) create mode 100644 model/poetry.lock rename {training => model}/pyproject.toml (64%) rename {training => model/serving}/.gcloudignore (100%) create mode 100644 model/serving/Dockerfile create mode 100644 model/serving/main.py create mode 100644 model/training/.gcloudignore rename {training => model/training}/Dockerfile (89%) rename training/main.py => model/training/train.py (100%) delete mode 100644 training/poetry.lock diff --git a/Makefile b/Makefile index f7ec8a3f..ef889513 100644 --- a/Makefile +++ b/Makefile @@ -92,10 +92,21 @@ destroy-infra: ## DESTROY the Terraform infrastructure in your project. Requires terraform destroy -var 'project_id=${VERTEX_PROJECT_ID}' -var 'region=${VERTEX_LOCATION}' build-training-container: ## Build and push training container image using Docker - @ cd training && \ - poetry export -f requirements.txt -o requirements.txt && \ + @ cd model && \ + poetry export -f requirements.txt -o training/requirements.txt && \ + cd training && \ gcloud builds submit . \ --tag=${TRAINING_CONTAINER_IMAGE} \ --region=${VERTEX_LOCATION} \ --project=${VERTEX_PROJECT_ID} \ --gcs-source-staging-dir=gs://${VERTEX_PROJECT_ID}-staging/source + +build-serving-container: ## Build and push serving container image using Docker + @ cd model && \ + poetry export --with serving -f requirements.txt -o serving/requirements.txt && \ + cd serving && \ + gcloud builds submit . \ + --tag=${SERVING_CONTAINER_IMAGE} \ + --region=${VERTEX_LOCATION} \ + --project=${VERTEX_PROJECT_ID} \ + --gcs-source-staging-dir=gs://${VERTEX_PROJECT_ID}-staging/source diff --git a/components/vertex-components/src/vertex_components/upload_model.py b/components/vertex-components/src/vertex_components/upload_model.py index 59838d37..4f3b5729 100644 --- a/components/vertex-components/src/vertex_components/upload_model.py +++ b/components/vertex-components/src/vertex_components/upload_model.py @@ -193,6 +193,8 @@ def import_evaluation( display_name=model_name, artifact_uri=model.uri, serving_container_image_uri=serving_container_image, + serving_container_predict_route="/predict", + serving_container_health_route="/healthz", parent_model=( champion_model.resource_name if champion_model is not None else None ), diff --git a/env.sh.example b/env.sh.example index 528c3ae7..eedd1308 100644 --- a/env.sh.example +++ b/env.sh.example @@ -25,3 +25,4 @@ export RESOURCE_SUFFIX=default export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root export TRAINING_CONTAINER_IMAGE=${VERTEX_LOCATION}-docker.pkg.dev/${VERTEX_PROJECT_ID}/vertex-images/training:${RESOURCE_SUFFIX} +export SERVING_CONTAINER_IMAGE=${VERTEX_LOCATION}-docker.pkg.dev/${VERTEX_PROJECT_ID}/vertex-images/serving:${RESOURCE_SUFFIX} diff --git a/training/.gitignore b/model/.gitignore similarity index 100% rename from training/.gitignore rename to model/.gitignore diff --git a/model/poetry.lock b/model/poetry.lock new file mode 100644 index 00000000..93cadc26 --- /dev/null +++ b/model/poetry.lock @@ -0,0 +1,991 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.5.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.7" +files = [ + {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"}, + {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"}, +] + +[[package]] +name = "anyio" +version = "3.7.1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.7" +files = [ + {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, + {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, +] + +[package.dependencies] +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] + +[[package]] +name = "cachetools" +version = "5.3.1" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, +] + +[[package]] +name = "certifi" +version = "2023.7.22" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.2.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, + {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, +] + +[[package]] +name = "click" +version = "8.1.6" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, + {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, + {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastapi" +version = "0.100.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.7" +files = [ + {file = "fastapi-0.100.0-py3-none-any.whl", hash = "sha256:271662daf986da8fa98dc2b7c7f61c4abdfdccfb4786d79ed8b2878f172c6d5f"}, + {file = "fastapi-0.100.0.tar.gz", hash = "sha256:acb5f941ea8215663283c10018323ba7ea737c571b67fc7e88e9469c7eb1d12e"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<3.0.0" +starlette = ">=0.27.0,<0.28.0" +typing-extensions = ">=4.5.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "google-api-core" +version = "2.11.1" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.11.1.tar.gz", hash = "sha256:25d29e05a0058ed5f19c61c0a78b1b53adea4d9364b464d014fbda941f6d1c9a"}, + {file = "google_api_core-2.11.1-py3-none-any.whl", hash = "sha256:d92a5a92dc36dd4f4b9ee4e55528a90e432b059f93aee6ad857f9de8cc7ae94a"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.22.0" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "google-auth-2.22.0.tar.gz", hash = "sha256:164cba9af4e6e4e40c3a4f90a1a6c12ee56f14c0b4868d1ca91b32826ab334ce"}, + {file = "google_auth-2.22.0-py2.py3-none-any.whl", hash = "sha256:d61d1b40897407b574da67da1a833bdc10d5a11642566e506565d1b1a46ba873"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" +six = ">=1.9.0" +urllib3 = "<2.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-core" +version = "2.3.3" +description = "Google Cloud API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-core-2.3.3.tar.gz", hash = "sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb"}, + {file = "google_cloud_core-2.3.3-py2.py3-none-any.whl", hash = "sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863"}, +] + +[package.dependencies] +google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" + +[package.extras] +grpc = ["grpcio (>=1.38.0,<2.0dev)"] + +[[package]] +name = "google-cloud-storage" +version = "2.10.0" +description = "Google Cloud Storage API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-storage-2.10.0.tar.gz", hash = "sha256:934b31ead5f3994e5360f9ff5750982c5b6b11604dc072bc452c25965e076dc7"}, + {file = "google_cloud_storage-2.10.0-py2.py3-none-any.whl", hash = "sha256:9433cf28801671de1c80434238fb1e7e4a1ba3087470e90f70c928ea77c2b9d7"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" +google-cloud-core = ">=2.3.0,<3.0dev" +google-resumable-media = ">=2.3.2" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +protobuf = ["protobuf (<5.0.0dev)"] + +[[package]] +name = "google-crc32c" +version = "1.5.0" +description = "A python wrapper of the C library 'Google CRC32C'" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win32.whl", hash = "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win32.whl", hash = "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win32.whl", hash = "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win32.whl", hash = "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win32.whl", hash = "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93"}, +] + +[package.extras] +testing = ["pytest"] + +[[package]] +name = "google-resumable-media" +version = "2.5.0" +description = "Utilities for Google Media Downloads and Resumable Uploads" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "google-resumable-media-2.5.0.tar.gz", hash = "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93"}, + {file = "google_resumable_media-2.5.0-py2.py3-none-any.whl", hash = "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec"}, +] + +[package.dependencies] +google-crc32c = ">=1.0,<2.0dev" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] +requests = ["requests (>=2.18.0,<3.0.0dev)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.59.1" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.59.1.tar.gz", hash = "sha256:b35d530fe825fb4227857bc47ad84c33c809ac96f312e13182bdeaa2abe1178a"}, + {file = "googleapis_common_protos-1.59.1-py2.py3-none-any.whl", hash = "sha256:0cbedb6fb68f1c07e18eb4c48256320777707e7d0c55063ae56c15db3224a61e"}, +] + +[package.dependencies] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "joblib" +version = "1.3.1" +description = "Lightweight pipelining with Python functions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "joblib-1.3.1-py3-none-any.whl", hash = "sha256:89cf0529520e01b3de7ac7b74a8102c90d16d54c64b5dd98cafcd14307fdf915"}, + {file = "joblib-1.3.1.tar.gz", hash = "sha256:1f937906df65329ba98013dc9692fe22a4c5e4a648112de500508b18a21b41e3"}, +] + +[[package]] +name = "numpy" +version = "1.25.1" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77d339465dff3eb33c701430bcb9c325b60354698340229e1dff97745e6b3efa"}, + {file = "numpy-1.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d736b75c3f2cb96843a5c7f8d8ccc414768d34b0a75f466c05f3a739b406f10b"}, + {file = "numpy-1.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a90725800caeaa160732d6b31f3f843ebd45d6b5f3eec9e8cc287e30f2805bf"}, + {file = "numpy-1.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c6c9261d21e617c6dc5eacba35cb68ec36bb72adcff0dee63f8fbc899362588"}, + {file = "numpy-1.25.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0def91f8af6ec4bb94c370e38c575855bf1d0be8a8fbfba42ef9c073faf2cf19"}, + {file = "numpy-1.25.1-cp310-cp310-win32.whl", hash = "sha256:fd67b306320dcadea700a8f79b9e671e607f8696e98ec255915c0c6d6b818503"}, + {file = "numpy-1.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:c1516db588987450b85595586605742879e50dcce923e8973f79529651545b57"}, + {file = "numpy-1.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b82655dd8efeea69dbf85d00fca40013d7f503212bc5259056244961268b66e"}, + {file = "numpy-1.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e8f6049c4878cb16960fbbfb22105e49d13d752d4d8371b55110941fb3b17800"}, + {file = "numpy-1.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41a56b70e8139884eccb2f733c2f7378af06c82304959e174f8e7370af112e09"}, + {file = "numpy-1.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5154b1a25ec796b1aee12ac1b22f414f94752c5f94832f14d8d6c9ac40bcca6"}, + {file = "numpy-1.25.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38eb6548bb91c421261b4805dc44def9ca1a6eef6444ce35ad1669c0f1a3fc5d"}, + {file = "numpy-1.25.1-cp311-cp311-win32.whl", hash = "sha256:791f409064d0a69dd20579345d852c59822c6aa087f23b07b1b4e28ff5880fcb"}, + {file = "numpy-1.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:c40571fe966393b212689aa17e32ed905924120737194b5d5c1b20b9ed0fb171"}, + {file = "numpy-1.25.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d7abcdd85aea3e6cdddb59af2350c7ab1ed764397f8eec97a038ad244d2d105"}, + {file = "numpy-1.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a180429394f81c7933634ae49b37b472d343cccb5bb0c4a575ac8bbc433722f"}, + {file = "numpy-1.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d412c1697c3853c6fc3cb9751b4915859c7afe6a277c2bf00acf287d56c4e625"}, + {file = "numpy-1.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20e1266411120a4f16fad8efa8e0454d21d00b8c7cee5b5ccad7565d95eb42dd"}, + {file = "numpy-1.25.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f76aebc3358ade9eacf9bc2bb8ae589863a4f911611694103af05346637df1b7"}, + {file = "numpy-1.25.1-cp39-cp39-win32.whl", hash = "sha256:247d3ffdd7775bdf191f848be8d49100495114c82c2bd134e8d5d075fb386a1c"}, + {file = "numpy-1.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:1d5d3c68e443c90b38fdf8ef40e60e2538a27548b39b12b73132456847f4b631"}, + {file = "numpy-1.25.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:35a9527c977b924042170a0887de727cd84ff179e478481404c5dc66b4170009"}, + {file = "numpy-1.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d3fe3dd0506a28493d82dc3cf254be8cd0d26f4008a417385cbf1ae95b54004"}, + {file = "numpy-1.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:012097b5b0d00a11070e8f2e261128c44157a8689f7dedcf35576e525893f4fe"}, + {file = "numpy-1.25.1.tar.gz", hash = "sha256:9a3a9f3a61480cc086117b426a8bd86869c213fc4072e606f01c4e4b66eb92bf"}, +] + +[[package]] +name = "pandas" +version = "2.0.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, + {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0"}, + {file = "pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210"}, + {file = "pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df"}, + {file = "pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd"}, + {file = "pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0"}, + {file = "pandas-2.0.3-cp38-cp38-win32.whl", hash = "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02"}, + {file = "pandas-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641"}, + {file = "pandas-2.0.3-cp39-cp39-win32.whl", hash = "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682"}, + {file = "pandas-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc"}, + {file = "pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.20.3", markers = "python_version < \"3.10\""}, + {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.1" + +[package.extras] +all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] +aws = ["s3fs (>=2021.08.0)"] +clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"] +compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"] +computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"] +feather = ["pyarrow (>=7.0.0)"] +fss = ["fsspec (>=2021.07.0)"] +gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"] +hdf5 = ["tables (>=3.6.1)"] +html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"] +mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"] +parquet = ["pyarrow (>=7.0.0)"] +performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"] +plot = ["matplotlib (>=3.6.1)"] +postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"] +spss = ["pyreadstat (>=1.1.2)"] +sql-other = ["SQLAlchemy (>=1.4.16)"] +test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.6.3)"] + +[[package]] +name = "protobuf" +version = "4.23.4" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "protobuf-4.23.4-cp310-abi3-win32.whl", hash = "sha256:5fea3c64d41ea5ecf5697b83e41d09b9589e6f20b677ab3c48e5f242d9b7897b"}, + {file = "protobuf-4.23.4-cp310-abi3-win_amd64.whl", hash = "sha256:7b19b6266d92ca6a2a87effa88ecc4af73ebc5cfde194dc737cf8ef23a9a3b12"}, + {file = "protobuf-4.23.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8547bf44fe8cec3c69e3042f5c4fb3e36eb2a7a013bb0a44c018fc1e427aafbd"}, + {file = "protobuf-4.23.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:fee88269a090ada09ca63551bf2f573eb2424035bcf2cb1b121895b01a46594a"}, + {file = "protobuf-4.23.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:effeac51ab79332d44fba74660d40ae79985901ac21bca408f8dc335a81aa597"}, + {file = "protobuf-4.23.4-cp37-cp37m-win32.whl", hash = "sha256:c3e0939433c40796ca4cfc0fac08af50b00eb66a40bbbc5dee711998fb0bbc1e"}, + {file = "protobuf-4.23.4-cp37-cp37m-win_amd64.whl", hash = "sha256:9053df6df8e5a76c84339ee4a9f5a2661ceee4a0dab019e8663c50ba324208b0"}, + {file = "protobuf-4.23.4-cp38-cp38-win32.whl", hash = "sha256:e1c915778d8ced71e26fcf43c0866d7499891bca14c4368448a82edc61fdbc70"}, + {file = "protobuf-4.23.4-cp38-cp38-win_amd64.whl", hash = "sha256:351cc90f7d10839c480aeb9b870a211e322bf05f6ab3f55fcb2f51331f80a7d2"}, + {file = "protobuf-4.23.4-cp39-cp39-win32.whl", hash = "sha256:6dd9b9940e3f17077e820b75851126615ee38643c2c5332aa7a359988820c720"}, + {file = "protobuf-4.23.4-cp39-cp39-win_amd64.whl", hash = "sha256:0a5759f5696895de8cc913f084e27fd4125e8fb0914bb729a17816a33819f474"}, + {file = "protobuf-4.23.4-py3-none-any.whl", hash = "sha256:e9d0be5bf34b275b9f87ba7407796556abeeba635455d036c7351f7c183ef8ff"}, + {file = "protobuf-4.23.4.tar.gz", hash = "sha256:ccd9430c0719dce806b93f89c91de7977304729e55377f872a92465d548329a9"}, +] + +[[package]] +name = "pyasn1" +version = "0.5.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.0-py2.py3-none-any.whl", hash = "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57"}, + {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pydantic" +version = "2.1.1" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-2.1.1-py3-none-any.whl", hash = "sha256:43bdbf359d6304c57afda15c2b95797295b702948082d4c23851ce752f21da70"}, + {file = "pydantic-2.1.1.tar.gz", hash = "sha256:22d63db5ce4831afd16e7c58b3192d3faf8f79154980d9397d9867254310ba4b"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.4.0" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.4.0" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.4.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:2ca4687dd996bde7f3c420def450797feeb20dcee2b9687023e3323c73fc14a2"}, + {file = "pydantic_core-2.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:782fced7d61469fd1231b184a80e4f2fa7ad54cd7173834651a453f96f29d673"}, + {file = "pydantic_core-2.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6213b471b68146af97b8551294e59e7392c2117e28ffad9c557c65087f4baee3"}, + {file = "pydantic_core-2.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63797499a219d8e81eb4e0c42222d0a4c8ec896f5c76751d4258af95de41fdf1"}, + {file = "pydantic_core-2.4.0-cp310-cp310-manylinux_2_24_armv7l.whl", hash = "sha256:0455876d575a35defc4da7e0a199596d6c773e20d3d42fa1fc29f6aa640369ed"}, + {file = "pydantic_core-2.4.0-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:8c938c96294d983dcf419b54dba2d21056959c22911d41788efbf949a29ae30d"}, + {file = "pydantic_core-2.4.0-cp310-cp310-manylinux_2_24_s390x.whl", hash = "sha256:878a5017d93e776c379af4e7b20f173c82594d94fa073059bcc546789ad50bf8"}, + {file = "pydantic_core-2.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:69159afc2f2dc43285725f16143bc5df3c853bc1cb7df6021fce7ef1c69e8171"}, + {file = "pydantic_core-2.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54df7df399b777c1fd144f541c95d351b3aa110535a6810a6a569905d106b6f3"}, + {file = "pydantic_core-2.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e412607ca89a0ced10758dfb8f9adcc365ce4c1c377e637c01989a75e9a9ec8a"}, + {file = "pydantic_core-2.4.0-cp310-none-win32.whl", hash = "sha256:853f103e2b9a58832fdd08a587a51de8b552ae90e1a5d167f316b7eabf8d7dde"}, + {file = "pydantic_core-2.4.0-cp310-none-win_amd64.whl", hash = "sha256:3ba2c9c94a9176f6321a879c8b864d7c5b12d34f549a4c216c72ce213d7d953c"}, + {file = "pydantic_core-2.4.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:a8b7acd04896e8f161e1500dc5f218017db05c1d322f054e89cbd089ce5d0071"}, + {file = "pydantic_core-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:16468bd074fa4567592d3255bf25528ed41e6b616d69bf07096bdb5b66f947d1"}, + {file = "pydantic_core-2.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cba5ad5eef02c86a1f3da00544cbc59a510d596b27566479a7cd4d91c6187a11"}, + {file = "pydantic_core-2.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7206e41e04b443016e930e01685bab7a308113c0b251b3f906942c8d4b48fcb"}, + {file = "pydantic_core-2.4.0-cp311-cp311-manylinux_2_24_armv7l.whl", hash = "sha256:c1375025f0bfc9155286ebae8eecc65e33e494c90025cda69e247c3ccd2bab00"}, + {file = "pydantic_core-2.4.0-cp311-cp311-manylinux_2_24_ppc64le.whl", hash = "sha256:3534118289e33130ed3f1cc487002e8d09b9f359be48b02e9cd3de58ce58fba9"}, + {file = "pydantic_core-2.4.0-cp311-cp311-manylinux_2_24_s390x.whl", hash = "sha256:94d2b36a74623caab262bf95f0e365c2c058396082bd9d6a9e825657d0c1e7fa"}, + {file = "pydantic_core-2.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:af24ad4fbaa5e4a2000beae0c3b7fd1c78d7819ab90f9370a1cfd8998e3f8a3c"}, + {file = "pydantic_core-2.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bf10963d8aed8bbe0165b41797c9463d4c5c8788ae6a77c68427569be6bead41"}, + {file = "pydantic_core-2.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68199ada7c310ddb8c76efbb606a0de656b40899388a7498954f423e03fc38be"}, + {file = "pydantic_core-2.4.0-cp311-none-win32.whl", hash = "sha256:6f855bcc96ed3dd56da7373cfcc9dcbabbc2073cac7f65c185772d08884790ce"}, + {file = "pydantic_core-2.4.0-cp311-none-win_amd64.whl", hash = "sha256:de39eb3bab93a99ddda1ac1b9aa331b944d8bcc4aa9141148f7fd8ee0299dafc"}, + {file = "pydantic_core-2.4.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:f773b39780323a0499b53ebd91a28ad11cde6705605d98d999dfa08624caf064"}, + {file = "pydantic_core-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a297c0d6c61963c5c3726840677b798ca5b7dfc71bc9c02b9a4af11d23236008"}, + {file = "pydantic_core-2.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:546064c55264156b973b5e65e5fafbe5e62390902ce3cf6b4005765505e8ff56"}, + {file = "pydantic_core-2.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36ba9e728588588f0196deaf6751b9222492331b5552f865a8ff120869d372e0"}, + {file = "pydantic_core-2.4.0-cp312-cp312-manylinux_2_24_armv7l.whl", hash = "sha256:57a53a75010c635b3ad6499e7721eaa3b450e03f6862afe2dbef9c8f66e46ec8"}, + {file = "pydantic_core-2.4.0-cp312-cp312-manylinux_2_24_ppc64le.whl", hash = "sha256:4b262bbc13022f2097c48a21adcc360a81d83dc1d854c11b94953cd46d7d3c07"}, + {file = "pydantic_core-2.4.0-cp312-cp312-manylinux_2_24_s390x.whl", hash = "sha256:01947ad728f426fa07fcb26457ebf90ce29320259938414bc0edd1476e75addb"}, + {file = "pydantic_core-2.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b2799c2eaf182769889761d4fb4d78b82bc47dae833799fedbf69fc7de306faa"}, + {file = "pydantic_core-2.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a08fd490ba36d1fbb2cd5dcdcfb9f3892deb93bd53456724389135712b5fc735"}, + {file = "pydantic_core-2.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1e8a7c62d15a5c4b307271e4252d76ebb981d6251c6ecea4daf203ef0179ea4f"}, + {file = "pydantic_core-2.4.0-cp312-none-win32.whl", hash = "sha256:9206c14a67c38de7b916e486ae280017cf394fa4b1aa95cfe88621a4e1d79725"}, + {file = "pydantic_core-2.4.0-cp312-none-win_amd64.whl", hash = "sha256:884235507549a6b2d3c4113fb1877ae263109e787d9e0eb25c35982ab28d0399"}, + {file = "pydantic_core-2.4.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:4cbe929efa77a806e8f1a97793f2dc3ea3475ae21a9ed0f37c21320fe93f6f50"}, + {file = "pydantic_core-2.4.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:9137289de8fe845c246a8c3482dd0cb40338846ba683756d8f489a4bd8fddcae"}, + {file = "pydantic_core-2.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5d8e764b5646623e57575f624f8ebb8f7a9f7fd1fae682ef87869ca5fec8dcf"}, + {file = "pydantic_core-2.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fba0aff4c407d0274e43697e785bcac155ad962be57518d1c711f45e72da70f"}, + {file = "pydantic_core-2.4.0-cp37-cp37m-manylinux_2_24_armv7l.whl", hash = "sha256:30527d173e826f2f7651f91c821e337073df1555e3b5a0b7b1e2c39e26e50678"}, + {file = "pydantic_core-2.4.0-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:bd7d1dde70ff3e09e4bc7a1cbb91a7a538add291bfd5b3e70ef1e7b45192440f"}, + {file = "pydantic_core-2.4.0-cp37-cp37m-manylinux_2_24_s390x.whl", hash = "sha256:72f1216ca8cef7b8adacd4c4c6b89c3b0c4f97503197f5284c80f36d6e4edd30"}, + {file = "pydantic_core-2.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b013c7861a7c7bfcec48fd709513fea6f9f31727e7a0a93ca0dd12e056740717"}, + {file = "pydantic_core-2.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:478f5f6d7e32bd4a04d102160efb2d389432ecf095fe87c555c0a6fc4adfc1a4"}, + {file = "pydantic_core-2.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d9610b47b5fe4aacbbba6a9cb5f12cbe864eec99dbfed5710bd32ef5dd8a5d5b"}, + {file = "pydantic_core-2.4.0-cp37-none-win32.whl", hash = "sha256:ff246c0111076c8022f9ba325c294f2cb5983403506989253e04dbae565e019b"}, + {file = "pydantic_core-2.4.0-cp37-none-win_amd64.whl", hash = "sha256:d0c2b713464a8e263a243ae7980d81ce2de5ac59a9f798a282e44350b42dc516"}, + {file = "pydantic_core-2.4.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:12ef6838245569fd60a179fade81ca4b90ae2fa0ef355d616f519f7bb27582db"}, + {file = "pydantic_core-2.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49db206eb8fdc4b4f30e6e3e410584146d813c151928f94ec0db06c4f2595538"}, + {file = "pydantic_core-2.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a507d7fa44688bbac76af6521e488b3da93de155b9cba6f2c9b7833ce243d59"}, + {file = "pydantic_core-2.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffe18407a4d000c568182ce5388bbbedeb099896904e43fc14eee76cfae6dec5"}, + {file = "pydantic_core-2.4.0-cp38-cp38-manylinux_2_24_armv7l.whl", hash = "sha256:fa8e48001b39d54d97d7b380a0669fa99fc0feeb972e35a2d677ba59164a9a22"}, + {file = "pydantic_core-2.4.0-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:394f12a2671ff8c4dfa2e85be6c08be0651ad85bc1e6aa9c77c21671baaf28cd"}, + {file = "pydantic_core-2.4.0-cp38-cp38-manylinux_2_24_s390x.whl", hash = "sha256:2f9ea0355f90db2a76af530245fa42f04d98f752a1236ed7c6809ec484560d5b"}, + {file = "pydantic_core-2.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:61d4e713f467abcdd59b47665d488bb898ad3dd47ce7446522a50e0cbd8e8279"}, + {file = "pydantic_core-2.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:453862ab268f6326b01f067ed89cb3a527d34dc46f6f4eeec46a15bbc706d0da"}, + {file = "pydantic_core-2.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:56a85fa0dab1567bd0cac10f0c3837b03e8a0d939e6a8061a3a420acd97e9421"}, + {file = "pydantic_core-2.4.0-cp38-none-win32.whl", hash = "sha256:0d726108c1c0380b88b6dd4db559f0280e0ceda9e077f46ff90bc85cd4d03e77"}, + {file = "pydantic_core-2.4.0-cp38-none-win_amd64.whl", hash = "sha256:047580388644c473b934d27849f8ed8dbe45df0adb72104e78b543e13bf69762"}, + {file = "pydantic_core-2.4.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:867d3eea954bea807cabba83cfc939c889a18576d66d197c60025b15269d7cc0"}, + {file = "pydantic_core-2.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:664402ef0c238a7f8a46efb101789d5f2275600fb18114446efec83cfadb5b66"}, + {file = "pydantic_core-2.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64e8012ad60a5f0da09ed48725e6e923d1be25f2f091a640af6079f874663813"}, + {file = "pydantic_core-2.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac2b680de398f293b68183317432b3d67ab3faeba216aec18de0c395cb5e3060"}, + {file = "pydantic_core-2.4.0-cp39-cp39-manylinux_2_24_armv7l.whl", hash = "sha256:8efc1be43b036c2b6bcfb1451df24ee0ddcf69c31351003daf2699ed93f5687b"}, + {file = "pydantic_core-2.4.0-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:d93aedbc4614cc21b9ab0d0c4ccd7143354c1f7cffbbe96ae5216ad21d1b21b5"}, + {file = "pydantic_core-2.4.0-cp39-cp39-manylinux_2_24_s390x.whl", hash = "sha256:af788b64e13d52fc3600a68b16d31fa8d8573e3ff2fc9a38f8a60b8d94d1f012"}, + {file = "pydantic_core-2.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97c6349c81cee2e69ef59eba6e6c08c5936e6b01c2d50b9e4ac152217845ae09"}, + {file = "pydantic_core-2.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cc086ddb6dc654a15deeed1d1f2bcb1cb924ebd70df9dca738af19f64229b06c"}, + {file = "pydantic_core-2.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e953353180bec330c3b830891d260b6f8e576e2d18db3c78d314e56bb2276066"}, + {file = "pydantic_core-2.4.0-cp39-none-win32.whl", hash = "sha256:6feb4b64d11d5420e517910d60a907d08d846cacaf4e029668725cd21d16743c"}, + {file = "pydantic_core-2.4.0-cp39-none-win_amd64.whl", hash = "sha256:153a61ac4030fa019b70b31fb7986461119230d3ba0ab661c757cfea652f4332"}, + {file = "pydantic_core-2.4.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:3fcf529382b282a30b466bd7af05be28e22aa620e016135ac414f14e1ee6b9e1"}, + {file = "pydantic_core-2.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2edef05b63d82568b877002dc4cb5cc18f8929b59077120192df1e03e0c633f8"}, + {file = "pydantic_core-2.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da055a1b0bfa8041bb2ff586b2cb0353ed03944a3472186a02cc44a557a0e661"}, + {file = "pydantic_core-2.4.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:77dadc764cf7c5405e04866181c5bd94a447372a9763e473abb63d1dfe9b7387"}, + {file = "pydantic_core-2.4.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a4ea23b07f29487a7bef2a869f68c7ee0e05424d81375ce3d3de829314c6b5ec"}, + {file = "pydantic_core-2.4.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:382f0baa044d674ad59455a5eff83d7965572b745cc72df35c52c2ce8c731d37"}, + {file = "pydantic_core-2.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:08f89697625e453421401c7f661b9d1eb4c9e4c0a12fd256eeb55b06994ac6af"}, + {file = "pydantic_core-2.4.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:43a405ce520b45941df9ff55d0cd09762017756a7b413bbad3a6e8178e64a2c2"}, + {file = "pydantic_core-2.4.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:584a7a818c84767af16ce8bda5d4f7fedb37d3d231fc89928a192f567e4ef685"}, + {file = "pydantic_core-2.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04922fea7b13cd480586fa106345fe06e43220b8327358873c22d8dfa7a711c7"}, + {file = "pydantic_core-2.4.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17156abac20a9feed10feec867fddd91a80819a485b0107fe61f09f2117fe5f3"}, + {file = "pydantic_core-2.4.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4e562cc63b04636cde361fd47569162f1daa94c759220ff202a8129902229114"}, + {file = "pydantic_core-2.4.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:90f3785146f701e053bb6b9e8f53acce2c919aca91df88bd4975be0cb926eb41"}, + {file = "pydantic_core-2.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e40b1e97edd3dc127aa53d8a5e539a3d0c227d71574d3f9ac1af02d58218a122"}, + {file = "pydantic_core-2.4.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:b27f3e67f6e031f6620655741b7d0d6bebea8b25d415924b3e8bfef2dd7bd841"}, + {file = "pydantic_core-2.4.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be86c2eb12fb0f846262ace9d8f032dc6978b8cb26a058920ecb723dbcb87d05"}, + {file = "pydantic_core-2.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4665f7ed345012a8d2eddf4203ef145f5f56a291d010382d235b94e91813f88a"}, + {file = "pydantic_core-2.4.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:79262be5a292d1df060f29b9a7cdd66934801f987a817632d7552534a172709a"}, + {file = "pydantic_core-2.4.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:5fd905a69ac74eaba5041e21a1e8b1a479dab2b41c93bdcc4c1cede3c12a8d86"}, + {file = "pydantic_core-2.4.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:2ad538b7e07343001934417cdc8584623b4d8823c5b8b258e75ec8d327cec969"}, + {file = "pydantic_core-2.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:dd2429f7635ad4857b5881503f9c310be7761dc681c467a9d27787b674d1250a"}, + {file = "pydantic_core-2.4.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:efff8b6761a1f6e45cebd1b7a6406eb2723d2d5710ff0d1b624fe11313693989"}, + {file = "pydantic_core-2.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32a1e0352558cd7ccc014ffe818c7d87b15ec6145875e2cc5fa4bb7351a1033d"}, + {file = "pydantic_core-2.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a027f41c5008571314861744d83aff75a34cf3a07022e0be32b214a5bc93f7f1"}, + {file = "pydantic_core-2.4.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1927f0e15d190f11f0b8344373731e28fd774c6d676d8a6cfadc95c77214a48b"}, + {file = "pydantic_core-2.4.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7aa82d483d5fb867d4fb10a138ffd57b0f1644e99f2f4f336e48790ada9ada5e"}, + {file = "pydantic_core-2.4.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b85778308bf945e9b33ac604e6793df9b07933108d20bdf53811bc7c2798a4af"}, + {file = "pydantic_core-2.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3ded19dcaefe2f6706d81e0db787b59095f4ad0fbadce1edffdf092294c8a23f"}, + {file = "pydantic_core-2.4.0.tar.gz", hash = "sha256:ec3473c9789cc00c7260d840c3db2c16dbfc816ca70ec87a00cddfa3e1a1cdd5"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2023.3" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, + {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "scikit-learn" +version = "1.3.0" +description = "A set of python modules for machine learning and data mining" +optional = false +python-versions = ">=3.8" +files = [ + {file = "scikit-learn-1.3.0.tar.gz", hash = "sha256:8be549886f5eda46436b6e555b0e4873b4f10aa21c07df45c4bc1735afbccd7a"}, + {file = "scikit_learn-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:981287869e576d42c682cf7ca96af0c6ac544ed9316328fd0d9292795c742cf5"}, + {file = "scikit_learn-1.3.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:436aaaae2c916ad16631142488e4c82f4296af2404f480e031d866863425d2a2"}, + {file = "scikit_learn-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7e28d8fa47a0b30ae1bd7a079519dd852764e31708a7804da6cb6f8b36e3630"}, + {file = "scikit_learn-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae80c08834a473d08a204d966982a62e11c976228d306a2648c575e3ead12111"}, + {file = "scikit_learn-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:552fd1b6ee22900cf1780d7386a554bb96949e9a359999177cf30211e6b20df6"}, + {file = "scikit_learn-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79970a6d759eb00a62266a31e2637d07d2d28446fca8079cf9afa7c07b0427f8"}, + {file = "scikit_learn-1.3.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:850a00b559e636b23901aabbe79b73dc604b4e4248ba9e2d6e72f95063765603"}, + {file = "scikit_learn-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee04835fb016e8062ee9fe9074aef9b82e430504e420bff51e3e5fffe72750ca"}, + {file = "scikit_learn-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d953531f5d9f00c90c34fa3b7d7cfb43ecff4c605dac9e4255a20b114a27369"}, + {file = "scikit_learn-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:151ac2bf65ccf363664a689b8beafc9e6aae36263db114b4ca06fbbbf827444a"}, + {file = "scikit_learn-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6a885a9edc9c0a341cab27ec4f8a6c58b35f3d449c9d2503a6fd23e06bbd4f6a"}, + {file = "scikit_learn-1.3.0-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:9877af9c6d1b15486e18a94101b742e9d0d2f343d35a634e337411ddb57783f3"}, + {file = "scikit_learn-1.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c470f53cea065ff3d588050955c492793bb50c19a92923490d18fcb637f6383a"}, + {file = "scikit_learn-1.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd6e2d7389542eae01077a1ee0318c4fec20c66c957f45c7aac0c6eb0fe3c612"}, + {file = "scikit_learn-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:3a11936adbc379a6061ea32fa03338d4ca7248d86dd507c81e13af428a5bc1db"}, + {file = "scikit_learn-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:998d38fcec96584deee1e79cd127469b3ad6fefd1ea6c2dfc54e8db367eb396b"}, + {file = "scikit_learn-1.3.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ded35e810438a527e17623ac6deae3b360134345b7c598175ab7741720d7ffa7"}, + {file = "scikit_learn-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e8102d5036e28d08ab47166b48c8d5e5810704daecf3a476a4282d562be9a28"}, + {file = "scikit_learn-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7617164951c422747e7c32be4afa15d75ad8044f42e7d70d3e2e0429a50e6718"}, + {file = "scikit_learn-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:1d54fb9e6038284548072df22fd34777e434153f7ffac72c8596f2d6987110dd"}, +] + +[package.dependencies] +joblib = ">=1.1.1" +numpy = ">=1.17.3" +scipy = ">=1.5.0" +threadpoolctl = ">=2.0.0" + +[package.extras] +benchmark = ["matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "pandas (>=1.0.5)"] +docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)", "sphinx (>=6.0.0)", "sphinx-copybutton (>=0.5.2)", "sphinx-gallery (>=0.10.1)", "sphinx-prompt (>=1.3.0)", "sphinxext-opengraph (>=0.4.2)"] +examples = ["matplotlib (>=3.1.3)", "pandas (>=1.0.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)"] +tests = ["black (>=23.3.0)", "matplotlib (>=3.1.3)", "mypy (>=1.3)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.0.272)", "scikit-image (>=0.16.2)"] + +[[package]] +name = "scipy" +version = "1.9.3" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "scipy-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1884b66a54887e21addf9c16fb588720a8309a57b2e258ae1c7986d4444d3bc0"}, + {file = "scipy-1.9.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:83b89e9586c62e787f5012e8475fbb12185bafb996a03257e9675cd73d3736dd"}, + {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a72d885fa44247f92743fc20732ae55564ff2a519e8302fb7e18717c5355a8b"}, + {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01e1dd7b15bd2449c8bfc6b7cc67d630700ed655654f0dfcf121600bad205c9"}, + {file = "scipy-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:68239b6aa6f9c593da8be1509a05cb7f9efe98b80f43a5861cd24c7557e98523"}, + {file = "scipy-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b41bc822679ad1c9a5f023bc93f6d0543129ca0f37c1ce294dd9d386f0a21096"}, + {file = "scipy-1.9.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:90453d2b93ea82a9f434e4e1cba043e779ff67b92f7a0e85d05d286a3625df3c"}, + {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c06e62a390a9167da60bedd4575a14c1f58ca9dfde59830fc42e5197283dab"}, + {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abaf921531b5aeaafced90157db505e10345e45038c39e5d9b6c7922d68085cb"}, + {file = "scipy-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:06d2e1b4c491dc7d8eacea139a1b0b295f74e1a1a0f704c375028f8320d16e31"}, + {file = "scipy-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a04cd7d0d3eff6ea4719371cbc44df31411862b9646db617c99718ff68d4840"}, + {file = "scipy-1.9.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:545c83ffb518094d8c9d83cce216c0c32f8c04aaf28b92cc8283eda0685162d5"}, + {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d54222d7a3ba6022fdf5773931b5d7c56efe41ede7f7128c7b1637700409108"}, + {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cff3a5295234037e39500d35316a4c5794739433528310e117b8a9a0c76d20fc"}, + {file = "scipy-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:2318bef588acc7a574f5bfdff9c172d0b1bf2c8143d9582e05f878e580a3781e"}, + {file = "scipy-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d644a64e174c16cb4b2e41dfea6af722053e83d066da7343f333a54dae9bc31c"}, + {file = "scipy-1.9.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:da8245491d73ed0a994ed9c2e380fd058ce2fa8a18da204681f2fe1f57f98f95"}, + {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4db5b30849606a95dcf519763dd3ab6fe9bd91df49eba517359e450a7d80ce2e"}, + {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c68db6b290cbd4049012990d7fe71a2abd9ffbe82c0056ebe0f01df8be5436b0"}, + {file = "scipy-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:5b88e6d91ad9d59478fafe92a7c757d00c59e3bdc3331be8ada76a4f8d683f58"}, + {file = "scipy-1.9.3.tar.gz", hash = "sha256:fbc5c05c85c1a02be77b1ff591087c83bc44579c6d2bd9fb798bb64ea5e1a027"}, +] + +[package.dependencies] +numpy = ">=1.18.5,<1.26.0" + +[package.extras] +dev = ["flake8", "mypy", "pycodestyle", "typing_extensions"] +doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-panels (>=0.5.2)", "sphinx-tabs"] +test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "starlette" +version = "0.27.0" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.7" +files = [ + {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, + {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] + +[[package]] +name = "threadpoolctl" +version = "3.1.0" +description = "threadpoolctl" +optional = false +python-versions = ">=3.6" +files = [ + {file = "threadpoolctl-3.1.0-py3-none-any.whl", hash = "sha256:8b99adda265feb6773280df41eece7b2e6561b772d21ffd52e372f999024907b"}, + {file = "threadpoolctl-3.1.0.tar.gz", hash = "sha256:a335baacfaa4400ae1f0d8e3a58d6674d2f8828e3716bb2802c44955ad391380"}, +] + +[[package]] +name = "typing-extensions" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "tzdata" +version = "2023.3" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + +[[package]] +name = "urllib3" +version = "1.26.16" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"}, + {file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "uvicorn" +version = "0.23.1" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.23.1-py3-none-any.whl", hash = "sha256:1d55d46b83ee4ce82b4e82f621f2050adb3eb7b5481c13f9af1744951cae2f1f"}, + {file = "uvicorn-0.23.1.tar.gz", hash = "sha256:da9b0c8443b2d7ee9db00a345f1eee6db7317432c9d4400f5049cc8d358383be"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "xgboost" +version = "1.7.6" +description = "XGBoost Python Package" +optional = false +python-versions = ">=3.8" +files = [ + {file = "xgboost-1.7.6-py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.macosx_12_0_x86_64.whl", hash = "sha256:4c34675b4d2678c624ddde5d45361e7e16046923e362e4e609b88353e6b87124"}, + {file = "xgboost-1.7.6-py3-none-macosx_12_0_arm64.whl", hash = "sha256:59b4b366d2cafc7f645e87d897983a5b59be02876194b1d213bd8d8b811d8ce8"}, + {file = "xgboost-1.7.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:281c3c6f4fbed2d36bf95cd02a641afa95e72e9abde70064056da5e76233e8df"}, + {file = "xgboost-1.7.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:b1d5db49b199152d62bd9217c98760207d3de86d2b9d243260c573ffe638f80a"}, + {file = "xgboost-1.7.6-py3-none-win_amd64.whl", hash = "sha256:127cf1f5e2ec25cd41429394c6719b87af1456ce583e89f0bffd35d02ad18bcb"}, + {file = "xgboost-1.7.6.tar.gz", hash = "sha256:1c527554a400445e0c38186039ba1a00425dcdb4e40b37eed0e74cb39a159c47"}, +] + +[package.dependencies] +numpy = "*" +scipy = "*" + +[package.extras] +dask = ["dask", "distributed", "pandas"] +datatable = ["datatable"] +pandas = ["pandas"] +plotting = ["graphviz", "matplotlib"] +pyspark = ["cloudpickle", "pyspark", "scikit-learn"] +scikit-learn = ["scikit-learn"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "2bbdc91e77ecba39a913eb039abe6c1be55f6dcd0b756ff97b8f11b7e376c970" diff --git a/training/pyproject.toml b/model/pyproject.toml similarity index 64% rename from training/pyproject.toml rename to model/pyproject.toml index ad6df515..e879a489 100644 --- a/training/pyproject.toml +++ b/model/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "training" +name = "model" version = "0.1.0" description = "" authors = ["Your Name "] @@ -12,6 +12,11 @@ xgboost = "^1.7.6" pandas = "^2.0.3" +[tool.poetry.group.serving.dependencies] +fastapi = {extras = ["uvicorn"], version = "^0.100.0"} +uvicorn = "^0.23.1" +google-cloud-storage = "^2.10.0" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/training/.gcloudignore b/model/serving/.gcloudignore similarity index 100% rename from training/.gcloudignore rename to model/serving/.gcloudignore diff --git a/model/serving/Dockerfile b/model/serving/Dockerfile new file mode 100644 index 00000000..71207403 --- /dev/null +++ b/model/serving/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.9.16-slim +ENV PIP_NO_CACHE_DIR=off \ + PIP_DISABLE_PIP_VERSION_CHECK=on \ + PIP_DEFAULT_TIMEOUT=100 + +COPY requirements.txt requirements.txt +RUN pip install -r requirements.txt +COPY main.py main.py + +CMD exec uvicorn main:app --host "0.0.0.0" --port "$AIP_HTTP_PORT" diff --git a/model/serving/main.py b/model/serving/main.py new file mode 100644 index 00000000..3b264b02 --- /dev/null +++ b/model/serving/main.py @@ -0,0 +1,29 @@ +import joblib +import os + +import pandas as pd +from fastapi import FastAPI, Request +from google.cloud import storage + +app = FastAPI() +client = storage.Client() + +with open("model.joblib", "wb") as f: + client.download_blob_to_file(f"{os.environ['AIP_STORAGE_URI']}/model.joblib", f) +_model = joblib.load("model.joblib") + + +@app.get(os.environ.get("AIP_HEALTH_ROUTE", "/healthz")) +def health(): + return {} + + +@app.post(os.environ.get("AIP_PREDICT_ROUTE", "/predict")) +async def predict(request: Request): + body = await request.json() + + instances = body["instances"] + inputs_df = pd.DataFrame(instances) + outputs = _model.predict(inputs_df).tolist() + + return {"predictions": outputs} diff --git a/model/training/.gcloudignore b/model/training/.gcloudignore new file mode 100644 index 00000000..510bf9ce --- /dev/null +++ b/model/training/.gcloudignore @@ -0,0 +1,2 @@ +.venv +.DS_Store diff --git a/training/Dockerfile b/model/training/Dockerfile similarity index 89% rename from training/Dockerfile rename to model/training/Dockerfile index bf4587f2..4b4effce 100644 --- a/training/Dockerfile +++ b/model/training/Dockerfile @@ -5,4 +5,4 @@ ENV PIP_NO_CACHE_DIR=off \ COPY requirements.txt requirements.txt RUN pip install -r requirements.txt -COPY main.py main.py +COPY train.py train.py diff --git a/training/main.py b/model/training/train.py similarity index 100% rename from training/main.py rename to model/training/train.py diff --git a/pipelines/src/pipelines/prediction/pipeline.py b/pipelines/src/pipelines/prediction/pipeline.py index 6d618511..cf038d70 100644 --- a/pipelines/src/pipelines/prediction/pipeline.py +++ b/pipelines/src/pipelines/prediction/pipeline.py @@ -78,8 +78,8 @@ def pipeline( # into different components of the pipeline time_column = "trip_start_timestamp" ingestion_table = "taxi_trips" - table_suffix = "_xgb_prediction" + str(resource_suffix) # suffix to table names - ingested_table = "ingested_data" + table_suffix + table_suffix = "_xgb_prediction_" + str(resource_suffix) # suffix to table names + ingested_table = "ingested_data_" + table_suffix monitoring_alert_email_addresses = [] monitoring_skew_config = {"defaultSkewThreshold": {"value": 0.001}} @@ -131,6 +131,9 @@ def pipeline( destination_uri=bigquery_destination_output_uri, source_format="bigquery", destination_format="bigquery", + instance_config={ + "instanceType": "object", + }, machine_type=batch_prediction_machine_type, starting_replica_count=batch_prediction_min_replicas, max_replica_count=batch_prediction_max_replicas, diff --git a/pipelines/src/pipelines/training/pipeline.py b/pipelines/src/pipelines/training/pipeline.py index 93c83228..3e2c3ed4 100644 --- a/pipelines/src/pipelines/training/pipeline.py +++ b/pipelines/src/pipelines/training/pipeline.py @@ -22,7 +22,8 @@ from bigquery_components import extract_bq_to_dataset from vertex_components import upload_model -IMAGE = os.environ.get("TRAINING_CONTAINER_IMAGE") +TRAINING_IMAGE = os.environ["TRAINING_CONTAINER_IMAGE"] +SERVING_IMAGE = os.environ["SERVING_CONTAINER_IMAGE"] @dsl.container_component @@ -36,10 +37,10 @@ def train( hparams: dict, ): return dsl.ContainerSpec( - image=IMAGE, + image=TRAINING_IMAGE, command=["python"], args=[ - "main.py", + "train.py", "--train-data", train_data.path, "--valid-data", @@ -197,9 +198,7 @@ def pipeline( eval_metric=primary_metric, eval_lower_is_better=True, model=train_model.outputs["model"], - serving_container_image=( - "europe-docker.pkg.dev/vertex-ai/prediction/sklearn-cpu.0-24:latest" - ), + serving_container_image=SERVING_IMAGE, model_name=model_name, pipeline_job_id="{{$.pipeline_job_name}}", test_dataset=test_dataset, diff --git a/training/poetry.lock b/training/poetry.lock deleted file mode 100644 index 611c8c73..00000000 --- a/training/poetry.lock +++ /dev/null @@ -1,283 +0,0 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. - -[[package]] -name = "joblib" -version = "1.3.1" -description = "Lightweight pipelining with Python functions" -optional = false -python-versions = ">=3.7" -files = [ - {file = "joblib-1.3.1-py3-none-any.whl", hash = "sha256:89cf0529520e01b3de7ac7b74a8102c90d16d54c64b5dd98cafcd14307fdf915"}, - {file = "joblib-1.3.1.tar.gz", hash = "sha256:1f937906df65329ba98013dc9692fe22a4c5e4a648112de500508b18a21b41e3"}, -] - -[[package]] -name = "numpy" -version = "1.25.1" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "numpy-1.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77d339465dff3eb33c701430bcb9c325b60354698340229e1dff97745e6b3efa"}, - {file = "numpy-1.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d736b75c3f2cb96843a5c7f8d8ccc414768d34b0a75f466c05f3a739b406f10b"}, - {file = "numpy-1.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a90725800caeaa160732d6b31f3f843ebd45d6b5f3eec9e8cc287e30f2805bf"}, - {file = "numpy-1.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c6c9261d21e617c6dc5eacba35cb68ec36bb72adcff0dee63f8fbc899362588"}, - {file = "numpy-1.25.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0def91f8af6ec4bb94c370e38c575855bf1d0be8a8fbfba42ef9c073faf2cf19"}, - {file = "numpy-1.25.1-cp310-cp310-win32.whl", hash = "sha256:fd67b306320dcadea700a8f79b9e671e607f8696e98ec255915c0c6d6b818503"}, - {file = "numpy-1.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:c1516db588987450b85595586605742879e50dcce923e8973f79529651545b57"}, - {file = "numpy-1.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b82655dd8efeea69dbf85d00fca40013d7f503212bc5259056244961268b66e"}, - {file = "numpy-1.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e8f6049c4878cb16960fbbfb22105e49d13d752d4d8371b55110941fb3b17800"}, - {file = "numpy-1.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41a56b70e8139884eccb2f733c2f7378af06c82304959e174f8e7370af112e09"}, - {file = "numpy-1.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5154b1a25ec796b1aee12ac1b22f414f94752c5f94832f14d8d6c9ac40bcca6"}, - {file = "numpy-1.25.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38eb6548bb91c421261b4805dc44def9ca1a6eef6444ce35ad1669c0f1a3fc5d"}, - {file = "numpy-1.25.1-cp311-cp311-win32.whl", hash = "sha256:791f409064d0a69dd20579345d852c59822c6aa087f23b07b1b4e28ff5880fcb"}, - {file = "numpy-1.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:c40571fe966393b212689aa17e32ed905924120737194b5d5c1b20b9ed0fb171"}, - {file = "numpy-1.25.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d7abcdd85aea3e6cdddb59af2350c7ab1ed764397f8eec97a038ad244d2d105"}, - {file = "numpy-1.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a180429394f81c7933634ae49b37b472d343cccb5bb0c4a575ac8bbc433722f"}, - {file = "numpy-1.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d412c1697c3853c6fc3cb9751b4915859c7afe6a277c2bf00acf287d56c4e625"}, - {file = "numpy-1.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20e1266411120a4f16fad8efa8e0454d21d00b8c7cee5b5ccad7565d95eb42dd"}, - {file = "numpy-1.25.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f76aebc3358ade9eacf9bc2bb8ae589863a4f911611694103af05346637df1b7"}, - {file = "numpy-1.25.1-cp39-cp39-win32.whl", hash = "sha256:247d3ffdd7775bdf191f848be8d49100495114c82c2bd134e8d5d075fb386a1c"}, - {file = "numpy-1.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:1d5d3c68e443c90b38fdf8ef40e60e2538a27548b39b12b73132456847f4b631"}, - {file = "numpy-1.25.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:35a9527c977b924042170a0887de727cd84ff179e478481404c5dc66b4170009"}, - {file = "numpy-1.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d3fe3dd0506a28493d82dc3cf254be8cd0d26f4008a417385cbf1ae95b54004"}, - {file = "numpy-1.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:012097b5b0d00a11070e8f2e261128c44157a8689f7dedcf35576e525893f4fe"}, - {file = "numpy-1.25.1.tar.gz", hash = "sha256:9a3a9f3a61480cc086117b426a8bd86869c213fc4072e606f01c4e4b66eb92bf"}, -] - -[[package]] -name = "pandas" -version = "2.0.3" -description = "Powerful data structures for data analysis, time series, and statistics" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, - {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, - {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183"}, - {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0"}, - {file = "pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210"}, - {file = "pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e"}, - {file = "pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8"}, - {file = "pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26"}, - {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d"}, - {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df"}, - {file = "pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd"}, - {file = "pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b"}, - {file = "pandas-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061"}, - {file = "pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5"}, - {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089"}, - {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0"}, - {file = "pandas-2.0.3-cp38-cp38-win32.whl", hash = "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02"}, - {file = "pandas-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78"}, - {file = "pandas-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b"}, - {file = "pandas-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e"}, - {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b"}, - {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641"}, - {file = "pandas-2.0.3-cp39-cp39-win32.whl", hash = "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682"}, - {file = "pandas-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc"}, - {file = "pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c"}, -] - -[package.dependencies] -numpy = [ - {version = ">=1.20.3", markers = "python_version < \"3.10\""}, - {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, - {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, -] -python-dateutil = ">=2.8.2" -pytz = ">=2020.1" -tzdata = ">=2022.1" - -[package.extras] -all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] -aws = ["s3fs (>=2021.08.0)"] -clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"] -compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"] -computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"] -feather = ["pyarrow (>=7.0.0)"] -fss = ["fsspec (>=2021.07.0)"] -gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"] -hdf5 = ["tables (>=3.6.1)"] -html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"] -mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"] -parquet = ["pyarrow (>=7.0.0)"] -performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"] -plot = ["matplotlib (>=3.6.1)"] -postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"] -spss = ["pyreadstat (>=1.1.2)"] -sql-other = ["SQLAlchemy (>=1.4.16)"] -test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.6.3)"] - -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pytz" -version = "2023.3" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, - {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, -] - -[[package]] -name = "scikit-learn" -version = "1.3.0" -description = "A set of python modules for machine learning and data mining" -optional = false -python-versions = ">=3.8" -files = [ - {file = "scikit-learn-1.3.0.tar.gz", hash = "sha256:8be549886f5eda46436b6e555b0e4873b4f10aa21c07df45c4bc1735afbccd7a"}, - {file = "scikit_learn-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:981287869e576d42c682cf7ca96af0c6ac544ed9316328fd0d9292795c742cf5"}, - {file = "scikit_learn-1.3.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:436aaaae2c916ad16631142488e4c82f4296af2404f480e031d866863425d2a2"}, - {file = "scikit_learn-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7e28d8fa47a0b30ae1bd7a079519dd852764e31708a7804da6cb6f8b36e3630"}, - {file = "scikit_learn-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae80c08834a473d08a204d966982a62e11c976228d306a2648c575e3ead12111"}, - {file = "scikit_learn-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:552fd1b6ee22900cf1780d7386a554bb96949e9a359999177cf30211e6b20df6"}, - {file = "scikit_learn-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79970a6d759eb00a62266a31e2637d07d2d28446fca8079cf9afa7c07b0427f8"}, - {file = "scikit_learn-1.3.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:850a00b559e636b23901aabbe79b73dc604b4e4248ba9e2d6e72f95063765603"}, - {file = "scikit_learn-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee04835fb016e8062ee9fe9074aef9b82e430504e420bff51e3e5fffe72750ca"}, - {file = "scikit_learn-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d953531f5d9f00c90c34fa3b7d7cfb43ecff4c605dac9e4255a20b114a27369"}, - {file = "scikit_learn-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:151ac2bf65ccf363664a689b8beafc9e6aae36263db114b4ca06fbbbf827444a"}, - {file = "scikit_learn-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6a885a9edc9c0a341cab27ec4f8a6c58b35f3d449c9d2503a6fd23e06bbd4f6a"}, - {file = "scikit_learn-1.3.0-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:9877af9c6d1b15486e18a94101b742e9d0d2f343d35a634e337411ddb57783f3"}, - {file = "scikit_learn-1.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c470f53cea065ff3d588050955c492793bb50c19a92923490d18fcb637f6383a"}, - {file = "scikit_learn-1.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd6e2d7389542eae01077a1ee0318c4fec20c66c957f45c7aac0c6eb0fe3c612"}, - {file = "scikit_learn-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:3a11936adbc379a6061ea32fa03338d4ca7248d86dd507c81e13af428a5bc1db"}, - {file = "scikit_learn-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:998d38fcec96584deee1e79cd127469b3ad6fefd1ea6c2dfc54e8db367eb396b"}, - {file = "scikit_learn-1.3.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ded35e810438a527e17623ac6deae3b360134345b7c598175ab7741720d7ffa7"}, - {file = "scikit_learn-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e8102d5036e28d08ab47166b48c8d5e5810704daecf3a476a4282d562be9a28"}, - {file = "scikit_learn-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7617164951c422747e7c32be4afa15d75ad8044f42e7d70d3e2e0429a50e6718"}, - {file = "scikit_learn-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:1d54fb9e6038284548072df22fd34777e434153f7ffac72c8596f2d6987110dd"}, -] - -[package.dependencies] -joblib = ">=1.1.1" -numpy = ">=1.17.3" -scipy = ">=1.5.0" -threadpoolctl = ">=2.0.0" - -[package.extras] -benchmark = ["matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "pandas (>=1.0.5)"] -docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)", "sphinx (>=6.0.0)", "sphinx-copybutton (>=0.5.2)", "sphinx-gallery (>=0.10.1)", "sphinx-prompt (>=1.3.0)", "sphinxext-opengraph (>=0.4.2)"] -examples = ["matplotlib (>=3.1.3)", "pandas (>=1.0.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)"] -tests = ["black (>=23.3.0)", "matplotlib (>=3.1.3)", "mypy (>=1.3)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.0.272)", "scikit-image (>=0.16.2)"] - -[[package]] -name = "scipy" -version = "1.9.3" -description = "Fundamental algorithms for scientific computing in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "scipy-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1884b66a54887e21addf9c16fb588720a8309a57b2e258ae1c7986d4444d3bc0"}, - {file = "scipy-1.9.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:83b89e9586c62e787f5012e8475fbb12185bafb996a03257e9675cd73d3736dd"}, - {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a72d885fa44247f92743fc20732ae55564ff2a519e8302fb7e18717c5355a8b"}, - {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01e1dd7b15bd2449c8bfc6b7cc67d630700ed655654f0dfcf121600bad205c9"}, - {file = "scipy-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:68239b6aa6f9c593da8be1509a05cb7f9efe98b80f43a5861cd24c7557e98523"}, - {file = "scipy-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b41bc822679ad1c9a5f023bc93f6d0543129ca0f37c1ce294dd9d386f0a21096"}, - {file = "scipy-1.9.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:90453d2b93ea82a9f434e4e1cba043e779ff67b92f7a0e85d05d286a3625df3c"}, - {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c06e62a390a9167da60bedd4575a14c1f58ca9dfde59830fc42e5197283dab"}, - {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abaf921531b5aeaafced90157db505e10345e45038c39e5d9b6c7922d68085cb"}, - {file = "scipy-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:06d2e1b4c491dc7d8eacea139a1b0b295f74e1a1a0f704c375028f8320d16e31"}, - {file = "scipy-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a04cd7d0d3eff6ea4719371cbc44df31411862b9646db617c99718ff68d4840"}, - {file = "scipy-1.9.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:545c83ffb518094d8c9d83cce216c0c32f8c04aaf28b92cc8283eda0685162d5"}, - {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d54222d7a3ba6022fdf5773931b5d7c56efe41ede7f7128c7b1637700409108"}, - {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cff3a5295234037e39500d35316a4c5794739433528310e117b8a9a0c76d20fc"}, - {file = "scipy-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:2318bef588acc7a574f5bfdff9c172d0b1bf2c8143d9582e05f878e580a3781e"}, - {file = "scipy-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d644a64e174c16cb4b2e41dfea6af722053e83d066da7343f333a54dae9bc31c"}, - {file = "scipy-1.9.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:da8245491d73ed0a994ed9c2e380fd058ce2fa8a18da204681f2fe1f57f98f95"}, - {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4db5b30849606a95dcf519763dd3ab6fe9bd91df49eba517359e450a7d80ce2e"}, - {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c68db6b290cbd4049012990d7fe71a2abd9ffbe82c0056ebe0f01df8be5436b0"}, - {file = "scipy-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:5b88e6d91ad9d59478fafe92a7c757d00c59e3bdc3331be8ada76a4f8d683f58"}, - {file = "scipy-1.9.3.tar.gz", hash = "sha256:fbc5c05c85c1a02be77b1ff591087c83bc44579c6d2bd9fb798bb64ea5e1a027"}, -] - -[package.dependencies] -numpy = ">=1.18.5,<1.26.0" - -[package.extras] -dev = ["flake8", "mypy", "pycodestyle", "typing_extensions"] -doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-panels (>=0.5.2)", "sphinx-tabs"] -test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "threadpoolctl" -version = "3.1.0" -description = "threadpoolctl" -optional = false -python-versions = ">=3.6" -files = [ - {file = "threadpoolctl-3.1.0-py3-none-any.whl", hash = "sha256:8b99adda265feb6773280df41eece7b2e6561b772d21ffd52e372f999024907b"}, - {file = "threadpoolctl-3.1.0.tar.gz", hash = "sha256:a335baacfaa4400ae1f0d8e3a58d6674d2f8828e3716bb2802c44955ad391380"}, -] - -[[package]] -name = "tzdata" -version = "2023.3" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -files = [ - {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, - {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, -] - -[[package]] -name = "xgboost" -version = "1.7.6" -description = "XGBoost Python Package" -optional = false -python-versions = ">=3.8" -files = [ - {file = "xgboost-1.7.6-py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.macosx_12_0_x86_64.whl", hash = "sha256:4c34675b4d2678c624ddde5d45361e7e16046923e362e4e609b88353e6b87124"}, - {file = "xgboost-1.7.6-py3-none-macosx_12_0_arm64.whl", hash = "sha256:59b4b366d2cafc7f645e87d897983a5b59be02876194b1d213bd8d8b811d8ce8"}, - {file = "xgboost-1.7.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:281c3c6f4fbed2d36bf95cd02a641afa95e72e9abde70064056da5e76233e8df"}, - {file = "xgboost-1.7.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:b1d5db49b199152d62bd9217c98760207d3de86d2b9d243260c573ffe638f80a"}, - {file = "xgboost-1.7.6-py3-none-win_amd64.whl", hash = "sha256:127cf1f5e2ec25cd41429394c6719b87af1456ce583e89f0bffd35d02ad18bcb"}, - {file = "xgboost-1.7.6.tar.gz", hash = "sha256:1c527554a400445e0c38186039ba1a00425dcdb4e40b37eed0e74cb39a159c47"}, -] - -[package.dependencies] -numpy = "*" -scipy = "*" - -[package.extras] -dask = ["dask", "distributed", "pandas"] -datatable = ["datatable"] -pandas = ["pandas"] -plotting = ["graphviz", "matplotlib"] -pyspark = ["cloudpickle", "pyspark", "scikit-learn"] -scikit-learn = ["scikit-learn"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.9" -content-hash = "5bcbe70fcb0085df96641192352231b73948cafcdaa211acffd4b4091e70c874" From e0939cf1074f6dca7b429037bec4c5ba5f18c4bd Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Thu, 27 Jul 2023 10:01:58 +0100 Subject: [PATCH 131/238] consolidate training and serving image builds --- Makefile | 19 ++++------------ env.sh.example | 3 +-- model/{serving => }/.gcloudignore | 0 model/.gitignore | 1 - model/Dockerfile | 24 ++++++++++++++++++++ model/cloudbuild.yaml | 7 ++++++ model/serving/Dockerfile | 10 -------- model/training/.gcloudignore | 2 -- model/training/Dockerfile | 8 ------- pipelines/src/pipelines/training/pipeline.py | 8 ++++--- 10 files changed, 41 insertions(+), 41 deletions(-) rename model/{serving => }/.gcloudignore (100%) delete mode 100644 model/.gitignore create mode 100644 model/Dockerfile create mode 100644 model/cloudbuild.yaml delete mode 100644 model/serving/Dockerfile delete mode 100644 model/training/.gcloudignore delete mode 100644 model/training/Dockerfile diff --git a/Makefile b/Makefile index ef889513..72adc2a1 100644 --- a/Makefile +++ b/Makefile @@ -91,22 +91,11 @@ destroy-infra: ## DESTROY the Terraform infrastructure in your project. Requires terraform init -backend-config='bucket=${VERTEX_PROJECT_ID}-tfstate' && \ terraform destroy -var 'project_id=${VERTEX_PROJECT_ID}' -var 'region=${VERTEX_LOCATION}' -build-training-container: ## Build and push training container image using Docker +target ?= training +build-container: ## Build and push training/serving container image using Docker. Specify target= @ cd model && \ - poetry export -f requirements.txt -o training/requirements.txt && \ - cd training && \ gcloud builds submit . \ - --tag=${TRAINING_CONTAINER_IMAGE} \ --region=${VERTEX_LOCATION} \ --project=${VERTEX_PROJECT_ID} \ - --gcs-source-staging-dir=gs://${VERTEX_PROJECT_ID}-staging/source - -build-serving-container: ## Build and push serving container image using Docker - @ cd model && \ - poetry export --with serving -f requirements.txt -o serving/requirements.txt && \ - cd serving && \ - gcloud builds submit . \ - --tag=${SERVING_CONTAINER_IMAGE} \ - --region=${VERTEX_LOCATION} \ - --project=${VERTEX_PROJECT_ID} \ - --gcs-source-staging-dir=gs://${VERTEX_PROJECT_ID}-staging/source + --gcs-source-staging-dir=gs://${VERTEX_PROJECT_ID}-staging/source \ + --substitutions=_DOCKER_TARGET=${target},_DESTINATION_IMAGE_URI=${CONTAINER_IMAGE_REGISTRY}/${target}:${RESOURCE_SUFFIX} diff --git a/env.sh.example b/env.sh.example index eedd1308..63152c5c 100644 --- a/env.sh.example +++ b/env.sh.example @@ -24,5 +24,4 @@ export RESOURCE_SUFFIX=default # Leave as-is export VERTEX_SA_EMAIL=vertex-pipelines@${VERTEX_PROJECT_ID}.iam.gserviceaccount.com export VERTEX_PIPELINE_ROOT=gs://${VERTEX_PROJECT_ID}-pl-root -export TRAINING_CONTAINER_IMAGE=${VERTEX_LOCATION}-docker.pkg.dev/${VERTEX_PROJECT_ID}/vertex-images/training:${RESOURCE_SUFFIX} -export SERVING_CONTAINER_IMAGE=${VERTEX_LOCATION}-docker.pkg.dev/${VERTEX_PROJECT_ID}/vertex-images/serving:${RESOURCE_SUFFIX} +export CONTAINER_IMAGE_REGISTRY=${VERTEX_LOCATION}-docker.pkg.dev/${VERTEX_PROJECT_ID}/vertex-images diff --git a/model/serving/.gcloudignore b/model/.gcloudignore similarity index 100% rename from model/serving/.gcloudignore rename to model/.gcloudignore diff --git a/model/.gitignore b/model/.gitignore deleted file mode 100644 index 4414fc1e..00000000 --- a/model/.gitignore +++ /dev/null @@ -1 +0,0 @@ -requirements.txt diff --git a/model/Dockerfile b/model/Dockerfile new file mode 100644 index 00000000..24e387bd --- /dev/null +++ b/model/Dockerfile @@ -0,0 +1,24 @@ +FROM python:3.9.16-slim AS builder + +ENV PIP_NO_CACHE_DIR=off \ + PIP_DISABLE_PIP_VERSION_CHECK=on \ + PIP_DEFAULT_TIMEOUT=100 + +ARG POETRY_VERSION=1.5.1 + +COPY pyproject.toml pyproject.toml +COPY poetry.lock poetry.lock + +RUN pip install poetry==${POETRY_VERSION} +RUN poetry config virtualenvs.create false && poetry install + +FROM builder AS training + +COPY training/train.py training/train.py + +FROM builder AS serving + +RUN poetry install --with serving +COPY serving/main.py serving/main.py + +CMD exec uvicorn serving.main:app --host "0.0.0.0" --port "$AIP_HTTP_PORT" diff --git a/model/cloudbuild.yaml b/model/cloudbuild.yaml new file mode 100644 index 00000000..167ceb0f --- /dev/null +++ b/model/cloudbuild.yaml @@ -0,0 +1,7 @@ +--- +steps: + - name: 'gcr.io/kaniko-project/executor:latest' + args: + - --destination=${_DESTINATION_IMAGE_URI} + - --target=${_DOCKER_TARGET} + - --cache=true diff --git a/model/serving/Dockerfile b/model/serving/Dockerfile deleted file mode 100644 index 71207403..00000000 --- a/model/serving/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM python:3.9.16-slim -ENV PIP_NO_CACHE_DIR=off \ - PIP_DISABLE_PIP_VERSION_CHECK=on \ - PIP_DEFAULT_TIMEOUT=100 - -COPY requirements.txt requirements.txt -RUN pip install -r requirements.txt -COPY main.py main.py - -CMD exec uvicorn main:app --host "0.0.0.0" --port "$AIP_HTTP_PORT" diff --git a/model/training/.gcloudignore b/model/training/.gcloudignore deleted file mode 100644 index 510bf9ce..00000000 --- a/model/training/.gcloudignore +++ /dev/null @@ -1,2 +0,0 @@ -.venv -.DS_Store diff --git a/model/training/Dockerfile b/model/training/Dockerfile deleted file mode 100644 index 4b4effce..00000000 --- a/model/training/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM python:3.9.16-slim -ENV PIP_NO_CACHE_DIR=off \ - PIP_DISABLE_PIP_VERSION_CHECK=on \ - PIP_DEFAULT_TIMEOUT=100 - -COPY requirements.txt requirements.txt -RUN pip install -r requirements.txt -COPY train.py train.py diff --git a/pipelines/src/pipelines/training/pipeline.py b/pipelines/src/pipelines/training/pipeline.py index 3e2c3ed4..a2906e44 100644 --- a/pipelines/src/pipelines/training/pipeline.py +++ b/pipelines/src/pipelines/training/pipeline.py @@ -22,8 +22,10 @@ from bigquery_components import extract_bq_to_dataset from vertex_components import upload_model -TRAINING_IMAGE = os.environ["TRAINING_CONTAINER_IMAGE"] -SERVING_IMAGE = os.environ["SERVING_CONTAINER_IMAGE"] +CONTAINER_IMAGE_REGISTRY = os.environ["CONTAINER_IMAGE_REGISTRY"] +RESOURCE_SUFFIX = os.environ.get("RESOURCE_SUFFIX", "default") +TRAINING_IMAGE = f"{CONTAINER_IMAGE_REGISTRY}/training:{RESOURCE_SUFFIX}" +SERVING_IMAGE = f"{CONTAINER_IMAGE_REGISTRY}/serving:{RESOURCE_SUFFIX}" @dsl.container_component @@ -40,7 +42,7 @@ def train( image=TRAINING_IMAGE, command=["python"], args=[ - "train.py", + "training/train.py", "--train-data", train_data.path, "--valid-data", From c7449564cf0b95263d71fe2a1d01dc9c17565a73 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Thu, 27 Jul 2023 19:02:10 +0100 Subject: [PATCH 132/238] add container builds to ci/cd --- cloudbuild/e2e-test.yaml | 27 ++++++++++++++++++++++++++- cloudbuild/pr-checks.yaml | 2 ++ cloudbuild/release.yaml | 30 ++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/cloudbuild/e2e-test.yaml b/cloudbuild/e2e-test.yaml index 7c6494ee..150a4c57 100644 --- a/cloudbuild/e2e-test.yaml +++ b/cloudbuild/e2e-test.yaml @@ -14,9 +14,30 @@ --- steps: + - id: build-training-image + name: gcr.io/kaniko-project/executor:latest + args: + - --context=dir:///workspace/model + - --destination=${_TEST_VERTEX_LOCATION}-docker.pkg.dev/${_TEST_VERTEX_PROJECT_ID}/vertex-images/training:${COMMIT_SHA} + - --target=training + - --cache=true + - --compressed-caching=false + waitFor: ['-'] + + - id: build-serving-image + name: gcr.io/kaniko-project/executor:latest + args: + - --context=dir:///workspace/model + - --destination=${_TEST_VERTEX_LOCATION}-docker.pkg.dev/${_TEST_VERTEX_PROJECT_ID}/vertex-images/serving:${COMMIT_SHA} + - --target=serving + - --cache=true + - --compressed-caching=false + waitFor: ['-'] + # Install Python deps # Run end-to-end (E2E) pipeline tests on both pipelines - - name: python:3.9 + - id: e2e-tests + name: python:3.9 entrypoint: /bin/sh args: - -c @@ -32,6 +53,10 @@ steps: - VERTEX_NETWORK=${_TEST_VERTEX_NETWORK} - VERTEX_PIPELINE_ROOT=${_TEST_VERTEX_PIPELINE_ROOT} - RESOURCE_SUFFIX=${COMMIT_SHA} + - CONTAINER_IMAGE_REGISTRY=${_TEST_VERTEX_LOCATION}-docker.pkg.dev/${_TEST_VERTEX_PROJECT_ID}/vertex-images + waitFor: + - build-training-image + - build-serving-image options: logging: CLOUD_LOGGING_ONLY diff --git a/cloudbuild/pr-checks.yaml b/cloudbuild/pr-checks.yaml index 93934970..61e1c923 100644 --- a/cloudbuild/pr-checks.yaml +++ b/cloudbuild/pr-checks.yaml @@ -32,6 +32,8 @@ steps: make test-all-components env: - SKIP=terraform-fmt,git-dirty + - CONTAINER_IMAGE_REGISTRY=dummy_value + - RESOURCE_SUFFIX=default options: logging: CLOUD_LOGGING_ONLY diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index 5852e0bd..ea6d44d8 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -14,6 +14,30 @@ --- steps: + - id: build-training-image + name: gcr.io/kaniko-project/executor:latest + args: + - --context=dir:///workspace/model + - --destination=${_DEV_VERTEX_LOCATION}-docker.pkg.dev/${_DEV_VERTEX_PROJECT_ID}/vertex-images/training:${TAG_NAME} + - --destination=${_TEST_VERTEX_LOCATION}-docker.pkg.dev/${_TEST_VERTEX_PROJECT_ID}/vertex-images/training:${TAG_NAME} + - --destination=${_PROD_VERTEX_LOCATION}-docker.pkg.dev/${_PROD_VERTEX_PROJECT_ID}/vertex-images/training:${TAG_NAME} + - --target=training + - --cache=true + - --compressed-caching=false + waitFor: ['-'] + + - id: build-serving-image + name: gcr.io/kaniko-project/executor:latest + args: + - --context=dir:///workspace/model + - --destination=${_DEV_VERTEX_LOCATION}-docker.pkg.dev/${_DEV_VERTEX_PROJECT_ID}/vertex-images/serving:${TAG_NAME} + - --destination=${_TEST_VERTEX_LOCATION}-docker.pkg.dev/${_TEST_VERTEX_PROJECT_ID}/vertex-images/serving:${TAG_NAME} + - --destination=${_PROD_VERTEX_LOCATION}-docker.pkg.dev/${_PROD_VERTEX_PROJECT_ID}/vertex-images/serving:${TAG_NAME} + - --target=serving + - --cache=true + - --compressed-caching=false + waitFor: ['-'] + # Install poetry, install deps, compile pipelines - name: python:3.9 entrypoint: /bin/sh @@ -36,6 +60,12 @@ steps: --tag=latest \ --tag=${TAG_NAME}; \ done + env: + - RESOURCE_SUFFIX=default + - CONTAINER_IMAGE_REGISTRY=${_TEST_VERTEX_LOCATION}-docker.pkg.dev/${_TEST_VERTEX_PROJECT_ID}/vertex-images + waitFor: + - build-training-image + - build-serving-image options: logging: CLOUD_LOGGING_ONLY From 6ba229896ff633e4744fb963ce10e1d518d64091 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Fri, 28 Jul 2023 11:26:23 +0100 Subject: [PATCH 133/238] fix: update E2E tests for kfp v2 --- pipelines/tests/e2e/test_e2e.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pipelines/tests/e2e/test_e2e.py b/pipelines/tests/e2e/test_e2e.py index c7b8081c..0ff9a25b 100644 --- a/pipelines/tests/e2e/test_e2e.py +++ b/pipelines/tests/e2e/test_e2e.py @@ -13,12 +13,12 @@ # limitations under the License. import logging -from typing import Callable import pytest import os from google.cloud import storage from kfp import compiler +from kfp.components import BaseComponent from pipelines.trigger.main import trigger_pipeline_from_payload @@ -151,7 +151,7 @@ def test_batch_prediction_job_uri( def pipeline_e2e_test( - pipeline_func: Callable, + pipeline_func: BaseComponent, common_tasks: dict, enable_caching: bool = None, **kwargs: dict, @@ -168,11 +168,11 @@ def pipeline_e2e_test( **kwargs (dict): conditional tasks groups in dictionary """ - pipeline_json = f"{pipeline_func.__name__}.json" + pipeline_yaml = f"{pipeline_func.name}.yaml" compiler.Compiler().compile( pipeline_func=pipeline_func, - package_path=pipeline_json, + package_path=pipeline_yaml, type_check=False, ) @@ -181,7 +181,7 @@ def pipeline_e2e_test( enable_caching = None payload = { - "attributes": {"template_path": pipeline_json, "enable_caching": enable_caching} + "attributes": {"template_path": pipeline_yaml, "enable_caching": enable_caching} } try: From 12c5dc2d7fd9d11ddf00fa5f22b5433a885809a1 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Fri, 28 Jul 2023 15:07:03 +0100 Subject: [PATCH 134/238] ci: fix release.yaml pipeline --- cloudbuild/release.yaml | 56 +++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/cloudbuild/release.yaml b/cloudbuild/release.yaml index ea6d44d8..d9c93ce7 100644 --- a/cloudbuild/release.yaml +++ b/cloudbuild/release.yaml @@ -14,58 +14,48 @@ --- steps: - - id: build-training-image - name: gcr.io/kaniko-project/executor:latest + - id: build-container-images + name: gcr.io/cloud-builders/docker + dir: /workspace/model + entrypoint: sh args: - - --context=dir:///workspace/model - - --destination=${_DEV_VERTEX_LOCATION}-docker.pkg.dev/${_DEV_VERTEX_PROJECT_ID}/vertex-images/training:${TAG_NAME} - - --destination=${_TEST_VERTEX_LOCATION}-docker.pkg.dev/${_TEST_VERTEX_PROJECT_ID}/vertex-images/training:${TAG_NAME} - - --destination=${_PROD_VERTEX_LOCATION}-docker.pkg.dev/${_PROD_VERTEX_PROJECT_ID}/vertex-images/training:${TAG_NAME} - - --target=training - - --cache=true - - --compressed-caching=false - waitFor: ['-'] - - - id: build-serving-image - name: gcr.io/kaniko-project/executor:latest - args: - - --context=dir:///workspace/model - - --destination=${_DEV_VERTEX_LOCATION}-docker.pkg.dev/${_DEV_VERTEX_PROJECT_ID}/vertex-images/serving:${TAG_NAME} - - --destination=${_TEST_VERTEX_LOCATION}-docker.pkg.dev/${_TEST_VERTEX_PROJECT_ID}/vertex-images/serving:${TAG_NAME} - - --destination=${_PROD_VERTEX_LOCATION}-docker.pkg.dev/${_PROD_VERTEX_PROJECT_ID}/vertex-images/serving:${TAG_NAME} - - --target=serving - - --cache=true - - --compressed-caching=false - waitFor: ['-'] + - -c + - | + docker build --target=training -t training:latest . && \ + docker build --target=serving -t serving:latest . && \ + for proj in ${_DESTINATION_PROJECTS} ; do \ + docker tag training:latest ${_VERTEX_LOCATION}-docker.pkg.dev/$$proj/vertex-images/training:${TAG_NAME} && \ + docker tag serving:latest ${_VERTEX_LOCATION}-docker.pkg.dev/$$proj/vertex-images/serving:${TAG_NAME} && \ + docker push ${_VERTEX_LOCATION}-docker.pkg.dev/$$proj/vertex-images/training:${TAG_NAME} && \ + docker push ${_VERTEX_LOCATION}-docker.pkg.dev/$$proj/vertex-images/serving:${TAG_NAME}; \ + done # Install poetry, install deps, compile pipelines - - name: python:3.9 + - id: compile-and-publish-pipelines + name: python:3.9 entrypoint: /bin/sh args: - -c - | make setup && \ - make compile-pipeline pipeline=training && \ - make compile-pipeline pipeline=prediction && \ - cd pipelines && \ - for dest in ${_PIPELINE_PUBLISH_AR_PATHS} ; do \ + for proj in ${_DESTINATION_PROJECTS} ; do \ + CONTAINER_IMAGE_REGISTRY=${_VERTEX_LOCATION}-docker.pkg.dev/$$proj/vertex-images \ + make compile-pipeline pipeline=training && \ + make compile-pipeline pipeline=prediction && \ + cd pipelines && \ poetry run python -m pipelines.utils.upload_pipeline \ - --dest=$$dest \ + --dest=https://${_VERTEX_LOCATION}-kfp.pkg.dev/$$proj/vertex-pipelines \ --yaml=src/pipelines/training/pipeline.yaml \ --tag=latest \ --tag=${TAG_NAME} && \ poetry run python -m pipelines.utils.upload_pipeline \ - --dest=$$dest \ + --dest=https://${_VERTEX_LOCATION}-kfp.pkg.dev/$$proj/vertex-pipelines \ --yaml=src/pipelines/prediction/pipeline.yaml \ --tag=latest \ --tag=${TAG_NAME}; \ done env: - RESOURCE_SUFFIX=default - - CONTAINER_IMAGE_REGISTRY=${_TEST_VERTEX_LOCATION}-docker.pkg.dev/${_TEST_VERTEX_PROJECT_ID}/vertex-images - waitFor: - - build-training-image - - build-serving-image options: logging: CLOUD_LOGGING_ONLY From 130addc7786324c25ff221f1f465b97d60a66932 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Sat, 29 Jul 2023 10:24:35 +0100 Subject: [PATCH 135/238] test: add unit tests for new component --- .../src/vertex_components/lookup_model.py | 2 +- .../vertex-components/tests/conftest.py | 4 +- .../tests/test_upload_model.py | 340 ++++++++++++++++++ 3 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 components/vertex-components/tests/test_upload_model.py diff --git a/components/vertex-components/src/vertex_components/lookup_model.py b/components/vertex-components/src/vertex_components/lookup_model.py index 76b24ac1..ac73eeef 100644 --- a/components/vertex-components/src/vertex_components/lookup_model.py +++ b/components/vertex-components/src/vertex_components/lookup_model.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from kfp.v2.dsl import component, Output, Model +from kfp.dsl import component, Output, Model from typing import NamedTuple diff --git a/components/vertex-components/tests/conftest.py b/components/vertex-components/tests/conftest.py index 898c2590..f689a9b9 100644 --- a/components/vertex-components/tests/conftest.py +++ b/components/vertex-components/tests/conftest.py @@ -13,7 +13,7 @@ # limitations under the License. import pytest -import kfp.v2.dsl +import kfp.dsl @pytest.fixture(autouse=True) @@ -47,4 +47,4 @@ def _get_path(self): return self.uri # mock the _get_path method of Artifact which is used by the property path - monkeypatch.setattr(kfp.v2.dsl.Artifact, "_get_path", _get_path) + monkeypatch.setattr(kfp.dsl.Artifact, "_get_path", _get_path) diff --git a/components/vertex-components/tests/test_upload_model.py b/components/vertex-components/tests/test_upload_model.py new file mode 100644 index 00000000..42fba113 --- /dev/null +++ b/components/vertex-components/tests/test_upload_model.py @@ -0,0 +1,340 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import json +import logging +from unittest import mock +from kfp.dsl import Dataset, Metrics, Model +from google.protobuf.json_format import ParseDict +from google.cloud.aiplatform_v1 import ModelEvaluation +from google_cloud_pipeline_components.types.artifact_types import VertexModel + + +import vertex_components + +upload_model = vertex_components.upload_model.python_func + + +@mock.patch("google.cloud.aiplatform_v1.ModelServiceClient") +@mock.patch("google.cloud.aiplatform.Model") +def test_model_upload_no_champion( + mock_model_class, mock_model_service_client, caplog, tmp_path +): + + caplog.set_level(logging.INFO) + + metrics_json = json.dumps( + { + "problemType": "regression", + "auc": 0.4, + "accuracy": 0.80, + } + ) + metrics_file_path = tmp_path / "metrics.json" + metrics_file_path.write_text(metrics_json) + + mock_model_class.list.return_value = [] + + model = Model(uri="dummy-model-uri") + serving_container_image = "dummy_image:latest" + model_name = "dummy-model-name" + vertex_model = VertexModel.create( + name=model_name, uri="chall_uri", model_resource_name="chall_resource_name" + ) + project = "dummy-project" + location = "dummy-location" + model_evaluation = Metrics(uri="") + model_evaluation.path = str(metrics_file_path) + eval_metric = "auc" + eval_lower_is_better = False + + pipeline_job_id = "dummy-pipeline-job-id" + test_dataset = Dataset(uri="test-dataset-uri") + evaluation_name = "dummy evaluation name" + order_models_by = "dummy_order_models_by" + + upload_model( + model=model, + serving_container_image=serving_container_image, + vertex_model=vertex_model, + project=project, + location=location, + model_evaluation=model_evaluation, + eval_metric=eval_metric, + eval_lower_is_better=eval_lower_is_better, + model_name=model_name, + pipeline_job_id=pipeline_job_id, + test_dataset=test_dataset, + evaluation_name=evaluation_name, + order_models_by=order_models_by, + ) + + # Check that model lookup is performed, and no existing model is found + mock_model_class.list.assert_called_once_with( + filter=f'display_name="{model_name}"', + order_by=order_models_by, + location=location, + project=project, + ) + assert "found 0 models" in caplog.text + + # Check no model comparison occurs + assert "wins" not in caplog.text + + # Check model upload call + mock_model_class.upload.assert_called_once_with( + display_name=model_name, + artifact_uri="dummy-model-uri", + serving_container_image_uri=serving_container_image, + serving_container_predict_route="/predict", + serving_container_health_route="/healthz", + parent_model=None, + is_default_version=True, + ) + + # check model output URI + assert vertex_model.uri == f"https://{location}-aiplatform.googleapis.com/v1/" + ( + str(mock_model_class.upload.return_value.versioned_resource_name) + ) + + # check evaluation import + mock_model_service_client.return_value.import_model_evaluation.assert_called_once_with( # noqa + parent=mock_model_class.upload.return_value.versioned_resource_name, + model_evaluation=mock.ANY, + ) + + +@mock.patch("google.cloud.aiplatform_v1.ModelServiceClient") +@mock.patch("google.cloud.aiplatform.Model") +def test_model_upload_challenger_wins( + mock_model_class, mock_model_service_client, caplog, tmp_path +): + + caplog.set_level(logging.INFO) + + metrics_json = json.dumps( + { + "problemType": "regression", + "auc": 0.4, + "accuracy": 0.80, + } + ) + metrics_file_path = tmp_path / "metrics.json" + metrics_file_path.write_text(metrics_json) + + # create mock champion model + mock_champion_model = mock.Mock() + mock_champion_model.version_id = "123" + dummy_champion_eval = ModelEvaluation() + dummy_champion_metrics = { + "auc": 0.2, + "f1": 0.7, + } + message_dict = { + "displayName": "Previously imported evaluation", + "metricsSchemaUri": "gs://google-cloud-aiplatform/schema/modelevaluation/regression_metrics_1.0.0.yaml", # noqa + "metrics": dummy_champion_metrics, + "metadata": { + "pipeline_job_id": "dummy-pipeline-id", + "evaluation_dataset_type": "gcs", + "evaluation_dataset_path": ["dummy-gcs-uri"], + }, + } + ParseDict(message_dict, dummy_champion_eval._pb) + mock_champion_model.get_model_evaluation.return_value._gca_resource = ( + dummy_champion_eval + ) + mock_champion_model.resource_name = "dummy-champion-resource-name" + mock_model_class.list.return_value = [mock_champion_model] + + # create mock challenger model + model = Model(uri="dummy-model-uri") + serving_container_image = "dummy_image:latest" + model_name = "dummy-model-name" + vertex_model = VertexModel.create( + name=model_name, uri="chall_uri", model_resource_name="chall_resource_name" + ) + project = "dummy-project" + location = "dummy-location" + model_evaluation = Metrics(uri="") + model_evaluation.path = str(metrics_file_path) + eval_metric = "auc" + eval_lower_is_better = False + + pipeline_job_id = "dummy-pipeline-job-id" + test_dataset = Dataset(uri="test-dataset-uri") + evaluation_name = "dummy evaluation name" + order_models_by = "dummy_order_models_by" + + upload_model( + model=model, + serving_container_image=serving_container_image, + vertex_model=vertex_model, + project=project, + location=location, + model_evaluation=model_evaluation, + eval_metric=eval_metric, + eval_lower_is_better=eval_lower_is_better, + model_name=model_name, + pipeline_job_id=pipeline_job_id, + test_dataset=test_dataset, + evaluation_name=evaluation_name, + order_models_by=order_models_by, + ) + + # Check that model lookup is performed, and no existing model is found + mock_model_class.list.assert_called_once_with( + filter=f'display_name="{model_name}"', + order_by=order_models_by, + location=location, + project=project, + ) + assert "is being challenged by new model" in caplog.text + + # Check challenger wins in model comparison + assert "Challenger wins!" in caplog.text + + # Check model upload call + mock_model_class.upload.assert_called_once_with( + display_name=model_name, + artifact_uri="dummy-model-uri", + serving_container_image_uri=serving_container_image, + serving_container_predict_route="/predict", + serving_container_health_route="/healthz", + parent_model=mock_champion_model.resource_name, + is_default_version=True, + ) + + # check model output URI + assert vertex_model.uri == f"https://{location}-aiplatform.googleapis.com/v1/" + ( + str(mock_model_class.upload.return_value.versioned_resource_name) + ) + + # check evaluation import + mock_model_service_client.return_value.import_model_evaluation.assert_called_once_with( # noqa + parent=mock_model_class.upload.return_value.versioned_resource_name, + model_evaluation=mock.ANY, + ) + + +@mock.patch("google.cloud.aiplatform_v1.ModelServiceClient") +@mock.patch("google.cloud.aiplatform.Model") +def test_model_upload_champion_wins( + mock_model_class, mock_model_service_client, caplog, tmp_path +): + + caplog.set_level(logging.INFO) + + metrics_json = json.dumps( + { + "problemType": "regression", + "auc": 0.4, + "accuracy": 0.80, + } + ) + metrics_file_path = tmp_path / "metrics.json" + metrics_file_path.write_text(metrics_json) + + # Create mock champion model + mock_champion_model = mock.Mock() + mock_champion_model.version_id = "123" + dummy_champion_eval = ModelEvaluation() + dummy_champion_metrics = { + "auc": 0.8, + "f1": 0.7, + } + message_dict = { + "displayName": "Previously imported evaluation", + "metricsSchemaUri": "gs://google-cloud-aiplatform/schema/modelevaluation/regression_metrics_1.0.0.yaml", # noqa + "metrics": dummy_champion_metrics, + "metadata": { + "pipeline_job_id": "dummy-pipeline-id", + "evaluation_dataset_type": "gcs", + "evaluation_dataset_path": ["dummy-gcs-uri"], + }, + } + ParseDict(message_dict, dummy_champion_eval._pb) + mock_champion_model.get_model_evaluation.return_value._gca_resource = ( + dummy_champion_eval + ) + mock_champion_model.resource_name = "dummy-champion-resource-name" + mock_model_class.list.return_value = [mock_champion_model] + + # create mock challenger model + model = Model(uri="dummy-model-uri") + serving_container_image = "dummy_image:latest" + model_name = "dummy-model-name" + vertex_model = VertexModel.create( + name=model_name, uri="chall_uri", model_resource_name="chall_resource_name" + ) + project = "dummy-project" + location = "dummy-location" + model_evaluation = Metrics(uri="") + model_evaluation.path = str(metrics_file_path) + eval_metric = "auc" + eval_lower_is_better = False + + pipeline_job_id = "dummy-pipeline-job-id" + test_dataset = Dataset(uri="test-dataset-uri") + evaluation_name = "dummy evaluation name" + order_models_by = "dummy_order_models_by" + + upload_model( + model=model, + serving_container_image=serving_container_image, + vertex_model=vertex_model, + project=project, + location=location, + model_evaluation=model_evaluation, + eval_metric=eval_metric, + eval_lower_is_better=eval_lower_is_better, + model_name=model_name, + pipeline_job_id=pipeline_job_id, + test_dataset=test_dataset, + evaluation_name=evaluation_name, + order_models_by=order_models_by, + ) + + # Check that model lookup is performed, and no existing model is found + mock_model_class.list.assert_called_once_with( + filter=f'display_name="{model_name}"', + order_by=order_models_by, + location=location, + project=project, + ) + assert "is being challenged by new model" in caplog.text + + # Check champion wins in model comparison + assert "Champion wins!" in caplog.text + + # Check model upload call + mock_model_class.upload.assert_called_once_with( + display_name=model_name, + artifact_uri="dummy-model-uri", + serving_container_image_uri=serving_container_image, + serving_container_predict_route="/predict", + serving_container_health_route="/healthz", + parent_model=mock_champion_model.resource_name, + is_default_version=False, + ) + + # check model output URI + assert vertex_model.uri == f"https://{location}-aiplatform.googleapis.com/v1/" + ( + str(mock_model_class.upload.return_value.versioned_resource_name) + ) + + # check evaluation import + mock_model_service_client.return_value.import_model_evaluation.assert_called_once_with( # noqa + parent=mock_model_class.upload.return_value.versioned_resource_name, + model_evaluation=mock.ANY, + ) From d28e2b2de15a6ef03d121990732f89e6b14d6bc9 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Sat, 29 Jul 2023 10:38:13 +0100 Subject: [PATCH 136/238] docs: update copyright headers --- .../src/vertex_components/upload_model.py | 10 +++++----- .../vertex-components/tests/test_upload_model.py | 2 +- model/Dockerfile | 13 +++++++++++++ model/cloudbuild.yaml | 13 +++++++++++++ model/serving/main.py | 13 +++++++++++++ model/training/train.py | 13 +++++++++++++ pipelines/src/pipelines/utils/upload_pipeline.py | 13 +++++++++++++ 7 files changed, 71 insertions(+), 6 deletions(-) diff --git a/components/vertex-components/src/vertex_components/upload_model.py b/components/vertex-components/src/vertex_components/upload_model.py index 4f3b5729..fad15e3e 100644 --- a/components/vertex-components/src/vertex_components/upload_model.py +++ b/components/vertex-components/src/vertex_components/upload_model.py @@ -1,11 +1,11 @@ -# Copyright 2022 Google LLC - +# Copyright 2023 Google LLC +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at - -# https://www.apache.org/licenses/LICENSE-2.0 - +# +# http://www.apache.org/licenses/LICENSE-2.0 +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/components/vertex-components/tests/test_upload_model.py b/components/vertex-components/tests/test_upload_model.py index 42fba113..d62e0bc9 100644 --- a/components/vertex-components/tests/test_upload_model.py +++ b/components/vertex-components/tests/test_upload_model.py @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/model/Dockerfile b/model/Dockerfile index 24e387bd..db0b33cd 100644 --- a/model/Dockerfile +++ b/model/Dockerfile @@ -1,3 +1,16 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. FROM python:3.9.16-slim AS builder ENV PIP_NO_CACHE_DIR=off \ diff --git a/model/cloudbuild.yaml b/model/cloudbuild.yaml index 167ceb0f..43998648 100644 --- a/model/cloudbuild.yaml +++ b/model/cloudbuild.yaml @@ -1,3 +1,16 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. --- steps: - name: 'gcr.io/kaniko-project/executor:latest' diff --git a/model/serving/main.py b/model/serving/main.py index 3b264b02..32f76450 100644 --- a/model/serving/main.py +++ b/model/serving/main.py @@ -1,3 +1,16 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import joblib import os diff --git a/model/training/train.py b/model/training/train.py index 18486e2d..a5cf8544 100644 --- a/model/training/train.py +++ b/model/training/train.py @@ -1,3 +1,16 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import argparse from pathlib import Path diff --git a/pipelines/src/pipelines/utils/upload_pipeline.py b/pipelines/src/pipelines/utils/upload_pipeline.py index d5137f41..b9524c51 100644 --- a/pipelines/src/pipelines/utils/upload_pipeline.py +++ b/pipelines/src/pipelines/utils/upload_pipeline.py @@ -1,3 +1,16 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import argparse from kfp.registry import RegistryClient From 0d778b4ca5a57b822b57f8b6b8d97a3a0949755a Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Sat, 29 Jul 2023 10:41:47 +0100 Subject: [PATCH 137/238] test: revert accidental changes to test_lookup_model --- .../tests/test_lookup_model.py | 129 ++++++++---------- 1 file changed, 55 insertions(+), 74 deletions(-) diff --git a/components/vertex-components/tests/test_lookup_model.py b/components/vertex-components/tests/test_lookup_model.py index 1ba31f81..5e0b2e4c 100644 --- a/components/vertex-components/tests/test_lookup_model.py +++ b/components/vertex-components/tests/test_lookup_model.py @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import google.cloud.aiplatform # noqa -from kfp.v2.dsl import Model +from kfp.dsl import Model from unittest import mock import pytest @@ -22,93 +21,75 @@ lookup_model = vertex_components.lookup_model.python_func -def test_lookup_model(tmpdir): +@mock.patch("google.cloud.aiplatform.Model") +def test_lookup_model(mock_model, tmpdir): """ Assert lookup_model produces expected resource name, and that list method is called with the correct arguemnts - - Args: - tmpdir: built-in pytest tmpdir fixture - - Returns: - None """ - with mock.patch("google.cloud.aiplatform.Model") as mock_model: - - # Mock attribute and method - - mock_path = tmpdir - mock_model.resource_name = "my-model-resource-name" - mock_model.uri = mock_path - mock_model.list.return_value = [mock_model] - - # Invoke the model look up - found_model_resource_name, _ = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - model=Model(uri=mock_path), - ) - - assert found_model_resource_name == "my-model-resource-name" - - # Check the list method was called once with the correct arguments - mock_model.list.assert_called_once_with( - filter='display_name="my-model"', - order_by="create_time desc", - location="europe-west4", - project="my-project-id", - ) - -def test_lookup_model_when_no_models(tmpdir): + # Mock attribute and method + mock_path = tmpdir + mock_model.resource_name = "my-model-resource-name" + mock_model.uri = mock_path + mock_model.list.return_value = [mock_model] + + # Invoke the model look up + found_model_resource_name, _ = lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=False, + model=Model(uri=mock_path), + ) + + assert found_model_resource_name == "my-model-resource-name" + + # Check the list method was called once with the correct arguments + mock_model.list.assert_called_once_with( + filter='display_name="my-model"', + order_by="create_time desc", + location="europe-west4", + project="my-project-id", + ) + + +@mock.patch("google.cloud.aiplatform.Model") +def test_lookup_model_when_no_models(mock_model, tmpdir): """ Checks that when there are no models and fail_on_model_found = False, lookup_model returns an empty string. - - Args: - tmpdir: built-in pytest tmpdir fixture - - Returns: - None """ - with mock.patch("google.cloud.aiplatform.Model") as mock_model: - mock_model.list.return_value = [] - exported_model_resource_name, _ = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - model=Model(uri=str(tmpdir)), - ) + mock_model.list.return_value = [] + exported_model_resource_name, _ = lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=False, + model=Model(uri=str(tmpdir)), + ) + print(exported_model_resource_name) assert exported_model_resource_name == "" -def test_lookup_model_when_no_models_fail(tmpdir): +@mock.patch("google.cloud.aiplatform.Model") +def test_lookup_model_when_no_models_fail(mock_model, tmpdir): """ Checks that when there are no models and fail_on_model_found = True, lookup_model raises a RuntimeError. - - Args: - tmpdir: built-in pytest tmpdir fixture - - Returns: - None """ - with mock.patch("google.cloud.aiplatform.Model") as mock_model: - mock_model.list.return_value = [] + mock_model.list.return_value = [] - # Verify that a ValueError is raised - with pytest.raises(RuntimeError): - lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=True, - model=Model(uri=str(tmpdir)), - ) + # Verify that a ValueError is raised + with pytest.raises(RuntimeError): + lookup_model( + model_name="my-model", + project_location="europe-west4", + project_id="my-project-id", + order_models_by="create_time desc", + fail_on_model_not_found=True, + model=Model(uri=str(tmpdir)), + ) From 8e138cddd070eecb60a812b8a8837968450bb310 Mon Sep 17 00:00:00 2001 From: Jonny Browning Date: Tue, 1 Aug 2023 14:00:30 +0100 Subject: [PATCH 138/238] docs: update documentation and Makefile --- CONTRIBUTING.md | 273 -------------------------- Makefile | 4 +- README.md | 209 ++++++++++---------- USAGE.md | 72 ------- cloudbuild/README.md | 33 ++-- docs/PRODUCTION.md | 40 ++-- docs/TESTING_SETUP.md | 36 +++- docs/TROUBLESHOOTING.md | 46 ----- docs/images/architecture.png | Bin 0 -> 247418 bytes docs/images/cf_view.png | Bin 80012 -> 0 bytes docs/images/prediction_pipeline.png | Bin 0 -> 96747 bytes docs/images/tf_model_architecture.png | Bin 248682 -> 0 bytes docs/images/training_pipeline.png | Bin 0 -> 162349 bytes docs/images/use_template.png | Bin 3127 -> 0 bytes pipelines/README.md | 142 ++++++-------- 15 files changed, 230 insertions(+), 625 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 USAGE.md delete mode 100644 docs/TROUBLESHOOTING.md create mode 100644 docs/images/architecture.png delete mode 100644 docs/images/cf_view.png create mode 100644 docs/images/prediction_pipeline.png delete mode 100644 docs/images/tf_model_architecture.png create mode 100644 docs/images/training_pipeline.png delete mode 100644 docs/images/use_template.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 905de864..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,273 +0,0 @@ - - # Contributing - -Welcome to the Vertex Templates Project, and thank you for your interest in contributing! - -This guide is chiefly for users wishing to contribute to the opensource version. Those who want to edit the templates to suit their own purposes should look at [USAGE](USAGE.md), but may find some sections useful. - -## Links to Important Resources -- [pytest](https://docs.pytest.org) -- [unittest.mock](https://docs.python.org/3/library/unittest.mock.html) -- [poetry](https://python-poetry.org/docs/#installation) -- [Kubeflow Pipelines](https://www.kubeflow.org/docs/components/pipelines/overview) -- [Vertex AI](https://cloud.google.com/vertex-ai/docs) -- [AI Platform SDK](https://googleapis.dev/python/aiplatform/latest/index.html) - -## Python code guidelines - -### Code linting and formatting -We use linting to highlight problems in our Python code which could later produce errors or affect efficiency. For example, linting detects things such as uninitialised or undefined variables, unused imported modules, and missing parentheses. To detect and enact fixes to these problems, we make use of the Python linter: [Flake8](https://flake8.pycqa.org/en/latest/) and the formatter [Black](https://black.readthedocs.io/en/stable/). These are run as part of our [pre-commit checks](#Pre-commit-checks). We have some configurations for Flake8 that you can find [here](.flake8). -### Docstring format -Please include docstrings that are compliant with the [Google Style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html). - -In brief, the docstring is a triple quoted string placed immediately after the function definition and should contain: -- A brief description of the function -- A section `Args:` where you list the function arguments - - For each argument, include the argument type in brackets -- A section `Returns:` where you list what the function returns - - For each returned item, include the type -## Testing -Testing is an important way for us to check that our code works as expected and forms a key part of our CI/CD strategy. For example, pull requests will not pass our checks if any unit tests fail. You should therefore ensure that your pull request passes all tests before merging. - -### Unit Tests - -We use unit tests to test small sections of logically isolated code in our pipeline components. - -#### Mocking & patching -When we test a function that makes an external API call (for example to a service on GCP), we mock the associated module or class and its functions and attributes. The rationale behind this is that we want to test our own logic and not the API call(s) themselves. Indeed, API calls can be broken, computationally expensive, slow, or limited, and we do not want our unit tests to fail because of any of these. - -We do mocking/patching in two different ways: -1. [`monkeypatch`](https://docs.pytest.org/en/6.2.x/monkeypatch.html): this built-in `pytest` fixture allows you to modify an attribute (such as an instance method in a class). We use `monkeypatch` only in the relevant `conftest.py` file for the fixtures that are applied before every unit test. We have used `monkeypatch` in the following fixtures: - - `mock_kfp_artifact`: used to mock the `Artifact` object (and thus any derived classes such as `Dataset`, `Model`, etc.) in `kfp.v2.dsl` to return the URI as - the path. This lets us create mock Artifact objects locally for our unit tests. -2. `unittest.mock.patch`: this object in the `unittest` library enables us to mock classes (and its associated attributes and methods) within a context manager. We use `mock.patch` inside the individual test scripts to mock object(s) that are used in the function being tested. This allows us to replace the target class/object with a Mock object, ultimately allowing us to make assertions on how this Mock object has been used. For example, the `assert_called_once_with` method allows us to check that a specific method of a Mock object was called once with specific arguments. Alternatively, we can set the attributes of our Mock objects to specific values, and assert that the component logic being tested handles these cases correctly (e.g. by raising a `ValueError`). An example of using the `mock.patch` context manager is in [`test_lookup_model.py`](tests/kfp_components/aiplatform/test_lookup_model.py) for [`lookup_model.py`](./pipeline_components/aiplatform/aiplatform/lookup_model/component.py), where there is an API call to list models (in Vertex AI) based on a filter, namely `google.cloud.aiplatform.Model.list`. When we test this KFP component, we are not interested in actually making this API call, so instead we mock it. We do this by mocking the `google.cloud.aiplatform.Model` class: -``` -with mock.patch("google.cloud.aiplatform.Model") as mock_model: - - # Mock attribute and method - mock_model.resource_name = "my-model-resource-name" - mock_model.list.return_value = [mock_model] - - # Invoke the model look up - found_model_resource_name = lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project_id", - order_models_by="create_time desc", - fail_on_model_not_found=False, - ) - - assert found_model_resource_name == "my-model-resource-name" -``` - -#### `pytest` fixtures & `conftest.py` -- `tmpdir`: a built-in `pytest` fixture that we use to create temporary paths for our unit tests. -- `monkeypatch`: see above. -- The file `conftest.py` contains our custom `pytest` fixtures which we apply for each testing session. - -#### How to write unit tests -Some things to consider testing for in your code: -- **Do you have any logical conditions?** Consider writing tests that assert the desired outcome occurs for each possible outcome. In particular, you can assert that a certain error is raised in your function under certain conditions. For example, in [`lookup_model.py`](./pipeline_components/aiplatform/aiplatform/lookup_model/component.py), the function `lookup_model` raises a `RuntimeError` if no models are found: -``` -if fail_on_model_not_found: - raise RuntimeError(f"Failed as model not found") -``` -Now in [`test_lookup_model.py`](./pipeline_components/aiplatform/tests/test_lookup_model.py), in the unit test `test_lookup_model_when_no_models_fail` you can use `pytest.raises` to check that `lookup_model` actually raises a `RuntimeError`: -``` -with pytest.raises(RuntimeError): - lookup_model( - model_name="my-model", - project_location="europe-west4", - project_id="my-project-id", - order_models_by="create_time desc", - fail_on_model_not_found=True, - ) -``` - -#### How to run unit tests -Unit tests for pipeline components are run automatically on each pull request. You can also run them on your local machine: -``` -make test-all-components -``` - -Or to just run the unit tests for a given component group (e.g. `aiplatform`): -``` -make test-components GROUP=vertex-components -``` - -### End-to-end (E2E) pipeline tests -We use End-to-end (E2E) pipeline tests to ensure that our pipelines are running as expected. Our E2E tests ensure: -- That the pipeline is successfully triggered locally, and that the pipeline run is completed -- That common tasks(components), which are stored in a dictionary object (`common_tasks`), occurred in the pipeline -- That if any task in a conditional tasks dictionary object occurred in the pipeline, the remaining tasks based on that condition should have all occurred as well -- That these pipeline tasks output the correct artifacts, by checking whether they have been saved to a GCS URI or have been generated successfully in Vertex AI. - -Note: -These dictionary objects (`common_tasks`, `conditional_tasks`) are defined in `test_e2e.py` in each pipeline folder e.g (`./pipelines/tests/xgboost/training/test_e2e.py`). -The E2E test only allows one common tasks group but the number of conditional tasks group is not limited. To define the correct task group, -please go to pipeline job on Vertex AI for more information. -For example, in the XGBoost training pipeline, we have two conditional tasks groups that are bounded in the dashed frame. -Thus, in `./pipelines/tests/xgboost/training/test_e2e.py`, there are two dictionaries of two conditional tasks group. - -- Optionally check for executed tasks and created output artifacts. - -#### How to run end-to-end (E2E) pipeline tests -E2E tests are run on each PR that is merged to the main branch. You can also run them on your local machine: -``` -make e2e-tests pipeline= -``` - -### How to adapt the end-to-end (E2E) pipeline tests for your own pipeline -As described above, we provide our E2E tests with a dictionary of expected outputs for the pipeline components, and confirm that these outputs are stored in a GCS uri or generated successfully in Vertex AI. - For information on how tasks and outputs are stored in your pipeline, we recommend looking at these [AI Platform documents](https://googleapis.dev/python/aiplatform/latest/aiplatform_v1beta1/types.html#google.cloud.aiplatform_v1beta1.types.PipelineJob). The following briefly describes how we created this dictionary, and you can use this to create your own dictionary of expected tasks: -1. Trigger a pipeline (using the default pipeline input parameters), and collect the pipeline tasks and their details. For example: -``` -from trigger.main import trigger_pipeline_from_payload - -... - - payload = { - "attributes": { - "template_path": template_path, - "enable_caching": False - } - } - pl = trigger_pipeline_from_payload(payload) - pl.wait() - details = pl.to_dict() - tasks = details["jobDetail"]["taskDetails"] - -``` -2. Check missing tasks by comparing the actual task produced in pipeline and the expected tasks defined in each pipeline test file ( e.g `./pipelines/tests/xgboost/training/test_e2e.py`)) -``` - missing_tasks = [ - task_name - for task_name in expected_tasks.keys() - if task_name not in actual_tasks.keys() - ] -``` -3. Check all tasks outputs are as expected and accessible -``` - storage_client = storage.Client() - # 2. Missing outputs check - for task_name, expected_output in expected_tasks.items(): - actual_outputs = actual_tasks[task_name] - # 2-2. if the output artifact are as expected - diff = set(expected_output).symmetric_difference(actual_outputs.keys()) - assert ( - len(diff) == 0 - ), f"task: {task_name}, expected_output {expected_output}, actual_outputs: {actual_outputs.keys()}" - for output_artifact in expected_output: - output_uri = actual_outputs[output_artifact] - - # 2-2. if output is generated successfully - # for all gcs uri check its file size - if output_uri.startswith("gs://"): - file_size = test_gcs_uri(output_uri, storage_client) - assert ( - file_size > 0 - ), f"{output_artifact} in task {task_name} is not accessible" - # for Vertex resource check if the resource exists - else: - # all Vertex AI resource uri following this pattern: - # ‘projects//locations///’ - artifact_type = output_uri.split('/')[-2] - object_existence = test_functions[artifact_type](output_uri.split('/')[-1]) - assert ( - object_existence - ), f"{output_artifact} in task {task_name} is not accessible" - -``` -Notes: -This template only provides three Vertex AI resource check functions: `test_vertex_model_uri`,`test_vertex_endpoint_uri`,`test_batch_prediction_job_uri`. -For other Vertex artifacts, you can create a new one following this code: -``` -def test_vertex_endpoint_uri(output_uri: str): - from google.cloud.aiplatform import Model - try: - Model( - endpoint_name=output_uri, - project=project_id, - location=project_location, - ) - return True - except: - return False - -``` - -## Adding or changing python dependencies -We use [poetry](https://python-poetry.org/docs/#installation) to handle our packages and their dependencies. Each group of pipeline components (e.g. [vertex](./components/vertex-components/)) includes its own poetry environment, and there is a [separate poetry environment](./pipelines/) for the ML pipelines themselves and the pipeline trigger code. - -### Adding python dependencies -You may need to add new packages for your own use cases. To do this, run the following from the relevant directory ([pipelines](./pipelines) for the main ML pipeline dependencies or the directory of the relevant component group e.g. [aiplatform](./pipeline_components/aiplatform/)): -``` -poetry install -``` - -## Committing Changes - -### Add changed files to staging area -``` -git add -``` -### Commit messages -To allow others (and yourself!) to understand your changes easily, we encourage writing detailed commit messages. We use [Commitizen](https://commitizen-tools.github.io/commitizen/) as a guide. In brief, all commit messages should start with one of the following prefixes: -- **feat**: A new feature -- **fix**: A bug fix -- **docs**: Documentation only changes -- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons etc) -- **refactor**: A change that neither fixes a bug or adds a feature -- **perf**: A change that improves performance -- **test**: Add new tests or correct existing tests -- **build**: Changes that affect the build system or external dependencies (e.g. pip, docker, npm) -- **ci**: Changes to CI configuration files and scripts - -After the prefix, you can specify the scope (e.g. the class or file name) of the change in brackets. Finally, you include the details of the change. For example -``` -git commit -s -m"prefix(scope): message here" -``` - -### Things to avoid in your commit messages: -- Too brief or non-specific messages (e.g. `bug fix`, `small changes`) -- Commit message does not explain why changes were made - - -### Pre-commit checks -We use pre-commit hooks to automatically identify and correct issues in code. You can find the details of the hooks we use [here](.pre-commit-config.yaml). If any of these fail, the commit will be unsuccessful and you will be able to see which hook failed and some additional details in your terminal. - -To run the pre-commit hooks over the entire repo, run: -``` -make pre-commit -``` - -#### What to do if pre-commit checks fail: -- **Checks fail and the pre-commit hook edits a file to fix the error**. Sometimes the pre-commit hook will identify an error (e.g. the absence of a blank line at the end of a file), and will correct this automatically by changing your file. You will then need to re-add these files to the staging area before trying to commit again. -- **Checks fail and displays an error message**. Some errors cannot be automatically fixed by pre-commit hooks, and instead they will display the error number and the file and line which failed. For more details beyond the error message, you can look up the error number online. The most common errors are caused by lines which exceed the character limit. Once you identify the cause of the error, you will need to fix this in your code, add the edited file to the staging area, and then commit again. - -### Commit changes to Python packages and dependencies -If you have changes to `pyproject.toml` and `poetry.lock`, please make sure you commit these files! - -## Makefile -This project contains a [Makefile](Makefile) which contains "rules" describing the commands to be executed by the system. These allow you to quickly and easily run commands for specific purposes, for example running all of the unit-tests, or compiling a pipeline. You can find the full set of available `make` rules by running: -``` -make help -``` - -Some of these rules use the environment variables specified in [`env.sh`](env.sh). - -**It is not expected that you will need to change the Makefile or create a new one.** diff --git a/Makefile b/Makefile index 72adc2a1..13020ced 100644 --- a/Makefile +++ b/Makefile @@ -24,9 +24,7 @@ pre-commit: ## Runs the pre-commit checks over entire repo poetry run pre-commit run --all-files setup: ## Set up local environment for Python development on pipelines - @pip install pip --upgrade && \ - pip install poetry --upgrade && \ - cd pipelines && \ + @cd pipelines && \ poetry install --with dev test-trigger: ## Runs unit tests for the pipeline trigger code diff --git a/README.md b/README.md index efc9196c..09022920 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ - # Vertex Pipelines End-to-end Samples -## Introduction - -This repository provides a reference implementation of [Vertex Pipelines](https://cloud.google.com/vertex-ai/docs/pipelines/) for creating a production-ready MLOps solution on Google Cloud. - -### Why does this matter? - -It is hard to productionalize data science use cases, especially because the journey from experimentation is lacking standardisation. -Thus, this project aims at boosting, scalability, productivity and standardisation of data science use cases amongst data science teams. -As a result, this will free up time for data scientists so that they can focus on data science with minimal engineering overhead. - -This project bundles reusable code and provides the creation of a MLOps platform via an template-driven approach allowing to: +# Vertex Pipelines End-to-end Samples -- **Create a new use case from a template.** Create a new ML training pipeline and batch prediction pipeline based on a template. -- **Execute a one-off pipeline run in a sandbox environment.** Try out a pipeline during the development cycle in a sandbox (development) environment. -- **Deploy a pipeline to a production environment.** Deploy a new or updated pipeline to a production environment allowing for orchestration, schedules and triggers. -- **Publish a new template.** Customize or extend the existing templates to create a new pipeline template that can be used by the data scientists on the team to support new use cases. +_AKA "Vertex AI Turbo Templates"_ -As such, this project includes the infrastructure on Google Cloud, a CI/CD integration and existing templates to support training and prediction pipelines for common ML frameworks such as TensorFlow and XGBoost. -To showcase the pipelines in action, the [Public Chicago Taxi Trips Dataset](https://console.cloud.google.com/bigquery?p=bigquery-public-data&d=chicago_taxi_trips&page=dataset) is used to predict the total fare of a taxi trip in Chicago. - -### Cloud Architecture - -Vertex AI Pipelines is a serverless orchestrator for running ML pipelines, using either the KFP SDK or TFX. However, unlike Kubeflow Pipelines, it does not have a built-in mechanism for saving Pipelines so that they can be run later, either on a schedule or via an external trigger. Instead, every time you want to run an ML pipeline in Vertex AI, you must make the API call to Vertex AI Pipelines, including the full pipeline definition, and Vertex Pipelines will run the pipeline there and then. - -In a production MLOps solution, your ML pipelines need to be repeatable. So, we have created a Cloud Function to trigger the execution of ML pipelines on Vertex AI. This can be done either using a schedule (via Cloud Scheduler), or from an external system using Pub/Sub. We use Cloud Build to compile the pipelines using the KFP SDK, and publish them to a GCS bucket. The Cloud Function retrieves the pipeline definition from the bucket, and triggers an execution of the pipeline in Vertex AI. - -![Using a Cloud Function to trigger Vertex Pipelines](docs/images/cf_view.png) +## Introduction -## Getting started +This repository provides a reference implementation of [Vertex Pipelines](https://cloud.google.com/vertex-ai/docs/pipelines/) for creating a production-ready MLOps solution on Google Cloud. +You can take this repository as a starting point you own ML use cases. The implementation includes: -### Prerequisites +* Infrastructure-as-Code using Terraform for a typical dev/test/prod setup of Vertex AI and other relevant services +* ML training and batch prediction pipelines using the Kubeflow Pipelines SDK for an example use case (using the [Chicago Taxi Trips Dataset](https://console.cloud.google.com/bigquery?p=bigquery-public-data&d=chicago_taxi_trips&page=dataset)) +* Reusable KFP components that can be used in ML pipelines +* CI/CD using Google Cloud Build for linting, testing, and deploying ML pipelines +* Developer scripts (Makefile, Python scripts etc) -- [pyenv](https://github.com/pyenv/pyenv#installation) for managing Python versions -- [Cloud SDK](https://cloud.google.com/sdk/docs/quickstart) -- Make +## Cloud Architecture -### Local setup +The diagram below shows the cloud architecture for this repository. -1. Clone the repository locally -1. Install Python: `pyenv install` -1. Install poetry and poetry dependencies: `make setup` -1. Install pre-commit hooks: `cd pipelines && poetry run pre-commit install` -1. Copy `env.sh.example` to `env.sh`, and update the environment variables in `env.sh` -1. Load the environment variables in `env.sh` by running `source env.sh` +![Cloud Architecture diagram](/docs/images/architecture.png) -Note: `poetry install` or `poetry add`, installs packages within the project's virtual environment. -If you use `pip` directly, you might accidentally install packages globally or in the wrong environment, leading to conflicts or difficulties in managing dependencies. +There are four different Google Cloud projects in use -### Configuring poetry to detect python version using pyenv +* `dev` - a shared sandbox environment for use during development +* `test` - environment for testing new changes before they are promoted to production. This environment should be treated as much as possible like a production environment. +* `prod` - production environment +* `admin` - separate Google Cloud project for setting up CI/CD in Cloud Build (since the CI/CD pipelines operate across the different environments) -1. Run `poetry config virtualenvs.prefer-active-python true` -1. Install project dependencies using `poetry install` +Vertex Pipelines are scheduled using Google Cloud Scheduler. Cloud Scheduler emits a Pub/Sub message that triggers a Cloud Function, which in turn triggers the Vertex Pipeline to run. _In future, this will be replaced with the Vertex Pipelines Scheduler (once there is a Terraform resource for it)._ -### Deploying Cloud Infrastructure +## Infrastructure The cloud infrastructure is managed using Terraform and is defined in the [`terraform`](terraform) directory. There are three Terraform modules defined in [`terraform/modules`](terraform/modules): @@ -78,22 +54,31 @@ The cloud infrastructure is managed using Terraform and is defined in the [`terr There is a Terraform configuration for each environment (dev/test/prod) under [`terraform/envs`](terraform/envs/). -#### Deploying only the dev environment +How to deploy this infrastructure is covered in a [later section](#deploying-infrastructure). -We recommend that you set up CI/CD to deploy your environments. However, if you would prefer to deploy the dev environment manually (for example to try out the repo), you can do so as follows: +## Developer setup -(Assuming you have an empty Google Cloud project where you are an Owner) +### Prerequisites -1. Install Terraform on your local machine. We recommend using [`tfswitch`](https://tfswitch.warrensbox.com/) to automatically choose and download an appropriate version for you (run `tfswitch` from the [`terraform/envs/dev`](terraform/envs/dev/) directory). -2. Using the `gsutil` command line tool, create a Cloud Storage bucket for the Terraform state: +- [Pyenv](https://github.com/pyenv/pyenv#installation) for managing Python versions +- [Google Cloud SDK (gcloud)](https://cloud.google.com/sdk/docs/quickstart) +- Make -``` -gsutil mb -l ${VERTEX_LOCATION} -p ${VERTEX_PROJECT_ID} --pap=enforced gs://${VERTEX_PROJECT_ID}-tfstate && gsutil ubla set on gs://${VERTEX_PROJECT_ID}-tfstate -``` +### Local setup -3. Deploy the cloud infrastructure by running the `make deploy-infra` command from the root of the repository. +1. Clone the repository locally (or create a new repo from this template) +1. Install the correct Python version: `pyenv install` +1. Install poetry - follow the instructions in the [poetry documentation](https://python-poetry.org/docs/#installation) +1. Configure poetry to use the Python version from pyenv: `poetry config virtualenvs.prefer-active-python true` +1. Install poetry dependencies for ML pipelines: `make setup` +1. Install pre-commit hooks: `cd pipelines && poetry run pre-commit install` +1. Copy `env.sh.example` to `env.sh`, and update the environment variables in `env.sh` for your dev environment (particularly `VERTEX_PROJECT_ID`, `VERTEX_LOCATION` and `RESOURCE_SUFFIX`) +1. (Optional) If you want to make changes to the KFP components under [/components](/components/), set up the Python virtual environments for these by running `make setup-all-components` +1. Authenticate to Google Cloud + 1. `gcloud auth login` + 1. `gcloud auth application-default login` -#### Full deployment of dev/test/prod using CI/CD +### Deploying infrastructure You will need four Google Cloud projects: @@ -121,19 +106,33 @@ gcloud services enable cloudresourcemanager.googleapis.com --project= ``` -Now that you have created a Terraform state bucket for each environment, you can set up the CI/CD pipelines. You can find instructions for this [here](cloudbuild/README.md). +Install Terraform on your local machine. We recommend using [`tfswitch`](https://tfswitch.warrensbox.com/) to automatically choose and download an appropriate version for you (run `tfswitch` from the [`terraform/envs/dev`](terraform/envs/dev/) directory). + +Now you can deploy the infrastructure using Terraform: + +```bash +make deploy-infra env=dev VERTEX_PROJECT_ID= +make deploy-infra env=test VERTEX_PROJECT_ID= +make deploy-infra env=prod VERTEX_PROJECT_ID= +``` + +#### Optional - Tearing down infrastructure -#### Tearing down infrastructure +To tear down the infrastructure you have created with Terraform, run these commands: -To tear down the infrastructure you have created with Terraform, run `make destroy-infra`. +```bash +make destroy-infra env=dev VERTEX_PROJECT_ID= +make destroy-infra env=test VERTEX_PROJECT_ID= +make destroy-infra env=prod VERTEX_PROJECT_ID= +``` ### Example ML pipelines -This repository contains example ML training and prediction pipelines for two popular frameworks (XGBoost/sklearn and Tensorflow) using the popular [Chicago Taxi Dataset](https://console.cloud.google.com/marketplace/details/city-of-chicago-public-data/chicago-taxi-trips). The details of these can be found in the [separate README](pipelines/README.md). +This repository contains example ML training and prediction pipelines for scikit-learn/XGBoost using the popular [Chicago Taxi Dataset](https://console.cloud.google.com/marketplace/details/city-of-chicago-public-data/chicago-taxi-trips). The details of these can be found in the [separate README](pipelines/README.md). #### Pre-requisites -Before you can run these example pipelines successfully there are a few additional things you will need to deploy (they have not been included in the Terraform code as they are specific to these pipelines) +Before you can run these example pipelines successfully there are a few additional things you will need to deploy into each environment (they have not been included in the Terraform code as they are specific to these Chicago Taxi pipelines) 1. Create a new BigQuery dataset for the Chicago Taxi data: @@ -158,26 +157,28 @@ bq mk --transfer_config \ --params='{"source_dataset_id":"'"chicago_taxi_trips"'","source_project_id":"'"bigquery-public-data"'"}' ``` -#### Building the training container image +### Building the container images + +The [model/](/model/) directory contains the code for custom training and serving container images, including the model training script at [model/training/train.py](model/training/train.py). You can modify this to suit your own use case. -Build the container image and push it to Artifact Registry with: +Build the training container image and push it to Artifact Registry with: ```bash -make build-training-container +make build-container target=training ``` -#### Running Pipelines - -You can run the XGBoost training pipeline (for example) with: +Do the same for the serving container image: ```bash -make run PIPELINE_TEMPLATE=xgboost pipeline=training +make build-container target=serving ``` -Alternatively, add the environment variable `PIPELINE_TEMPLATE=xgboost` and/or `pipeline=training` to `env.sh`, then: +### Running Pipelines + +You can run the training pipeline (for example) with: ```bash -make run pipeline= +make run pipeline=training ``` This will execute the pipeline using the chosen template on Vertex AI, namely it will: @@ -185,25 +186,23 @@ This will execute the pipeline using the chosen template on Vertex AI, namely it 1. Compile the pipeline using the Kubeflow Pipelines SDK 1. Trigger the pipeline with the help of `pipelines/trigger/main.py` -To avoid resource conflicts when running pipelines concurrently in the same Google Cloud project with multiple developers, populate the `RESOURCE_SUFFIX` environment variable in your `env.sh` file. This will append your defined suffix to Google resources and ensure each developer's resources remain unique and separate, preventing unintentional overwriting. - #### Pipeline input parameters -The ML pipelines have input parameters. As you can see in the pipeline definition files (`pipelines/pipelines///pipeline.py`), they have default values, and some of these default values are derived from environment variables (which in turn are defined in `env.sh`). +The ML pipelines have input parameters. As you can see in the pipeline definition files (`pipelines/src/pipelines//pipeline.py`), they have default values, and some of these default values are derived from environment variables (which in turn are defined in `env.sh`). -When triggering ad hoc runs in your dev/sandbox environment, or when running the E2E tests in CI, these default values are used. For the test and production deployments, the pipeline parameters are defined in the Terraform code for the Cloud Scheduler jobs (`terraform/envs//variables.auto.tfvars`). +When triggering ad hoc runs in your dev/sandbox environment, or when running the E2E tests in CI, these default values are used. For the test and production deployments, the pipeline parameters are defined in the Terraform code for the Cloud Scheduler jobs (`terraform/envs//scheduled_jobs.auto.tfvars`) - see the section on [Scheduling pipelines](#scheduling-pipelines). ## Testing Unit tests and end-to-end (E2E) pipeline tests are performed using [pytest](https://docs.pytest.org). -The unit tests for custom KFP components are run on each pull request, and the E2E tests are run on merge to the main branch. To run them on your local machine: +The unit tests for custom KFP components are run on each pull request, as well as the E2E tests. To run them on your local machine: ``` make setup-all-components make test-all-components ``` -Alternatively, only setup and install one of the components groups by running: +Alternatively, only setup and install one of the component groups by running: ``` make setup-components GROUP=vertex-components make test-components GROUP=vertex-components @@ -215,8 +214,7 @@ To run end-to-end tests of a single pipeline, you can use: make e2e-tests pipeline= [ enable_caching= ] ``` -There are also unit tests for the pipeline triggering code. -This is not run as part of a CI/CD pipeline, as we don't expect this to be changed for each use case. To run them on your local machine: +There are also unit tests for the pipeline triggering code/Cloud Function. To run them on your local machine: ``` make test-trigger @@ -224,43 +222,50 @@ make test-trigger ## Customize pipelines -### Update existing pipelines +### Adding a new pipeline -See existing [XGBoost](pipelines/src/pipelines/xgboost) and [Tensorflow](pipelines/src/pipelines/tensorflow) pipelines as part of this template. -Update `PIPELINE_TEMPLATE` to `xgboost` or `tensorflow` in [env.sh](env.sh.example) to specify whether to run the XGBoost pipelines or TensorFlow pipelines. -Make changes to the ML pipelines and their associated tests. -Refer to the [contribution instructions](CONTRIBUTING.md) for more information on committing changes. +This repository contains a training and a (batch) prediction pipeline. To add another ML pipeline (e.g. for continuous evaluation), create a new directory under the `pipelines/src/pipelines` directory. Within your new pipeline folder, create a `pipeline.py` file - this is where you should provide your pipeline definition using the KFP DSL (in a function named `pipeline`). -### Add new pipelines +Alternatively, you can just copy and paste the `training` or `prediction` directory. -See [USAGE](USAGE.md) for guidelines on how to add new pipelines (e.g. other than XGBoost and TensorFlow). +See below for an example folder structure: -### Scheduling pipelines +``` +vertex-pipelines-end-to-end-samples +| +├── pipelines +│ ├── src +│ │ ├── pipelines +│ │ │ ├── new_pipeline +│ │ │ │ ├── pipeline.py +│ │ │ │ └── queries +│ │ │ │ └── my_query.sql +``` -Terraform is used to deploy Cloud Scheduler jobs that trigger the Vertex Pipeline runs. This is done by the CI/CD pipelines (see section below on CI/CD). +Make sure that you give the ML pipeline a unique name in the `@pipeline` decorator. -To schedule pipelines into an environment, you will need to provide the `cloud_schedulers_config` variable to the Terraform configuration for the relevant environment. You can find an example of this configuration in [`terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example`](terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example). Copy this example file into the relevant directory for your environment (e.g. `terraform/envs/dev` for the dev environment) and remove the `.example` suffix. Adjust the configuration file as appropriate. +To run your pipeline, use `make run` as before: -### CI/CD +```bash +make run pipeline=your_new_pipeline +``` -There are five CI/CD pipelines located under the [cloudbuild](cloudbuild) directory: +You will also need to add an E2E test - copy and paste the `training` or `prediction` example in [pipelines/tests/](/pipelines/tests/). -1. `pr-checks.yaml` - runs pre-commit checks and unit tests on the custom KFP components, and checks that the ML pipelines (training and prediction) can compile. -2. `e2e-test.yaml` - runs end-to-end tests of the training and prediction pipeline. -3. `release.yaml` - Compiles the training and prediction pipelines, and copies the compiled pipelines to Google Cloud Storage in the build / CI/CD environment. The Google Cloud Storage destination is namespaced using the git tag (see below). Following this, the E2E tests are run on the new compiled pipelines. +Some of the scripts e.g. CI/CD pipelines assume only a training and prediction pipeline. You will need to adapt these to add in the compile, run and upload steps for your new pipeline in [cloudbuild/pr-checks.yaml](/cloudbuild/pr-checks.yaml), [cloudbuild/e2e-test.yaml](/cloudbuild/e2e-test.yaml) and [cloudbuild/release.yaml](/cloudbuild/release.yaml). -Below is a diagram of how the files are published in each environment in the `e2e-test.yaml` and `release.yaml` pipelines: +### Scheduling pipelines -``` -. <-- GCS directory set by _PIPELINE_PUBLISH_GCS_PATH -└── TAG_NAME or GIT COMMIT HASH <-- Git tag used for the release (release.yaml) OR git commit hash (e2e-test.yaml) - ├── training.json - ├── prediction.json -``` +Terraform is used to deploy Cloud Scheduler jobs that trigger the Vertex Pipeline runs. This is done by the CI/CD pipelines (see section below on CI/CD). + +To schedule pipelines into an environment, you will need to provide the `cloud_schedulers_config` variable to the Terraform configuration for the relevant environment. You can find an example of this configuration in [`terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example`](terraform/modules/scheduled_pipelines/scheduled_jobs.auto.tfvars.example). Copy this example file into the relevant directory for your environment (e.g. `terraform/envs/dev` for the dev environment) and remove the `.example` suffix. Adjust the configuration file as appropriate. + +## CI/CD + +For details on setting up CI/CD, see the [CI/CD README](/cloudbuild/README.md). -4. `terraform-plan.yaml` - Checks the Terraform configuration under `terraform/envs/` (e.g. `terraform/envs/test`), and produces a summary of any proposed changes that will be applied on merge to the main branch. -5. `terraform-apply.yaml` - Applies the Terraform configuration under `terraform/envs/` (e.g. `terraform/envs/test`). +For details on setting up CI/CD for the template codebase itself (instead of for your own ML use case), follow the guide [here](/docs/TESTING_SETUP.md). -For more details on setting up CI/CD, see the [separate README](cloudbuild/README.md). +## Putting it all together For a full walkthrough of the journey from changing the ML pipeline code to having it scheduled and running in production, please see the guide [here](docs/PRODUCTION.md). diff --git a/USAGE.md b/USAGE.md deleted file mode 100644 index 12688e02..00000000 --- a/USAGE.md +++ /dev/null @@ -1,72 +0,0 @@ - - # USAGE.md - -## Introduction -This document is for users who are editing these templates for their own purposes and would like to add their own pipelines. If you are looking to contribute to the open source templates themselves, please refer to [`CONTRIBUTING`](CONTRIBUTING.md). - -## How to add a new pipeline - -### Folder structure -Add your new pipeline as a folder under the `pipelines` directory. Within your new pipeline folder you should have two separate folders: `training` and `prediction`. For both `training` and `prediction`, you should include the appropriate `pipeline.py` which describes the structure of the pipeline built from the components under `pipelines/kfp_components/`. Your pipeline may make use of supporting SQL scripts, and you can store these under a separate folder named `queries`. - -See below for an example folder structure: - -``` -kfp-template-0 -│ -├── pipelines -│ │ -│ ├── new-pipeline -│ │ │ -│ │ ├── training -│ │ │ ├── queries -│ │ │ └── pipeline.py -│ │ │ -│ │ └── prediction -│ │ ├── queries -│ │ └── pipeline.py - -``` - -## Testing -Please refer to [`CONTRIBUTING.md`](CONTRIBUTING.md#Testing) for information on how to write both unit and end-to-end (E2E) pipeline tests for your pipeline. It is advisable to check that your pipelines pass all tests before being shared with others. - -## Compiling and running your pipeline using the Makefile -To use the same rules listed in the Makefile, you will only need to update the environment variables. In [`.env.sh`](.env.sh), change the value of `PIPELINE_TEMPLATE` to the name of your new pipeline (this should be the same name as the new folder you have created for the pipeline). - -If you have organised your pipeline as described [above](#Folder-structure), you should then be able to use the Makefile as usual. - -Before compiling your pipeline, make sure to re-compile your pipeline components if you have made any changes: - -``` -make compile-components GROUP= -``` - -Or to re-compile all pipeline components to YAML: -``` -make compile-all-components -``` - -You can compile your pipeline to `training.json` or `prediction.json` with the following command: -``` -make compile pipeline= -``` - -Or you can compile your pipeline, and then immediately run it with the following command: -``` -make run pipeline= -``` diff --git a/cloudbuild/README.md b/cloudbuild/README.md index 7adc610f..a1c90943 100644 --- a/cloudbuild/README.md +++ b/cloudbuild/README.md @@ -1,5 +1,5 @@ - -# Troubleshooting - -List of common issues during project setup: - -```bash -$ make run pipeline=` - -google.auth.exceptions.DefaultCredentialsError: Could not automatically determine credentials. -Please set GOOGLE_APPLICATION_CREDENTIALS or explicitly create credentials and re-run the application. -``` - -**Solution:** `gcloud auth application-default login` - -```bash -$ make run pipeline= - -google.api_core.exceptions.InvalidArgument: 400 You do not have permission to act as service_account: -<...>@<...>.iam.gserviceaccount.com. (or it may not exist). -``` - -**Solution:** Ensure the service account mentioned in `.env.sh` (`VERTEX_SA_EMAIL`) -exists and you have permission to act as the service account. - -```bash -$ make test-components GROUP=_xgboost - -FAILED tests/test_train.py::test_xgboost_train - xgboost.core.XGBoostError: XGBoost Library (libxgboost.dylib) could not be loaded. -``` - -**Solution:** if you are using macOS, try to also install XGBoost using Homebrew: `brew install xgboost` diff --git a/docs/images/architecture.png b/docs/images/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..73a2388a438e3675cb6f6974e7c1acd2acc92cf9 GIT binary patch literal 247418 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

!N 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%0A(t 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

S~uzcGy~yl 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%0DYms#mztZ~>B#i75DJzY*Sb6Rs!T{jh_n zh`Zuln3tg7w~^stwp@SL?s`DF8`tDvO|uHHLE^7p?*$e&xB$s-kcFa&clyROPjH}l zgyYq^F1l9bL-!UZa*NBK#ZzIWw(o3?g9%QfQ zaR|P8p~7?H4HTg+|F_AE!c`|3=Yj?Lk7jCelkZH*yH%U;k*1b)H>a47f{mqv5zJL} zJSFXm^DS{`t;agwF}=)KcB4Ca=9_=c&UBx^1;L+8Y$D}Q^_g)K#ql$aS{w=Kdip%2 z+Pkf@ngNY-C9*B^d0GVIqZ=#wwus82{HB>g^OMrrQ1sU9ONAtXAL(tN3_-c<(%mmw zBa^!@%)gXJ$c?wkj75trSP0=^^M)z;&D}O{fI+yEcfx%dL^9Uf;-w$F9ZH^t9)32-86D`@UfdCCX1-&L*=$y&z9ZLmon&@ zi6m}B?;K~()<>=&J)Go&6!Vn~k1m7WS(rqEzZ6rQW;|zH0V(tz(0p=G35L0?Ryt zZZw(KyWbJatUDT=0%Ec_X#m+((ywT4)#*$J_o^0yK^Cf$5BDhIiPV97_W3g&u z0lO&5Yl|lP3UiAKuG}WOrL!g!suq{tAA{V>==dh7=_p71%CseUtI+6EMPo2U?5yfs zwVnA@p6ZLTwnJqH~++Y@FLJ6c^mKDDyTP_B`6$PZ_mH7n(1Syr%I4F7|vP* zCBH)iVb*o{&itZ?2+h?13jYO1;U8ZfoLnPgJmh?j?%Sm#r7@n`%@E4zPN%|zuBlEs zZ6sShT|U>DTY8u*R5o4LLAv1dv6FMByW#S}bp|(XZkb`8pC_wv$Vt0&I!4&h$xP5- zd_8&8f^s}nsChz!lcfVs%|?n2Ys}Rm#4GRT+AQgMQ1Jee^c>1-XMX$;=qDbdSYZSrECK!!s_Wr! zx!2C`t0X$gn`QPZQ|HFQ6-|(AD8p5CUdFkwpT#^KU*hQk{ z#LMM8slByfc{jCB*!h$#^>M5={ZbRf`T_`GxJkl9vNm@_0b)Re{RC)kMDceo^rJOe z%}9KHs@QDMnw@CJ`8SRFXTPgGwo|s`l&#gN{8{GgSg*6{zc|i4jK~D(p5=G?i}rcM z?w;`CNJ)^{mps%F%etB%$tKDA&Fg!HnWMSrnldc9X)nbfaoqhmEW3CL|MIn|#I^2c~1nXCXHE7{hT{+ce0|?8SG~ z)}xKSSD{>0Qd@~xB^7Or*j84r$z8UDPUslC5D^g}rwT64S7s|JBR%S(Ia==#<<5(a z(r!0v(&A>-s~lAD#W8LxQqV0uynb}RGk9CHS1QyVG%vH<+i+?=<-Tx7jlfYYGo3lr zZILH8JeA6F0BD+_Ar^)z1qx=W3r z2{kq8rTOYF&^0}NXU2Axw ztBs|_LN_Oh(#E*o{AYrrRX`nSm6YU0yJ(FZjd``<#+N5?74YLW!L4E=;p%C!bkk3N zDIzDybl+tDNY(RVEDdQLt4@Bm@PK!C_IwoEI1UHYl6-TO_As9=)$pa3Uv+k(bQpLR z*d&)zy*}8Xx^d|&=_{Lo{;{*Mm;>s-O7ZH$D+!Y9GY@NH8~FnrS|roaJ)&5L;KEf1 zULDMSnZuAVQ+jlwF(Ogq84iy2T&5>BV`ShGEgCha5{fB>_DE5AA8*fh)q>;$jIC-N zk5k7P`pj&^_o8j##Sm`o45y9n`%LI&jV}sO2KMJ~u68wL>@duw&c?65S-FOZ9{*vs z4ji8rG8%#U4(s>9CIq7M(F$Uaq3n#WBv? z`9%6KRME5HaOx<$i8n}zx5$(_f{oj@EXS?4@MdO=fMYAk9L?#(;b4x~878atHE-%q zo#~vz#?X)?UtQL_oVgPx^l_d>23c!u$GWV#+7lj=e*K|35?}L#BT{}3s5Y*ZdZ0MH zm1=Z*toCa3!7;cuMrtTkcvo?bz|zMMoFGSxMJ@5|LC;`^BoUWVw|v_XWoh0<^&;`n zVcv^{PlO64CN_bEjXy;mUJ3wxyLL({xaI9_!xv+IN@rp+@y)8aWOCi3nK8Z61jl@b z^^5H>jv0rwX=iPt_M^r7%~ASWt8264UED=-(V=t4y>BJ)__rLzq;bD~)6K*n|10}$ z+bxVTx6Fxp2k&LLcdcx+!>gh95ejeBZ+?ubZ@=Ja+6<}S__!lt^`E8jaA^rG^5ljlb-UvD7+bG}BC^+rG@w{lP_qpfclR})Zr7swF=MCR zE~ByZL_u1vYBp@}JyW8viyAzShO_Sr zCSp9=ib{@BJgI?i{DJP?%199MG1C>UZJu1r7AZunRK75E!YR5M*|WIbQWkpYs(P`9a7&MD*sg4g z%@3;wn0rC{A0B&v=mtsrDztYx@3m1aJ}<~Oo>GRDu1P`fj_L!;^~-7t)F@#zqLSs zI=;JkKkrN3Y1zl&_aMNXIx>B+>}Q?~fTYLZ3X>gd*I%DJ7}k9HW*OW8G`)v5qT%Rh zF&*T!NAtKUlYM-;3CWF0o$YP!l^I0eudonDCy#d z9M^13GK0)Tc8#emMVGe!vp)SXxgC?rZBQs=@x9}P zwt#Z6IXt{Qx0cCkQ|4^z(S0ntz{s2<@}b2YPmQ*%_;{#@P1_9{ky@W;*^C?6pzoXN zPfKBDi@K?es}x%@x%8H_ov;$}p@A|ND@rJwt2W|ach>lnK+@>fV*Y6q**s!2xs_}r_spXIkP0*fx$WMdzoMfC+{{LL+_#b0sN}>@3BNfYDj0qx z&Rf&lTAB|e;YBIxLpxEZ2TDQKOpeWh(k69FO!+jbn-!MD(!L=Qws8md^(@5gPqmK3 z`t#k<>DsL<7A{HO;#I|Uy&pfvGi<&zr`j{K#=Oi}ILLtNLf4;NKzfr}M7wrM;_p@! zl6Q0`O9&5{JnyQmvfV>C&`clX36$Wf*YV+AYCAeRwp)_wRfF3}!3F3<+aK&zW4$?! zQi+fJvWmzYx>7d$ai!a)R*_Gc<Ixw?_rr5PcxOPNlc5YLt&RW?-;uKsg57cFJ3$8AdZS;_nr1~3)Ws5e<) zg?d!|D{LjN%r`vx_6!`M0&dPL+%c+LeZ0q6iS-;NQ9il{&<;DCZ%D#ybI1l=Smgz)Ugu|X0_W(*FS(y zcY68b<>Y2XJOn{@0i6jN%(sL?thaC}rbJ5h<`gqg`tC{{PgOkPb!8+UTG^|gvgo}( zbe(1&Dq&|Rp%OMmF)IOF6uNnnv)1Gf7wSzc?7?Z6%ywhP!(J8p-{Hki&T4qR&PNQm zcrR0gYr$@g(QLePfkA~P9VdtL=$+IZ_CFiN8JAb!nIaLsz~F9Z9*_(U`^bTfcm)cw z2ud_matA4wxg5>I+={&`yV}=+tJIT<2=mZzk&>ioOK02uEryuJDzlE7W{rQ?NubxNA0el^neg>9j8O-B=*MRlMX9u!wB*zDSYz zY0qmcH<5d%LL+%sJV(0cv;GTJSB#=m?oO5Ix|y5ts1hrZ=;QIr&t%9_*3Bd= z__KH*&F?mvO79mv;7sLG4)^dYJ2Gdf94qKr!pJyu;CDK%HU^i>3*LG7H&1aw1oCa&Xp3JeE;$OMBAW8kGCdn=D4Oy>1b+l zQ0LxXXLmGJz21?0_Iy6VA)>vp7hv~hp>`zJRJ!gKYZYv14fS+S`&hAfT9}Apn{%v* zMHR1PQNN42%^~O6ei^F~wP*`^R1bmi7Z+0Q(wU+uulgiC9gN)^3R;~TFB?)w94!q~ zLyBMXUf=bBVvCLr)?^;7Ht{TZc&~^>)yt`6xE|%Exov}T;9gGsmd;}{Ea7ib7RvIR zpLebLG2W(waOJ^gX1GSA3CN8dxB@d(PRCs>R$1rMW$oxNFsfZ*RUc$;weUDBo_#jO zxR?aNx?wqXzx<+*25(6W@7@hLlO*S~fdT@lj2qn;$jZ=C(HET-b z)vG$3fc~Jr-k)hu<-$|(Lw;Gq5;ta<^4Zj^_$9KRe`+CIaA zeq~UM>Td}RS5=RvD593D6PtrrtbF1E-lfgp4BH}`$#}j(8n)$Xd76)jp)=k9^ev2T zdvOKZ9VlACrj)1V#A(uBwE4L}_TtDKsm#yIuQM1!hXk9nm4i7icwe|>C32mOZ1{P2 zL`{$0B=!T*wp$e--eQAm8DKItQ>V)0RN+#1C`0lLUwm|UKXew;)4J}q0R=3cYi%KOXZO6je&bDzD7oHu?`N+&JHP#PzWZSRUmq*V>#`MSq*P$)Sj#eV z7c7BRA0_D@Ri6qv9W^tDxq?pqPVq{R_SOkH_MnQt(x7K`0DGj;OSx&}eUD}Ku^v_C zYYu#iQl|;uGs>4sCX^SKCB#95iVFXTkrFMz5xiCz?Qq!5z zdKM3!9~~mt@U#WS!*7kDCo=7O_T@DSIn_}H08~;Ccv}(n@RwaecpOdY7QSwhqklw+1nT9h z4(9GoI1ReA;1#zleoRQbTa{DRFQ+-y0!1ICr{%z|TAZp>7kf6Npv=OBn*)mbP~Xus zA6N#-%a)f`n7Ti(th#ThyE!wr%A5_I*S&|t8+JrxX zYD9S!8~3`VRjQiT@gX}Jy2pjG%krl=xc3s4_FmP#x9i)i_aA53ghuLQdHV9&G2N*u zZnRf{*TDTZHR@i__Ndf{sGJ4!a&eLisps6t3Y}s=rOdt(!^bL3GVrSv{TYrIIQ$Wg zixw!}GN*9gzm?^d>Gh%LwE?C_xKnf+y`?hD;w_&hiF<@hfrt-W_C9i#o2!(M7fx2f z9le&p@$R~JWotlOiP^pMo)6LC@$hKS$H6HyP{!-BOc)4TMzHQ+Y&C3z4=%B{j-0o% zW%Zjqq`JUj5Y@kynF9{%($5jovK((_^>n4yVY5$~VqV%7GCC+RqINJLiEmdZ{y_Y- zA0_As+3l%Zld_N^Vhr1E*){8_ACanJ)pTw?yE)=VQPg);tv*2LI^xRW1kWfR33Jq;BwZUj+J`BTrb?HKfGKaj{ zj6ctVUeO#?w9Q#1y_A|+6y2F$1!xTt&6!(KajiW@(~{~ug##s8rQRS7 zcD?7~rgK`OfzVqM=e1)cD)AXQywwq0TVZkZb9_O(-VB)|vto=zGH<&QiJ_;4r$g=Z zT(j^VmV()WbzVw4&5RxS2f8d9(|kvBgj@#2D_25J>LsUzd2&^ZF-~%j!Z^+FqYokR z4{`746UKfh&}C?c(NkT3*Sc1?PMX2pcltB@9P_4)2G=edS*fR{gi5|o?olxg zb|m*N`_Q9`8c+Xx7ys>Ml!l7p1GGVTkN^4-V~9Fw@8<1c6Y{TW@k5y8y}%2Wnmb-< zq`l|)J4Y?8ww0MJ?qF=-pBlV9y!+X(ir7mrG>qE;U$iz zIYgx?o$9Txq(kmu_L9zeX#l8;DfZVZHlFWss_ zn3+${bnU$5=vO;@n>0dZ=Q^{@;=BeUTI!7Q4E52FAKSpRGSEIRCazbu)Ox+p2$|Eo z+L;pgjKpI47PF+-(uV7F@-tlhmy}@n%46+P??MF@%+gXeJlDF$8QeM#eGxxnVq&kJ z-V0^M*8(- zjp0X(k<#0KN>G3Bq)i!fCiN-tDAB3HProgwT8jVGcssifrOUS(CsfCk@|>y(NXceK zicPXoW?5oIIk$2bk*e}b!f$bFdAhw8J@>8O{N3;sav1ySZ<V zJ>A0IlSk_qCQN*$m@GrpFH+=ZyLcJrQ|jVQB`L46L}U9Ns-LRyFT8ZVD-fpP-HT?A zI<&GXz7xRvUQlgIuR-FU%-tvmd6V$6)N8WbY-_UEp$DBup?1DZ`n=rvWY691heJSh zMLj2O>(SGO?6g=aIzvF+C1kd$a7BS0)n6DDWa&pqjn%ata_UZ%TK|^GHxS}D7iw$p z5a%gk8~b+qR5+=)7p6)&%pKh*Mi*zd54DNYuQbfw?B{Wvquv>NmmE>hZO#=U;mxlF zd1toeMsP@%v?QIRWQYfLJ=fH}#Hc<#D_{Evt}4;^z+z!XjQlS%x+Yj+DW7$z-D|53 ze`zxMzJEi4o&7pFor6)W&29pF@zpaFd~e()W0*4x^4UeNb2q*d=4q^Eitr-OeJ+Hv zO!t*sO-TqSOQ+CLlYn-{f&|%9;85H-_U%p;(#7B*N8ht=zf4-#l5Z+E*OnLDxOx0I zDwD_;hzgCLJ9IZ*9#CC!91!onva(tF{HQV}t^fP5%U=1b?#zySsDI*Tf9-qsuZHVQ zq~SU-8<_f_r!&5@wvrW-%VOr*Ud@h4Qw?+YG7dH3vn0MM>j7)eNsKdXb10{fOrX5c zM}uZrQ{#d2>)D1Q;E~Tm`kYo;p5rM zQrP&_b8~P?2G?Wfd>pqdVvlp_oKq!yH!m#Ir&Gl6$nwKnw6Xm<-crma18pFtV^e?A zY~aVE_;gb574T;{b#j`EPN6L3J@nJQKV1T(ppidCWIV98Zo^e&l321mn?=p$4kU<@ zvlP7tT<}=e6f*PzuE~qJP*IGIp37!vAcbVad3?aV&~6z5*t(mvVsugRE>nd8xF65%_Oh?F<-+g58O+=PL;RfRX=JHJ1C zX#SNIeTk^sA$#c+ca-XsJAXm93aDLYp;6bKp1V2%!koxtG*KFkdlf!5HDGH z4NK*`KHGwSq)a59Bbo5I^G@vWc5m^$iEHH4<&BTYX1q$vNi?6*ik|%{Ueah#wNCfc z7YY%x%Cco@>!~pRFM)cX$^G6u>@NAmJM~&aDl;7}vcmgmcTF19;&u9mP_U~X3F%Wh z-(3#Fy_a)VoKWWO-2D+%6^yTxf7-DmT3DO5s-CFHP!RRL^SJ1#yAw%e1+&$vHV@-Y zPJF}r%kkc~!`CqfRde#$pxP1-xS!IUoc6Weq5>|27kmWRGh-x|%hxI5v-ssaIh6=U z2f8a@ia(&}cu$YozLO?pjF3qnqulDncNHjO#$b|9S#AAh*svNJ#v6Bej(7^)cz5&& zOVkld!()ZBJLBj+QFLwjhM4~NBKYHB819@2WzpP5_^?$y@L;* zFrd@S6^YP>MQK6uC2A-4*xHl2+4MIB9NUPPU(0)eTCGuC2%{O`^9DaHB_N|`!Zp)T zlW9pE7MwSt+jj>S<=iMFdA|HScs1m3u(45@hL#cWYE0XsI)u7$;qizU1<~VH3u>p# zCj)*2hmr?)^P7b@qiGl#?NBey^M8Bd3<@&BHP)x8=>%`I6Hr!?)+k(j^h^U%|NJ5M zxfr7_{@P3Lm^o4|*DDHByowXWpoDji%HJw%a4c7(>6O`Ap>PBetY|}j)XzO)66<`N zW!6&^PWr=6O?L5eMW+Xwn?`g^^-(sG8bd1KM?aT~YR*#lQg)&M4#7#+o?Iht3f#uz z!>Th+stb-32s*m0K>Fsuhy4oJp&-vaKBzTB=>7M z@Y8LSP=;j6tjl}u?NImNTTg}&DA#*sLx}LjYYsFB_|*g(Qj_uI*qVN$CpvXp>45a{ zfTuxluLTlW@92BZ*Bk)@Di*a43Z)n3oKMsQp_HPf?6}OH$_Vl+cATXG`% z^{8kcW?=6-&@puT9B@w}d3xg{lu0TN8T!o}H5mKSXGpceJ_Ho^&oheSKFwD+SZ$t& zXnFYLy$eirt~ZLbF%dU~MjKuzHVqf5tZU)ChzvRa8&RxRVQe*(GEc{D%i(|j-aPgs z8m*y~P?smdvOtlZF*&c%V~_}OC0e7fQ7HZiIl&=gg#*$rn02o})%v{xKt04v0?E^5 z&=7l%({73Id$30)@ZG`AAJ#Qt`8=W%^Oi|be(z_!Osrwm6Vrj3O2bL$w9(~-o8^)| zj*)P<*OeHV)BB?bO1J-pm%dzGI% zrK%=*&0=6JF!K>sfThV)@zeax6Q1$6vGYd_I9Q5=42AD++80(`xN<7M=loF}c`c73 zoeWP2liNL)I&lh@J^C!Y^)Z7`xES?{UleWqcgBYoGz3+H_s3NSodE{bY+EQRaV6<9 zI7b>jR31>18ef55E~&M!`OMdQW*NTySbgMeSFEo6+U}Rid9MQ^lAdRJ@C!}AiZUDN ztZ`wdUS`jzSI9;!6wN9RrB+zaV55$Cvx1A$!f;UCWR$i3<`r zKutn)UG{+V?Z;Qn1c1RN!_F&ZNW86-IYQ5P!|st_$wnRT$~(xvYN7E%a#LoWxKFO5 zN2$9>B_*HxN!sRsxk&T!=$E*ypP*BAnzcZl*?cC5=U$|N*H+}i+omt61Yk(#?7?Z@D>S45fSNgi zpnfEYpOAAhhl-sbLr2OrD$oaJGm2S9OHcH15S)+GJD8Q^GL&}YRw)-IW35n5q|K!q2V|n`m`N^a5gs1w(Q#goU2JV zyCM*Glwi$MLYm#334rGc7j^n`5}SDV|XpbltM2_#l7%W7_)5OkhUoOPa( za1BgThijY0n8a4CN|ZvJ&;pcETLSM9WX3#T&fMgbnTWaC-?5ZH%-(#BR?ir?Kew0h zK;<>{#l;KZ&dtisyDnjWALw%z&K3(@wDhS5#rhWblv~0eXugVesc3ox;65>{?2rBc z>AG|eVf}MDp%HU2nY2+&Cza-pqxphpvVHo%0Y;3H++{UpunO!)4&(o@3fV;>gqqF~ zvbrZLob)3AX5+~R_PZG1#o|m?&F3rzl5;s3RdH`1J&Qrulv{L#iW;YNI54g4Le8^l zt3X4=)%+t^HuC-sCtXT52%LqGxGm!G*c28=D zXaCb8|Le1ZESD9K%{>;Noc)e@Rasd+9xB!Rb7fqY&q{vYOlC(rA4mcH<#9j`){E$9 z-IRfW7Bqr!!FB8ha_3T&;W`#Z$lMTGGzRiMiRmOi6#r-^Cf3)0_!&#)m2W50UHE={HH8&#-mq|<0rkwdv;0L=je19;YZ^A^L2L*Q8!g(4NV0g@6SiJ16vDZ|Id5*yli!RPZ6BqSU~xDDWZXFlm#KT6a8T2-BBXV1W-!-eCR}o z;pOQ~-y1o zr9Gnsv1c=^F$*8{LhGB5z#R}95dscc3x)y&k44{dqpl}NR!TYv8x7c5bLvJ2k%CPN z>Pz7u4uDz>&FBI`^!J9q4sU5#hr~_crHE+TB(NvmZ{o3=(^T+2w~Ek(&I42z9rgK5 z3h9x%C)kPpxIk{R9IVL`{w>7$a~-bVI3;jQ z&X^JUZ})%F&Qn|RE3hE+b#-t(#E!B81Tv%$qhG!yB~T)N2{Th`*lQ}B4Tv|9m5+cA z%9;_6uIL}z1O$LNk+4tPX0735b4&>H!@gt=e56_K?gKWh$AuhZJL3TGwjq+woE!%5 z+h^UxpI^CRNBivu#-plsO&}GM0Ac&O^I6$cI|!XN#ue_?xV;pWfk&hsw(@SHI!_Nf zy(N8IaaY%P9;8rJ*nfOs-OH`@7C4(Ur-psQp z&&lE}7*H~6$rPer_96L-i}@EbSfJD7HRq1wgf1Yj%kd4x4b-c{JrOhC#5O%E2^2qse+Oe%ja2 zd-x>%OG=S%o+)kYasQJEYai%%b!X>(J~D}HK`C5kSBCL? z=RSd&8QsFIbF_3QL_>ilK>@!5jHI^nd-;2nuRm1nm1%>()ms)}+>V$4RUA+!*P44P z4Oak1Kr*X`bo7>`7}l72r;CV06H?QrCp$)g8IdF>b>~hL4S;f)wTk3S>hgersk%jb zUha}xo5$ygr*>sC>A9#YZ8cTZ(0 z<4ak&#S%~lYP$D_9eM4T=_KNWn8-Q>YHn9LXM=$%43^wyWfKJ77(g&~oj?Bzpb-2=+s|6`$n(&<`t;zu_~Xs-FY?Bh3!oB4Ji z9PTdx`dS^+WD-4^n=-L24-p-o$DRyqU-D`3_Jo9QD|)CNRw*Ab6U}d#)K*@$)Ha^m zTmRrKIv_D|OIT5lR{TrPHlj`ucd;`NSiI2}r|O7q57#X~xqInARHjl=0e& z91Rhfx9(?`kRDq(A@}l~@(WYqd~UbR)u~14_$9deyzEDplwNa+;w|N~rLL;6 zG#c7RoW7f{m@)upWNdSyg!u<`%(|65g5@5hsx-yaww%>K0sj9H0M7?!Ia4Nf6=b$m zm7{sZw^X$c#?Sgn96%t7K?5a{vVP}TK!&13f%-!m%uS89S*!N}u-9hH(Lp?4sN^l_ z*mFmON*sydfyNSe7sF5d<>&rwONiO+A$cmMyRHua%?34n*U_iEd)b#KxMa4Jt~R~Y z;qpslP2WWnG>Y{ zp#)-&mZGmcGhugCE~KTQkkKX;)>?yr!#|g-->^Jyx(DNQl~YRoUNR{1dlEpa%SLLC zXvS1-hI*Ts*k7Y?LJ_$_OZAsyK7|k#(+Qv;o&QH{5$; zuI8}~S&B~H27!fdPYx~GRZX?=mDf9>r%+2cX%CsBQH~E?9zL8Db|hW#Ff&!+sP4^a zrrd2HEy z1x5%H>~uVN_oDBujMYXAuY5A7TiWvtg|YMEAN41<@&!%Ts0$X>63Pyu>wHykXv$Nv zJ_iU6;tw4v*+}8wB{)cQ==dLhf)~+-2anS7@f@_KOr3jJKst7>s?N3vBhMmum3KqC z*^gt(D~a-FkaqOex|XfT#)AbXvMbfjpyM4MsE^8$8e!lM^l@IqE(kwX4?4-qfCW8a z)gl!GTvX^#k&SMKF7Z(XxKYnMYKA@%6$EUDRqnD6lK?1nH?HdWTKa*XNm8 zUD*GruUXwA(ayK<2A&ljY+6&cy3jyy=J3Cs6&|{ph$>pf+lz99j&}s^*bgUX&K#m5 zctObg(Zz~IAfW9~QPGkV!)5FR$4RH%b27Rrv>uWhlJ|xA@5}f`p1rlV=&HH(eB$-0 z#|k|`V@K#!O8byaZ?O0r>3dQO$+~=YMEc%8N1vgu*;14oBPVyhIVbN($8>`Z$?Hi73)aEgQ z_vKYFmk*arp6u`v2cv56A;^oh) zfj`|Kv~=%$u08LzA0+d}Tt>_Wr#8T!A=NkG*FQ`>Rp+};QR1KPkaU;AA>`MR@46g3 zh~AH{5U2jzI*=E>ekY$xUs^q-@Mv+UqWpe>L&Y1u$Wzkof_6in#$qnEcV7BZ_{L0T zn1XY}9U|X{yzWFiP5E-9ydyaMk6OK=ky0V_HYUTi3%fw9q1m}UH?Ze5<#!tF*?y&# zmKHw_^~9xj5Asc0&L_l3I$KV}^lcXI?ws@#d0YyfSb<~EH9njWOnGs%T9r-ozLvpun+N=kk-0iWWq$*9*oVa z*u4=amg8zbbZOPB$aWA{7(8ejZibyOnF-$jCwP8{-Z{R30D}nG&?tr&T6$dQpDQwH zio9O&Ij?JF`3nW@jJ)o>vaWnIIa>C^c=bZ@u>K6|$FCda3c-XEKC_T@X)3Yc#aQo- z37eW?+~`Py37t3i-F_{suOH=cducl9Hmcv3d0#8Zvu*~|QHKlVf@t~CL<*2Pr=cyx zt@~~(?LgDr`DXt@9ne*4eRqI>z(u*YYaJwb-!^a`437`3+TZg?WqJ`aO@L9)(x7n; z%v;12X5B5+!Q5kQeqfl@Ul~2&qeIlp{Q4>!NJDI%NrYE{X-llC?-?JRMHj@SzVrgm z5#b@2U%2>1!jLJmJnUQIFT)5{m#ucrevrWS>mK>ro~h-MJ~dB*WO!MmWql}f!~vyd zlx~$5-fQOmhOwK5E|J7O&Rq-vrYB2N zvvC!ixLV^U&z+4%Jiy=`zbAU{y-N*vt#$Gzd<1`|eE%*i%uZU+(TPfXNKTz@jNcO` zZnPP;VR~L|z2ze1rEqU}c!OL!#)qjK{`&I4?i0kP}N*98S!8 zelx#;Yvlf*wpaz_(HTTD2Q@eG9e~`|wU>(`MB+wcS%95LpJgt3 z5@HeMu8dnB2AWLm(}6dL8*cWDn{i_5ixXVfR~Z%yxWX-q#)Gjl5CHMCBBOBOEwS9K z{Z%Lke`WyHWUGfH2zO7uIseLfphOTv9r7jip-uua6Y*+q%yx9syHuodB-##QW*#5q zQ`9~sX;RbPyADz^LKUFJq{`W#WrIbMvIN3bwPR)@^U1 zc-TuBnlC7?*t`Q;2)$FFqd_b$oYY*WnNBCjI5Y+FVn%16oN^P<{hr%Ax4nTe@YYkx ze=yPL1nlj!7!a~Shqt)$w(j78hN3vl`a?%0i|{cz((PFRsz2=i>N)$&Gvz~9e?Pc~ zy4hY3D+a}w3VQIGW;jdTqv(ysN8*RC-jewkzHIJM@zCQIinWa%{c`y2wV-2KlhGk@ z#0h~;3mz$T2@w-zW(1gqvN=I0R-f@aX`hqoF%|C;=<(7FUJr!5K6f`=ty8W{>!hem zB~Z@e7o(amd(s<23q?zlP#Mzu#vQ5k@CJ*oe!VXkVMzFnvj^*(6H60uaTwvmgr}6W zW0IA2weyXT`%H>#b-sem%oM-es;>?)Py%s(X#U+z$>I-|yDXuJaJVS+g4$yct#vg9 zC{n~!Bf+afCu|4H3cFII4a$K)C1{rwSd0=98F@$H=F$1C&1d&0jN>U>eDyCg65QV_ zIzr~5mKVg~(bQHel$pXzncLD;JVmoJF}XPgEM4(CCWY{M*JBkmkCoIBLh;Ll+viSe zR@bILvuVZRX6Wt}vGH723?$1xnl5n#$X1ObbYGflQ7U*|bZtU|ZjtrOSvn6Q7Y}sQ z95}YQXghw|5W+fN{p|peU*}=u_7SZ+5<9d>et&r8zpgE^GTD=#6q};>?bK5+X_EQ$ zRx_}aH^G}J;lTMzZcnFlVa*cbTcKtu;yl~!1l8Z0ijmflwt*AwDa}~xie8~gvo{X% zk}wU;W$cy`jJ?rYV1)tuk+tQeebg*iuhz(5BPb#go_ET2PB<>@%`utXWhW@t7J0SG z?C&-{sEv4*|GL6sGaV{_g0(_l9llaEeN4^2|KRYN)UjQlFvv0adVgy~OXXtZz@^bA z)Bsnk^MMd+P(naOuL1t|0#r^BJET^FEAFva@V!5Sr9W>3UoWCFT2wVJcl+|qZb@$R z-Cw<%NKUI&;qF`pg*M&7H{hNdBc*IzL)1UK_Hjsh-T1xKlqve6O-?%U2?D^Ai{jFc zJ4^X{&_(xJwkXwBr_fS;4hwE2&3d?2aAG{UhUz)?wiA%AKxKxf!GUb9^ng3I30}*P?@_W z^>AKMf!VqH``2i>!l7KFtFh7^53}m93HivS^Ez|mJ&wGzaT9vu z_UDsQ3lGteS{lXY#xRg_T6dkLLmu%UzBEws-~IQW4OtN>)~jU%mA z`*(7Rhpt0tV5tr_P?|-}W8#(%1q>{=t0nAZ-^z19CF=bJb$`SzNst|sWmNh2kCQ0w zo1C01V%GYmcY%gmBNael50z)PeU9>=N(6EYswzdZ)}iL4sKDg#)d!=vQm(<1+`@5)qyFXYt1{vHGUV@=v0 zft5@ae`NTt@4EdOzVK#l9>@Q%mcM>d(iTLEEq3=sk^TI~*Zk`*Nu2P7v4$qf|K&YC zva7^IsJP)eOU3{CuAA_MqBV_?|MR!~f5ZCAqW`~PA!qD=ZCLw+bT(mIhPbWz1Mr{H MH5EDZ6~o8>2Q;~vs{jB1 diff --git a/docs/images/training_pipeline.png b/docs/images/training_pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..f84f6d14fa18cf482c4b328fe5f2e967e58d8f64 GIT binary patch literal 162349 zcmaHT1y~eo+b}8JC7`f?lr$(vE-4}{5=%;hbazUJEG>;Fg3^n0NjE6nA?;%$OfwV|^e*gsz!|g1M0*qz z+#+ik88rnN8IYQ@gN3!NISR^)$mCQkH4O+=;Jyz}R!9o*R>;EV8kASkR;c8k-xY>D zl4N7R2#$IP(QmEFWekqWEkeUBeHq^w##Sw-UI7p0MC*G@X-Lc~yViId3AA&ss{s(F{etSM&bfjcrcmx{1Xvbp#C z%E6QoM>jYs>A#n8R6dyDe~7^$GXJE69w@o)%&dR{r7p~gZ&#E)hKj3KQyzvgCUvqk z6cktd(C$5gF^f~mp1y{~Q~tk=yeObgP6PYIJ^l{2WG3`R;D zz2{UFWc5wQgh&{Va`s{;4<@r2SAI$3NK4aBr(rl|#d`+3uIpV^AI)Tn$rKudd^HXd z;ImCLVkAf&=OMoE8XSOg==pe07)@}j_H4Rpx% z{O+7Kgw=IeGr&Bh(sKrKlF3+TGh*p_(kd8W;!4Z+LX-sFyu*iveZ%0DB)(iFkgR@V zebym8Mg7~H)cBRN;JeTTUetF?zac{wQ@XP=sJu=_1@GHMFIUAwHnlB%y$(@ow_@Vr zYKG2(e;$wKqM~e_#tetxqO6gkq#k>|QW#2dIN~-&#bHCCQA3~Eocw{IgQB;DtslgC zfQkybAM);gIrf1h{|k8zBQ&kRs$3NEAg(u9Z0Hnkz|ZjC2e0Jfd=EZeBCWk=A8dbs zeG=$@KpGQB^aei|BqkeF2&MUqPA$C-g6Sv2#$Tj@d2lDN%GkPLu}U8G)L}CB8Onb z<;UmPbJ#iIWv%m$MAxKrawv@9KS>Na(2N;vAKj0XBPGFs;j+uVgg*XEXe}iIoqa*X zALHA##QPc_-89Il^W2n?S4@e;g2IC73%<+!(Fp7AxvmqF=e&y*gVf}dcHi&{#{6_ z+54Pbhuy&X)2rAKihL>aMp!&7j^9Yw$gAZDb4}v0^mqa6tJt>4HtM$ATQGsVz!xVg z_V0`{>bk6UvT~44L*`fJUtiYsStrwpNDJk;Y+9cTo~)cSobXhBx2Ukdv9SA~DFmM= zj{8RQE&ChVxb(R9xcxZKTl}Z=Rj;c`-YUOU9pl@!*ghUlE!@{^DU>Ld8g(3BvL=7~ zVA8V?$0pr6>@D}?T$z`F%WFlwepSWdDn)qkM7f~BtcIwZPs17YF2lob?;GB$SvCwo zvZ(MW?nTFUv6+7tc<%bt?Zz#?KDj=t-qgFxo5b78`_xo8{a#H#r10N>El(x?8m(tL-!y-&XK*O^8M zn5Tb9-{WhtzLz2VfjJ{NW1csB@Zn(Sy3as^g{h_JFy&xry5DfHZ7Zy&qOfYS>a*TG z$T~z2(hTD-kE)t^J8pbBKhr8Qco}%gL~EXbk|CBsnNd@1Q7ty>JS*x>E^5>;?mp=5 zurs)edFHdbFRJ!%U8LB<%H@20bCu1h*yVvUzhjig%&u#kf@v795I>KoX;xFFwrGt= zfV-1xuec=y-!7>s9_#rRf4kFdfpQ!pj)`0)}jss=(g?hVWfA`Zpc zz4E-2TR|Vl8oACDBAi}W^pqVt9m}kz4hPV^C97xr&J;sa{j8W}N2XT$eEmS4goR`z zqWVQ>Pi@b3Pn`OKdZ)VhFRfo3pUOH5yRl8vO%E&LE3i!Esm?j4DssU?45gsGJ?WKD zMJhwc=Vms$Ut68H(RISgf62d5s|RpyEsHoT8k5f zi5_Vm*%|px!CqlDAuK^WK`|jb)YdrdKshSlrCeY4*wUEH*A;7Z>(i}}lk*b|MsxLV zh2k=zrq<6>r}Xtfd%do`bMeBAI7tr%f?<>vPc27(c5T*=$zHzuF4QevAht1@(8J7{ z@|cT)tEqXwb~?-)y{@?q%+t-e!R4U$vB6|FY9YBeIk_lT6Q^iK3tl#4G~d$YFt}Xx zx$3J;K!4>B$B<78_ge$SW-Vm;bu<07e<2r?2TDGtw_o4w)XK%iPP>ymyO)k%XrY2*u|NcV8PA ze1cTgQI;l85I&3)bv`ac{8|@@uJ7~q_WQ->sN}uV$#d8;Cr;~|1#xQhfHHR9U;p2Xic|Lb= z@%h-#E_#;EAfnl^z|Y@jqw_~}D$j*s{m<6zXFk)r&TxuUj)GGDGC>2E#tF~)W$$(e z!j`A5BJP3L4+(41WJEqI5RjT1&0kDT}Qe` z+*cyNdyO*JQLs=}MtKYzW20c8(x6}hN2tJ80+seZ$InsOP|*K4kA{L0ZjFNR*F7r0 z{_X<>zIS8(+@mLipO{L`)s*xQ)#mOw74G?Hupmpon>j z0Ec$wuErowJ6n4f5l?Z(Kkg6#j_*$MFoOQL#nncfQAb$~B;(+04ie<%<>qCSzy*Oo zV$Nn3BClkh|1})=OPtZl)zwjihsVRigWE%Z+rinAhYv9IJiPoo{QO+N9b7J6_O8aB zT=p(Ze**a%j;y(hsk61CtF?nY=nk&2iG!P~I3wfTgZ}gRbDrj&*8e=o-sP`l0Sn~0 zyTZfA&CBy2*uYS+yR#x{)}H3J5Ls(G0B686B!mS8#s0Yee_i?KiT@g@^Up{=foHt` z9{R6K|8uCOi@CFmgB|cpSBZb<_1EBkU;JyJ7|-3(|AmS_@%+bG0MQb-Vm$vLnglK^ zX`vCI;{$71)z`ot;Ig|9>I(3~_U9frMs=$}ac&7mL6JmJkd=DviMo@88scV8e?STMC= zmGl+2$MC90+VuSNeA?~6-mU~%Vl2o|AKa-LoMt0Y18jAC5zkEgiee`ud--_gI*j zy)ZY=-$GqpUQS3!Sr2gdnK_qpJr&@2v-G9)3Tr>-8o7k9l%WHiU;o&^`VY+C$4oi& z7k|RMR1F*+wmZH^3oee67$Te(6#Z_eQcXY0qh_?}#wa(d?ANu^veObY6X55r+kNlM z=xkAPtA&Jwwp9V&q52@8d6RwJ@^TxHSWl1fQ{W?>wa1F#m$-qG{4tn z_K8<@>Z94X-EM{)E}_*?4YRe=Zg||JQNKaJna^-nBhvHc>H4M3WNFrx^bU9j9J>W> zysgDPxLiJMhldY?YiWP3VH>>vxfT(?E$@p9J8KQ*!-1mUKqZyYve9e@OY|B4pt2Pl zqgZlw0^WL*2wePo!(c(hg#}G>b5Z~UP)HBtBTiXWQM6*^?gj-xnU>-RP%^2Flr`i2>wSAl-ck>B(gsR#)?lLWXShZkpsq|kAg#?%kPQ<*<06b0@Et}Cs zCRIGhYj3|UtN%~F8 z*0d`a=4)%OAdoGIRF@1NX0kuQpvwGUuMZ0kSA1>uxxv;wBetS7?W}rdAr0rh$;===Qwn=KVQffe$wuQLHF^hVQim6H=6b*FY(?}~+47<`f4#`BG8{?Xmt z`O;QNi3{&rE>;(A0tybs)1V{wH7NtIUR_kwOLi`<)Sco7?A+Y(bOsGCCWo@-5w6%} zDu?6RGjwr(GR5C?ACm`U(l4Yo3*!ao5w=e2xYh(WllzxRGmDq&VX-3;!jMbE78k(E z?o?fOxFLRmaWce8i&q)^MFOey`E%~;35()X?uVi;brJUV3=`~qBAAc|lP=SXE`0R= zM*k+TFxq;%;dFrN&#-lVQ54zEXv7Q-^(Ld(S=XMN7MX7%G<;TMcT5(@v|<9(Q+u>TZmb^QU}9@;n9ZqIrF-vWm+7H~h71WA93UUDG={VRG?{DH z>>^Kdq{YJ_Ylox4P<;Zonqf>%NAur6?LbQ!P%bt$XlCXEm(8Rka)lQ!tbSV8f-fp= z4=!7knSSomYsJVx>_os|BUoi+#mfX%}1K#n_K7N|-IGBL?K=HM9g6A=^BAEDXgiN8vuzalE5pOZwpK^n{HB#rBKeJLo& zxS~&KZ)^LU(Hv9Or^Cc-p3iTxsb*$=XF7s%StO;j%=|8&N8-o5&RG^8@Cc$X5jUMB zDbF5m@kOrP&BRF+`((q&XVJOqKeK2IsM@*72rfR>>xo~dB5i@5hmv0#y#R=3kE&yd zIsNV1H=5w$?_|`)NDS&b>Vc+k)Swi-Lj#GApQKpoHzV&8S09ug_Nb0iII36G>7t4vxoj!Nmpl;p_u>Oyrc5BWqL! z*=at~ykPJ}{np`4{C=Eo!0Hv#9#P|M)g08r2fTh$W493ChYPDVroPjflI5fa%ssls zl$5YD^i)jaWn*%)cU1!AhJau_1(?_k2S%~B_7nqRlvf|ON)0Phf^BHNwm)W~&`EnCrVk&6lW(L8w zUXV0ZJV*o#d%`Vf&9W2HF!15Od;u|lFsF~&%j_-g(3Ec1%za82xtUC! zO?d(U?BySmn;4azuzqOMsZ)RrSE>{M^znbp5HwH-cwUqGvdA4*vHm2Mq7?9#hQ>z3 z=^rxzknTW(>_D zH$C3EJ$aSS0Kz)ti6;JZzXYYMXxx8hR+9z!bZRh(&9d@1c}3>QpN&coyFC4Sl6T^YO?JTELrG5lLPh1h zRANE`BMuG@L0iX7Z`@7zB#}K$kmqfoDs)ie7q6)3Yy?a}L4lNv%+$B1It*Z|3OU0Y z3DxW?Zf#t}nh#e8n@B$Xi*v$}PcpLa_WDb*F1{aHDtJE+d*DCyk>C3Fc!|M-mf_nK zRdD}^dH-j{jUnfb*?KwU@HdO&)4tfk7^v_%UW-@4hdEG(Ycgs{(Rh?w$U3(7g zjP^+7Hqh0FY8t3o46rB3gZtyF>L{x0Thy0#&8`;|oQI@(Ny)}bC(b1Ge`#S(i|*I! zNn{+>;m;Zuid`PObIZ#T-KVcRAL&=lu6Ro^TjoGtciS31c=S};fIC z6s45=PDQt3HFC=D3}V?cZJAB-GdUkL`%G#=;#L~Eb}X<+e~#=xbcfAt?%tqZ-Ngo2 z`}DxFkjvnpYVX_6Ao!=)*|+azw#%={JJ=(xC_B&%pf{G9U*|3sEFk681BN3KEB3w1 z&KK&tYfBGT{MK=j2o^A=V>1LJFC&O`80BSUd%kHFVNy~y`5+QjS66lA!Z{*OPViSQ zb5(N|NXzU|1RWM^kKA^qD!QP?#dGDxpNjkSSjHcvDsGWMYHCkDq*~8tm z!3XFU&Mq#PH;@GI_q-y`1IbE71gpt3 zzd1+tB2=3LF8yzd_jP}tlBG|^sY<2D5I$QCy!$~hdeHjJtU^Mnx=Nq2cvhc*$TySA z2&wUU(ZjuD_mj7LKLM*NbRmn$#ttuu0-&$o8MuesVXmUPr)JULvKY(Gi`CFC|AAM> zvb(xgu?~A8%Oc2Tvc#oK^aT12a9*09^wT#Y-hbC7Av0dzuU!4roefPc(n>SGo^^?v zH~&4ZZJo35_(bAnz~$(usI2Z7RB^RAznWfDnxt8z-Mgke=3b(f>b6qWGME&Z$da+r zbb870N=u8oAjhj3(tNwnU|yikq&zbTKA-kG|JAK5evj}0sAc%FJTv!(9IhlAI@SabTs5exf<;T?uPdkoq}!XLw+*O#;oy|fO^nxDX#e8?wE zRx-uUb*g>q=G?DdlVwm`61g?*Bt*yP;Wz9x><2)<>PnpZReg-ZZ6qtJdl^PZZL2*V zb@(c%`U7&|YR`^IeuvEkc{$4|>es_<2$Qu>b8a9xv$IHA(-p_MW(ME>Ry?}K zo8S2Qo7})zew=Mw2`uJZAP26%Ew?mBH~SjUk}G(a-dzBZyGshFtkChSV{~H{zg-b) zZhBZKv*yuQYTeN~&&4aPts}D8`}HJm4?K?cBAr`}*-5E(b54y+heoITwn@!)`HXvU z!Igr(yf=1CV^T`A9&Nq$>Lmd!{%ML7!(dKxbbk?1z@tt4a8th0&Jmc1F?3m{! z0(<7{6e3@dNL-$4)LkuH&8<0BfXK;HwBrdN4-X;I4*1ve0 z9q^IFlmYqv=P@#0`s>=sk{{weeF=QRyicJZ+kPnW>^^U9i|^Gu!h*Wne`P$?2K!{M z&H8s}s$t4TcEGRVHsLoxaPspK5FXw-nX1H~pAv65ny^;$b(;%X>sfzUvpZg?RU-1{ zvhQLetH_?~vTw^Wb9WDMF7;*$wr2n3pa#FCpX(u8kDv;)Y8OK)CFs9=PG1thF^|Td z1e|0Ht{_PgGmawJ1$C0MULKc61v*seY_3Y!sfuwg;+Z^Ns`b~ih**(5_`)qknG#hF z>cn_UTx#ERqvF!luwSp?KI=x+YW5;d?5q~zF`JZOp3{Wj&%3bmP)aUqMs^j~1n*Fb za6V|G*2kao7&hp7*jA7;$g7pmbAJAbh_=7y80itusbBjdlW{kir-^Y`;x<@#p_^Vw z=t{67oYOs|b8pw9Ue<`y)avr$R(!II;YcOkb!JR7CX{`SP^lUDyygR9*=L`I+m1v}pitcFgq5fxA9lJN!)S?zon1z zN)RL8J?e*TIgAIDQ7DRwiagCfb;8>SI#_f3EjZsSYR4n@TQ>T}$yn2Twcd0&-nNft zp|mNmKH#A;@C*_=9jH9&D=N!yGa5MW(4o4`zcTc`ps;wTueMXyO~KRAzl~R4^#>+qFohZTdehWvC$eCmUfa-np;l1m(;kV6@QqR>A(}{tD~5xF znemp-FXq&YT_L6Bi6ksAc2+JUPl)S@+sQ==;hk})cFkG2_$LJbN&?()g8T^!13>uk z?>K?S%lqARZY@N0k||DNf_z#RYr(a@qzgvE z2p3ST3xSxO9~R`89}x(pH4>XG#~nxTaUa6l@P-m)h{QW=+li56Dk9^bjpe*gK72BZ z(v6oM2>lS@)=+p5MxC$t@}-hQad@(x48}CGVoIOpq&~cQtRtv&WcggaX#BBkeH>U_ zoliogD`)+2v~c`Njt2B*dA4j$uUXxLP)N0IkW;TngQEwze|;RYX(Ir>I1xD7wsH92 z-E=vEM@qQ|OX;d$P{}hCkhmxh1IMt^`yr<@XY!gl4X2*#ipIeV`(G zYDWFDrNwC+CE8s_R7Ubf$Ka7tL$`Ty^wC4lCFJubVV==}^eBxN^#7Ru=>xUCuuuEu97=2G#8SCbBT3lO%JoVHDfhOI7qoYiSP7`MKY>IL&?) z--yWdb(m_Hr=s$qj`nFawjbDFmtbqXcEyF2V~ZawwGX(TFNys=Xz$RiG}#3E_RlP_ z_mqzHrgCSLyEP1M#PF>?lv!L(JUikj{l<`O-*N*I-tJ*qWfPCfI_Wi%qLk(@rZJAi zO~uU;^&mY@k_$ZDe_S;a`qat*Edz)EQxdwfJfnms2ohO)Td^K)T3~UEti;h;RxdAh zrYb}0TsG=bo0vg8ZNJGSUXf%57q@IU<8BDe^Fj#4-UJ}Nt`A+wb2pEfpI-b#N8nuZ z8wIQkS~OIdxm&tF%Ctv&P-OF{Wc);i2>f_oDPXvXK>AopH;imtq&ijQO&H2D1zOo? z3)dWGu3sE@PbVkQ;^w-a=P^n{am=D0+zlb?@a&6_6s6WLRq^A@*+G<}>7_W3Xh194 zsL-2=UFz*^o@}oS=ejD4>8NkG)6fPf=V<4f(xD&r=*)}*YkE`rS=T)uveH=1FF#4? zo<4G`Wqk%dZ@t$)As!U#WMw%Mr*W6-{3nu&05mYI3Bk}d5Zy(j>GwF7|K>EoC#!Z+ zyo3&N7bb;E^v1_oP({$-+G|+R)3e-)K9lQ;ReAmKO$Ooks;EOW=uXb5)d`SK_v??{ zJ*LGlr#rV>#t6;B*=9b0&Y#x@u>Wk+S} zT7N&V2*p9dV|YTLbjM6TU?`jE025z`%6){p++WhK$G(jF0r8uMH|=rYUIh5sO2Q%^ z`A#&HmCk3Xhz;(ld~jWf z;N8}+jUuo1ZH5p)EYQCr?6OUMV3(H8WUnAqod=U{);q`=&_8S79MXT3tXT?)x+$4l zu>>-kD3z=WkSX z<$~e~^sSa6g!&hY6m9H|Ejzb-n0vj#A1O*JDKoo!S=71O&@)v0DuJuH>tR67%|a;{ z+iHyb_UJ73*n4Ias-Q$NV|+;q)IUqht=iQtz{vAaTN)A9h&LlF_(B1{dkfa0c??R? zzO)>FImFs716_^um-Pt@i#xo(NYl;jD&oavIGVdPRBOjYoas5lHz8(5A)FpFsh}&w zT%B{TId@CJ{l1p=pC2W-%=XjW<9zcZTu5w%cW`WPD(1*HU@`~GRA(xum2c6D9ro+5 z3@gu1Qw3rg?wy~>1$OR4JTxHlYo)oI_b7l9k?5J_G@T^`Yz%u`EmoCM52#!YL)@{O z;X<}*d{0}IXzqES67!TbUg{>^OjXGDhxGG2MY=8rGjf%LzX^VV{h8kHI#TTNJduSy z#0h&f8+BsT61kyYIa6O-?j?o^IfvhDZ5YK+?9!Gs+0I#jcvw928te_4f_Fr9Z^P-l z1|HB!Soxf~LJ(~Z+$_)V-0`McMmPFK7sZ%`<%B7n-kIXU2%dL6{KvcnV(8v!MlE}k z>}gmAn7#qx!Ue$%0=P*@|`?mSN9lGbFa+%G9A* zxD=t#wvdHv7A?rq?`vv{cZ~Wx5x^~Lp_7`IU5@M^zC1to%n2?YeMQx_>Pe9=>b{w4 zvVm52xXzA-AokgjsQ<$fwql~QtV?@tM|Q_(J`Ne_=WZE^h`)n=j5pzZjH*w>8z(*;%u^MJUbB4~&DW!}0|}b~DB=Jy&~3o?CfDeJE== z$~%0Dr|zDq(t)X{VH0ZG`AR1{PkdS1eQ>7AxzF12gNwe?h51Zs*N5-N^nE_pIIJ0_ zQp8RB3)Ts_0}RlG+og7vqO38SkyYB&yV%X{cmiJbLfRzH7kNR|h2Z6%!RdlllMS_^ z(U!9E7SB(w$BCT;;?q~ZOS9br?4{rh*{S@rp2+u*(4EvD|&gWe(bKW=E z6c;%dw~=VS(0{G(H%-%+MftxN=lYSP)P$o!+3!Ds4EPg^x?SEd4<3N~h2&!6w#Q)^j~fV78bR zuDlsow5*#4z4^3(p=E(}6CRm~^!Sy(XF7G7eHgz@=8<>VbxN7}`$4s6|NAwUAqhp$ z8RNM#Bv3-kX~e9x+=n)^yF$V@=IDyp&L5uJNQtj22fq!TGsYd_uaLb*aa23-qUbg z5iJj_9nRX^Q2o&(ake{IBbKo0kw#}&*SIAMqlhJ&x1_B^QH; z(vUa^qB1S2UczHjPoa~j$$$u`g)~FdY%3LC7GFk+KazVt+fJ}Vh>DlvO|9Yx@g?^( z$KN>Jy`5CZ;pFKRa7eplwx3FPXMnD0|5w%=vtj{0Yl=2=-S;b94>g#GC^H$^IX%E* z%cp{p5uvxQ;o9T4uv8=OiWt%A^$HMS%jG5QQCEgTgWOB(Pai za12xAcE&5D6|^05uWJcAh!M#SSg3nCM|^#X(D#yM<*76g9YTfA_H#G)U>~1lE|};D zf=hO-G5sQ@`%}1ty4yM&KY$1k_}EwFA=So4ryBZ&dn7-#)#a~s0#WenbSP#ee7o&5 z%*wf0^jH0=oY%t($E_A=O??fw(~`tycH*?O zmgaUTAt9cvKV6mB#y^_M*PT|2uEHIfpN5}U$VK2}fmYjS_{HXG`tK#UxzUn9{w%{n};^woF9gAgw~@S1^LLAw3I>Mtu)*Opc$604JbyaZI|nV z(<9A(Q5_u}=Y3i>;@7j%_f^#WfBkC4l+g>Q9gK*LEpdCWfvZDVR1-{N7kr#s!q~<% z2fp_VVZG(*f(vWmx=^OO*e21)@Ax3=b#Q`(-t~B*7wFhXyf^xflu4o~D*%-*jgi#~ z0LwENcL**26QN-so8{Q;hj!Qf_BmR&<@3s3$wv{F88@CPA5~(^;?zX#fp-*GEP_P? zftpE>xd|49TtJrasQCB!Et6dL-Ps`;)%fqiiVF!FZ-=}OrhrZaz&MA41728gd(@5H zuQ`6^HqT$`^|&HdJ|EN&i&7{0FCQ&X6{xZJFvI&h;%>2 zAt=lF(GUP=E+LMLYH7~jjg4Zx6UM)Z%@bYE`ND(>$_y-v1+d^T@L8Jo8P!@^wvQ2T zUcUI<)y$iOwaj-lghIE-MaDD{zslm_vJ?3Io|C~?>x2X~y{XeH6jUgQ%+TT_WN%~e zNjj`kZj?=G=UiMHtp{QQ9`5zu9HAME!GM9t()dmx9@G;S8oI@~X^{RJ`cTL&ws`Kf z%LrFer*=f#TUPMGM1PZ4n+xsgRj3~8g|kB;)2|6GGk77*U4ion`rS(QFHQ_EY{nmG zY#;i(Pn8L$-_|DJrJkZFGi1oX6U?|~cj|SX|CN~UbG7HcyZBQnmc(rLmZh_qx~~L2PG@4JHRiZ5^3QB9p6=A+o@{^}ChJ@t_75Y#&9|8QS2@SJ zh?MoyVR*~a(g1+hl=rNiRTF*E=T`b3-w)q`kWijhS{Hz3=eY5#81Rnp0m%+h$`$4^ z6Iydfz$sI4Tntd1I>n%F7}aTU;)K-5e(~oY=j!W{XDwtCg?PK|Y$5ord#COLC8WQz z%99SZS4>8{65tA?8`+zyUTiOx%{}XmdBD`Y@0>}P_};Osh@--D z-lPtm*zh3Fwo!@3CF^8stf6$C=6eZWaIrWl2IRln@D6|qeK8NeJ|KU@-|sv(+?gjp z@!HN_D+Y)GNHEg%pj_M>DS8VTjMN#U^ZAOg4m_W{_a7-ZDeSqqF$1#A=rX!dSJ@%o z+26rOmT$D-zvniakPB+yI;rLPm+Fx;xGNsZh0g*CF*qTR!dP86;|qv3NqIgmS+~VF z@viFXj-LJ;|Lw1~W3xv~Qn_`QkWB6ZHhH=ahk=}$E#=A-CXA-5GklYMf!yfT7f_(U zO3gs@AI}CjSh_2=)4!=r2P)5=e<1O@H7>06*Ft9}^U>IGSzmU;KBH*dgN4GE=EV?J zBj4k9F`}1_3JPb#uZUQ^&pIc~hvov-TCHn7On(iybW0hyxT3#UtXpAbY-=%|iha!M z@sF?*=`w?rG!>OX;G^HT7{Ds9(Iu{=wA8cbY3qIK5dht>! zpijR=O2lKE&ByxI-hR1-83XdV5%%p>b0#9+_t}O_FUL?(s+=En9wVdqi27tNkS2lm z>+ZYb!cG}Y{!Oi*=Rk|d>A*mKV?>#W>PPeV5w@hm8uZ8ajBkjy=sE(8-7{~F^zlsIxE}f zLK#x-7^&1?2U`?qS&_{S-ia=2=Bl)~<t}K2=bJm1rUZ19GYIM>c5^ZJ%mmAP zF!*l&5s?If;u(NxI7?@`4m&ToO;TpbzK4&spd)9yf`~-;-~@fJEa` z^wQYG<9>v1!yOWD5Ysa6AG&p;HcKYbJjK+_{6K;f<@PVy%)z#)&#MgbtYALQ~ik4}KH z)Xeb3E5o|Z(ozy3z$WCP?S$~{nj@r6iLy$a@+=GkpIt1o8uhfi^`rSj8Pc#P_Day) zPS8@^eusqRx#;zI|33fzA|}k(H04AI&Mi;xR8IXR7JonBx^8uCO)+lIU@bK*pl{RQ zd0#8mj_Fw^7#}mhuoGtBLBr6nB}fYh$EQ6EWV~A%?V}QTA>p0en}Ld5ciP+QSx&E; zU82>LUhS9RV`uJFMim91t3jd`U@G&Tzq~A8>gaqByIwu!_uYKM&ch>QVkm3&d8e|w zl;64gVN5i-Pr@39R_}1j4a5B}3;8arT=3>D{ty}b8PbsZt;BK^qleWBuCR!i+xd= zTO%z-4%4PJS(xp|BtMR`5MUsCG&eZLZVOOxF|n`vr))BQ@=q{+0@*0={=0R^>g`9%@Nc)gfX7JWykDA}raq#0{`Ez_CqEwr*+8#fp05Tv^ zyK&U1ZRkK4dU>KL<#M`>ukn6Vm4@|`=BA7e%r;c)e5&%xP*0_j90bVIiD=(Dft!wG z-@f^Z6RnI9k(vSz=*oHY={deY+p?X9I9t9*F`YCMhTXY%D6hCvTWA*-ddNUo^UbdZ z=+fqqy0{KgwY~U>-7j?>cx=t7hxc245Ys5oXT>>mLL)i;*qtK)2X(alc8!nvYBhO0 zB%vI(&pTyXR7Q~59jV#!ZC=x{#;xTvtSG-p_Z?&ILnqGFt)Cy8w(vGNJPLVfXlXS* z1?19sU0^08W>`MU%%3P8ZaxY7DymNyIQ|3GZ!JQG5L$Q3$=e+MtHArqDU?~4=Y*u6 z*pJZ9f%R0;_iT$b9p;I=x@^c5VQRDHQZ8MeL`M!d}_i$m#?~|||{Yy<10*#z0 z20)n=w0f62bwZ}Pno`=>XYMd!0xe&fg3HgBX+N6p_ z%W9`A@An#2wyEq7&BuH5H%nypQtow8Rfw-vc)y5E=wyFfyj8xM8s*vUkZF!X~l57<#noQidJkRtpWJ;KFdRqz~5I=WtLFqWoH}R26)Kxe?=`x4GcFag&gk z$bnF9>K$v%=tp}%)!nl3o)+Ju7Q6Pe6l*NKszu+qbvg`7A5$`<`lhqE7@1nq9lmeE zo0B&&zHs#H_zEb1{5~(eqEF;|%j~}ybX8BZOc}r&&Pu zGSZ)i$oFW58!?NX%zebMX+Bj095vXRsDBD@FLJH#N(H*vVl;r6q6GnQDEasobUCP8 zS|3Ms569w0ugl{4OXKMj5zOg#VO_oE@>QvH4;jmCe%!q63iMyE(NW8*5vUw|^9ygFnl?Bh0K!RgbyKGPYw8K3RVJds3l zVjR7V#h02i<4|0ZgLQZ*Z%U2M?U@gKhVczzo7WM$fPmmH%Hywu>sX<8NJqGMEc!Q9 zyy%owcYKOcq)hu}JQC+qsoEhyv#MR-3sKhtPPb`adE9h5{Xbx=#7FC+KHx(!tiM#M(=ZI?O<6@ zWqG7bB7@xJsJ|3G%lal=D84`*Y?>m>%{4FXy3=r#5Q zT>)B!BDAL2hyIr9>L!@+;!rSkSj9HuVD8>DRF7Tzy&cb|%g(7C)Q5V}1Mt#j*?N!N z-`qtS&?p=^qPZB{1tMxZP&@Sg_=EG$6ts2;}`}YV?GtW0idDf20TFTaH#@TyJoD28; zudBL#dJjMLBAd(f65}#6PQ&4V=%|XY*1=4^p0-;1MHu=`W16r!JJU_(d>3DTH)_u( zvnQSkCo?NSsgK4j8j9>2s2eW|*668TYc(^B10N^&^fVhdm@0j~mwN9Y%V%CQb!yyR z8(Xw6bQg2uy@RvV*%p;yV#~&0>eXJOLg+pZ+zuVPJDU;)nA$4W(5lL;*8^H?x~!4y zk8Bq%!@)pks?vO!nN>vA_mRFW6qPf^xC+jp(@IbFt~IBkrPWBP`t#@4rRly!X-W8d z%(j8~yqsYrJVp)8>?@L#W*M+-w};Vxx0(Z;S$}SC^svv#&G2wt8DoI=Aa?$K55nu8 z_aKOY_aH*#E^KGmd)C+16x`jbE7Fx-zD&4Zm7-|Wpu+fx(fmMiQq6j&e+e7}-WZ_J z`O&t|b{)s~BYeAqcAr;fl_6#*^J%w^4_#g}f1FlM8a8nc2-f!4XN5~#x> zKW_L*4L3~gHQcYk>(MkcWFS^u-X}8wu~gLWcG+Zx+o;WG1fJ@b0{vvsLA3Tw zvr3-poLDUG!TmvIZo5m=I}#sq`-MrS{o=$*CW`aqt!q?LYHM{|utl$L zH<^gyWeBG%Gcc!_h{S)^lKyCAK$Eemu_wK@Mu*ExY{7m2;&+2imTS!Tgmx+V{%QCk z#Nnp>u4!G7gM(wO+L}lq$Iu#02p?OkC#(#}F`QBsYx0HYV8aM-khrjCESw3S7@07E z{|aI*mu!+^v&!k#KxkG|nN9;rVI2dq0p7nb!s*y=xfF@w0v}ae``#{J(JuzT8!VsI z93xv10nN9^H#x&Z1U~*pf26ap4POx-BqXM!Sd1W%E6CMuWILQy?H{TDuY?roXxn2< zmzVQyjpXA2(TxLA;z&wA!?XS2EB#j{#ljk|{vTCe0TopnZVdxSsg#s}beEujfH<@u z-Q6HaHzJKR(nyG;lynU#3?T>%C7lk^Fd*H`%zyCvzI*?>WT|U8p7WgZzVDNJ?@3-m z><0=nmC`xK`}KK=eS3t`6v#q)V_khMTH){0JnTm2H{u>miXsQA&Z3!ZZ6LbVf(@Kd0*q5G9weMDd*h=3WLYm^1BaUUP z?@jD~uU7;A)Ym`>J2V=*qvlAfQzU!=@&oJ?zwTKzq!~K}>wH0Lxt?5R*W&k%lP3SJ z6%gJzERX7gD0l>?zv6Dm>aTiA^N5pB#`~tK`hP4r4PSrE25OUH2Pr8;!Cy;Jtam0} zRhfvx((UovUjCuV6 zE@!`?gWXB3g!hu-9j6(yKe|D!98UrtQIm&F$dU;qX3Fd0f{8P!|fAu4H!LZzrQH+{aLUS-aD{ zAb*a8X|Yk%HT`PD7Xj%S2`Cs~JoNp=frs+s0%*z3bM_AKl?N2!yNkqv-D0Kxf~L=Y zEvpS(zg(DvbM#+rE$xmu)yphwSYOv7cdn!WpSHy>97hErwghN}uk7ckWIE}6k9?V( zfA?pk5-??uTi4#7&Qp^>msJ>KMawb2&GuZRY6{X;BUIoP?%tuWd>E#uPh==S`Xd-4 zI=VKE*9`bj4ikTc7|Cn@%WVJWtK8K+(8{;Ak%c`4BFf;2|U3to>ZzwSzR2w%WvyV{3kTY?hw)#)DZW^TYI&L)%155H$lAx&^f zeMaWxMJjvnpRJxHlv)rHspgAVN*H3%@1IZ#->(0WyOz@lGxQS+*faFOqv9e8A<3_( zP-dSm+moSi%B)%us$vXV@_xiQOJpNeT7A!)F*bVIQQVRqVALqK*YL6HvA5m!W;!g% zDTWU<8dDO$^OIBcK@6jQ^uy|hX!i3$(~K}gPGn9Z1Tmd;^#!>sa|V>GiE>J57L*}` zVZUW?Y0B*jlPH~V1kO^);*;2O{p2=~ZDVVvXZx5CV>!5#LAF<&FX73{NCyc2-0IwE zDg0Ecc%UYK0d|QNU8=Z)8wItKp;_jCdo!>)MA&9BGwD`JP?zQ2*neUMTN_>1NzOLo z+*@tx?~qwpOO>!>eZe+#B6{NB&(JzgbyMq@3+Cw8R4>Srmx>F0hy#i67g^XR+U@xV zMIY9O>?vV@G2+s?ci#RMj2miBTW9xoG?5+3V?q(EJb|n4XW9RphxW^t0&QcMkW8ywD43Byx4SHni>HTg_3QZoNX za6Ou*4Lr~8S12!(u!wl-`2^N%UQF-;zgtxFAy%PuK>3sMitLrEeTCv)flzJwQ?ujS)yOq=Js9VFcm-UB!|{?EX~ zzqe$(7v^bXYT3)hLHcyvFC9$-JNz#ODEk2BQ%H-G?eAZ_DxIAD-CXI6Z}GxDF|-Mk zyc!AmHd{{RU<9#^boD)5IU~&nC+oxXX_ynpUjro2p#MWnVlf9YbqLEXK`iX#P0s)1%h4vA3c& zqu4+*Gx`5EGh1&tO|kczfMEkR>+Pl8Z!c~0=^-F_Qe>;(<`$i4?qC%0xk)+NH?0*f zk50gsoQ+ON_nhEvmkh%s_S`W&Z<1 zdsN14tvP#JYo6BKwuGk#D~RwDI~$4d0AmZ^$R!(nD>AiKtYu=4kN;>&$5f=Zsj$7o zX-H~i5}31HmuKof^#3=S)m4H+dD=ig_0msr+hlW|xaCOE030bvkk{X9CbP%dD85Eg z)Mb+`DEed{I|Y~)Rg6|95AyD@D9)yW>>y+J2^I-E1vCi zj;h>_0si}uQU#tMQRHzs&{a{-0vhMFy~RLgMp@aPdWw@YUhShsJ<0s4faxOCB#UPT zcyMgO5Op$Zv&%+h)64uo`nr@pw!w_-l@GAcVH}-goo2b|P3^%5`w(v#=Wy$dWi3`j z9rxl=zS67a6NNbK41dYe|Fi&p3U@~}wu_bNm#^@yY$*ihHcJUe{jX(=(tu@APoI`6 zf>>%k-dfgI9DrY!c_oefE+Tg{3^g*F?CFtJo#oZwqo2s>-qo#8zFlpSTl->Rjs0>7 zl(2bPxHDnIc}(gLREp*I*_y|YSf`Td+9`ogzb0(Div7v$bv~Qq8gXV`g}Amo3at#R zu9!NJS78nd|2pO5hu28*uv~!G^;*Y?j-qawb`ZQM_rEQPBGRf6(^;3JvHK_-;H0kcN`nWWZUAv6#?}sw^uL zMEuIq;h17-}g5gjSr=I|Kkc2% zd-(@(Z2kG}I&T#U{~6~vZZ6|LZ^c+!*k}tQOF%cp?g}S{R8W9pNFV_fNBE_*#4^xLps_2m+YX;1QszY(&_xg`N$qcsi zso#d*0<8V;yjI69uIfcQ(DA4y!_P$z({~%k4VaUWXD@@4`!G0 zV*zSS?wQ+GQ+}c&klqyps`gLkJ`dQbV>n+V?A{Hq^-K6z?5+qetW|xMrnk0|LQDBNZ2l?o#hGz8%RMMy8A(>ZCp$x3`XN1H7`ENXX5M2`2fJyB*uht*Fwke818 z`uZkkXFHjeQQENf*MqgUl?q{&7?&jSr}1g!eZ_-kZyGi{Ne=hXyX!^nf5Dc{hx7JK*Ng;vj1lku(Q#iyV0P!EPWioanmPpeAWQaH^l z`$KSGX&0_al&axw(&HireV)8GM3>5^ypGZJ=YA`f>n>M1AdLK)Bhp&4?IBKsLup6 zRQ>`3Dgs_QX}2?a1win^x>RSF39stPz|Q!7>cG2f#)p{GPqErw4&*s&?AS|!xxRZl zbUKa!f&#O52-&Z@4j`FIFq_K=cg~?vE$$pHQOk4FWMXB~^zAb+GC->!AQ2Oa#d(WZ15a|89 z^FnM;SV_nkln^^iP|&AQKYYp1HpVzzCRABlrBf?x67*o0-y8Y(VSb>)Z&aS%%jOsA zKN};s!=n1KGyMKHC6pw*?PYd%`rcCi=dqW6PDNGOAGRv6&$&m}a0RC6Dq72m&vh8O z(oHhY>v!HCU(e`w^80x4P2|p zW}O5E45hAGHSeaxqP1gZtLRRDneLT!rc)6*7yx-Q6jt*WS)A&=tC=UVXRj9FpwSSCg^y zIfYyF^0IjGtwBO;oDl!|YaH|?i|Zdk4b2rn_p?k~nh5W&$%f-1gJNQ^gSJ3R8#3AO=N)nKk! z;9HB@D!!v`?KED))O5pv4xtEMO${%{+`D_7OPJpcpA6_j%pH2?XG@)~^P3KfHgo!gURm>$k(+kAxR`3LPQJ&})izh%+Rh{Q)3Fv#qskfsr}QVhlPf8> za%w{%o1qwpOShPskp^-H825=>b2XO zSz*;g_8HbradwMM%#I#g(R_b2!S}>D0Az5*1F{gNL)i-n>q4?7=XKM8-bF;b;!a7U zOmj~G1w|v*uQH=B+3esC|6kVlDH*_O78ed!B>~a_`YDwtQ;Wk-8f7cxN?}W>n-4#| zkgPPt?O7oQRkYGkRZ!FbdW}d($u5Zy%qF7HhsD z6jn@a4G38XUBd0{R}HZGW^*soOSN{gIJ>({gppzaL1wjAi!q*gpSs4WxxnBuqhZ2E z1>qTjQQKJRi4aPwNKrA=!>g&<%kOq_A$RaA)nAZ$dH(o+O1DHwncI|$L*N$SkZ6PW zjnHAA@3uCYqNvHBrY3W`tLkCU{_s#IT<;>N(z~A0b?vj|R~6<)dhSLsD?kLp-#cpw z_oM9m9ozXT!tl4h%1AGLHKK2t^JJG}-QkS2g3g`=p5v4=+-5VV`d=d9?<2zoY$6YE zW<(gB@x4G`-m2uUxzl^GzJs+Sg3nb>9`dBQpm@{Z=?`G>?O$vXq-x(u^eJA4jn!Xx z#1sj~%?4Hg8ZT-j)6Vs^OSl0fZ)iThX^VGTUnAC&Y*P%Q{XzULV99|Hvc4!sq)Q&rO#;fwOmcUN{=THDy=Q}h9{fV$(_x=kUyiN zw<%_xn-(<|(p_Oe$f!=g~lXoVlLNha6fuwXtRxk84QKX#Hjg-JRS zcU&m7`v+BwK9oMo^AMj4a&4BxF5|u_0Dtzcp4k;aQw*rnP1T^so-vOUvxG$m<&vvr z*OKd1635zPHP#MWSv{8jy9=#o-(i2Y77MW^HcEy6W6(Hk&5V44xaS=Kql(#m^F4e2 z4qc-T@4N13yBwynlFk?#wZkb7>fO)R7Svsj79bCvqmL&CPqQw=_?9;T{Mvd48Ti7&}bFb&%VFPNbT1iF`X(tLC{-Dv71E&6)9zpxk>-E7*%~ zHB~rQMck^8TA`CNEOzX`|)vP|_N3pB^2xVTVQz z$Nviwv0DI!SdDvUR@;i5{Gghl3m>mcg6g89>X_=|r@Bo!j-zel{M4mXbLWOv_fG=E9UtanqQXNA7Pu>c04{z5C&)kyA4gVCzjLHKUf?%H*fgEC|5Co8 zKV3nOHfMp8lVOR%?PE=NXFuuF_ocG6(_NBUQRTm6Mg)Nuy9-M8YohIWMDoC<90M{EW2Ub>E_6z@NpxJ;zw zF2VVpu!R(}V9c=Se)j4muagcpyr|&ih1=`I+O|H_g-oSEOiq9J{13`7q>~wF;~mV0 zu`*#o@t36*_1kx3ji!+p5qV6K{&YZ)A+hk2`yGGq7*SW#j!~U3CqsU?cFiEcua0VL{II)=0-RvSZ@rhii%9$H_KT6GH|!QSexXy z*ErJ+o<~$n{rjmR6(hn9Y^}Cm)PBVh=^8$5Bkl*rjR@)#Ne?ppbS+NqsWJ-YeBJIc z@Aohqa%1LMVw%c7L}F#R^Y$Ur&61_6FV)K-8UKk{Nyh$vx;mWyi9D9N{#9lCgt{^) zEXW`+pF(2rn5SE}-gHPiE=kGhIbbfd1&CI|$fSz%`TvX z1Sjjqw6A4YPQB4yIllqRw_hVTQ~*6)U=8V*a0si{tovfgi)@}=SCuDafIMy5o=BCb5}$A%z}uOvTv4Uh|j^?1y?b9 zaU=^f6$Vo24 z9paHwFA9FKsrCyfx7PrD%paFG6j5;cX0=O#U1%Hv3Xm;MIJ`^yyf|W0ZXWhk*V)Go z>6Fj-mMg6TQ+q*Mxms&-N0Pg&G>etlA08&IogsG;_#Yp+Pz5cxIDO8^`J<%{kuHm? zO?VP#qlWUr2lek$e-4!l2o2rr(0ATK!M~m^+&!g^etVKfHTOj=^TVIb+`OL;HqW2S z%fLpF4x|iVpaF+_wb6{`Q5O-VM;qjUmmetFOJ}q`amBB%X>{O5j%q1GV9h~BQZoc~ z&E31+eh^>2h0`2hRIC0oujlMu(8}~K7+`9nhk^trG`5IIk=<2J!cb4ZcT-gAT7@x%<3Xk>?40 zOJHEGLF?Yl$WH4%?k}S{y`|jhp_f*MS5D@#ZQIeY$W-VRmG~P-v()vHOci^_p-&<| zK&BHZOJJb8Ee8)w5Qb;o=#wEA=(lgZv(T@Rw0=1yl4ezp-;CON#C*7p?U@wWO}d)X zVc)TjKK4ud(K2)_5_B!@>sLIGdxLC8FSqS}ncEpL*Uee`=%@jnaI1soJ>JeXwrU#Ype0}Cu2HQs!Tslj)j9C52NX=)VF zP}`e#y9cyn9Ekh5%QR!>q%S2aU#Huo4RpM>8paOBy_et;xAJu7a}HDaW}QT7^{KbG z6)7GoDC1^SmE*N#K%aj2K(A+VZK!3sj+3LdxRr_n8`*>?(bZN-^;NkX?Z}T`>N|WD zK>Hk+z69d}5`vE;n-xF{uP{Pqo_mQurlj|hsj)UuK>*=U+JC9z-iUU4rQ`e#3J_*B zLtxeB5YD6yv_aRiqJ3M7WdV7B)pa~_E2LWpNz|!DCeV-oC14a2TwNr9$Zcb-&yFmq zKU47jh*wRt@;P*|d@os=#NRbFb0x{~1O|{b6wFf|(sznfPSjCYI=xGCkTy(hDY+^P zhhZ^~i!CxVB$Q~ zpqVk%T+(`G`7fk9#GDZb$ohuKr*S-F@DnM}0jt@lqx&&R-z1wX`UHnBRenCj8uK_dG zfS`7GiutWE>46GETg@w8d&ZG8Sj@euw?%#BBEW8rh%CRxBDcTZ_81PX(im4Wp+DK* zaSvL)E?t!VH$xA(<++(R5-O21U{Al+>;Jr7B)AY&Q*?^t3s;kqaS}u zlo09V@)o|>xlE;)VWO;>3<*c;V?Zy_0IO)q;6AC{3(&R!`RniV?x$*P+U(@tzlSQ# zAdcJ0n%|m==r-*9!2ne%mg~oEviz5S**O7Y0;4vZw|$kKy4{DopC0zgdJ`qE(c#q}?^bE{I1T9VF>v5@dZm!ts#Hijy@Md$Ut~OPQYTEu zt*HvPaVjZ1$SW1+{%71bCVWe#pxOJbDNKT?{OvT|u(KPDaD;^R78>K%TFCp;eP!aQ zF!NET_qls3-Z)(<9_OPo)QMY9(gWr(Ci^aby!l?zMLaD5Tfv+=gxW(pV@eU%7LicU zc0cN;pFsib^UuI|Q2KT}_>?@vJ^RVsm0eOKj~=(7gGN^7>L+C$PStQj{R!zjo&0IT z$>JRrtN*IRu}Pq&5EjyS#BYw*34g!_55?gzJwa)MYshzO%ibH3bf0!|c*$T8&hB~Q zkzwICkb@8(h_kAz@CjI?c%Pf!BNU)`^LGL4%CTQ$-%IDldA&09bD?*KNEzR~^D#la z6GXzmnbb}RJLXjrRg!n+Rgp=WfradX%?^l>dvv{(M+Z)K1xV34h1C|*;nnJf8&P@v z*T#^vy6s``i2k8;#V#w8MH#LiA;lctMBn5y@!Y)ZQhTs?zY|1Jh4)yT8!$}>gqX?| zSS0=SC70?n`Zcem#cPf`4dnWb zr?baH3krGP++&x+_x3;D4zX%U%(=bjuxGO;>FE}Ub_;sw+a}!5!pR$dDitvT%Q?WPvE=kge zsfMvhll}gCA*U9enbC3kAPDQa*mE_R(ffRE-gz&FOyE$u z=HBLyuXwRU#?F5i0wB%3RSTcl{KabdT5;SG!FjIKl9uEc`7HSZ;j?@h5B`dBMQ}@bxKHr9f|5 zL0Ve+J)T?8(ZVz;rA6Q+g^hF4REvOM&>n>qrjem;5fNXK;5VPtT|B~BRPA`1)gCVO z8Tbt9v=>y_f-b2ShB<&KGf+X@Tl%6NQ0*r?Bk-)p*Wl|Q2Y@ySd~Rbas2W)aR^PS- zCiWyYYH)Eyv3Hpy*~kQBwdfT+GziJui5R^;ZSu!5epD*Q%SNgNn=jfynFFH|Eb>41 zme0(CuS!)M%vhI5Ulp3YOfp?sI7Vh&y$_-^_2M%P`c(}MVoavLg!CQ1|8J^h@n3lBWzY@(pqn;Kw^w!nG4w#A# z3<$ZkD<3&JS7KO>9#Z1hik*Fac~dPt=i-<2B_h^w=Ht0pAo}2X#y4O(-MJDmlNP(_ z(y^agaq|jUJIFdzT(*#=b*`E^arUZiOGOzq`~!N&j-GH$4drxr3G?1e+l7)}eTq~3 z&M@-u9G7dFPk}$V0;Kmwb5$SKM-CNDg)Z(m0WpG!Qoz859)VlYd6$#drICSF$<{w^_ zFsMk)F9b}_w5m6w`81TC=)(f@{@jf~BtUA?BE+8B*~S$ z95#qWOzwJFUE9cktAb|+4UNiaTeysd>|RT%IP-h0t6!KZi*d{JX=v6z=>;Rxkxh_m z{!uk4igT_G=oP>&xYhUv}dMFx|C<8wX=>V zwN4YP0-lyy`Z$Xn%*&C6712v=;Vt>Y-4y&S@GX)9^A~uhBj~N)Bgd}*-*&T4%xp7~ z1ZIh3v>%vvH)w9uo>;Gw1-HoY9hT_*P@paaIl=E+8b3PGNXNzU8wc=DeBMGSs(e0N0f$xqK^+_10;8<5j~`GG{zviW*`| zky`GDD+xJ!~CQ&d{6m78+p}cuPjhbyNS$C{b?w znve4%JnaE&s27mdX_dQEs#cIac||VG!?w25Pnn%(=b&4#jn}D(_*(65eKiCUbwFE_ zV<#^@`7OL;@RsEttcBU%@HVA~3@xcUP z%CBP@er3Ax{@qt;Ku@qQnJ@%NvBh8X#Oux~Tst^i&QaSsV^@qp-g}$gYDG#ciyf#V zf_*^rNCS@1(%4WwKr#4_)IeRismQ;%4zC&pI9{Use6=YEmR{S&((j8~q9Ec``Td{3 zqz(=tIE6W38$t?AQh$>=9Oguw>K4+CKY0~VrZz9Nw4KrzSTOWAqvHc!&K#LNaqxi{ z89lkhCW6xWd{Tkj{L7NG;}4dO&Pj|u9MS^@42PqVsfUPiXzZFXBnml-Iq*mnDC%yb z;Mha#j$AXI(SQ{0tRwEJ0OdN`FDpEME1^WmRXiT}GN~Qo$jA?N8Fmld1YF|RktZM0 z@+CY$0zg6GlTV_yis!)(BroQ-xWQSY=A0|?bltBsAV{cCuB9Seo3ZCzIY#-ke_Q86 zUGNEx5s|DjTZlbqA7#p*7L>`nfWYFoA-_KrsqsJOQ5_X@R)zH z6dJtGRP)A#+NM2h$Zpx7-<+%+QF3%0xz2|0_F6uWxJC*nYs0uETTWC zlt$+A{rw~iZwM)B)WWf~B%sN8>huss8I&%SdV2{5yh&2faZr&8R51T#xk59t``K0$ zP1-AB;8CR{i4SN&(Vu(4F>h9Du9~G9nC#q&=I{r$jxc!bX}-tA@3dql ze!{_&%4;McmTqjc?R>xY1RwbBP*5K94=;0`54*bzF%ElEQqkn2Fw?U87})o1Qo9`B z)}2=-;jZvt+2)%Eb1mW+#U>FPzzgys#(Zb?j&Nx>@w!+8i*&ipbj7IgzH0qlMPRA? zsW1&|6`k)#^zFBc+IkFkX?{RLs~})JX_udUd7E5dQ&7DnV~R2_)jdd`~a=mm`c$y&G&zo2@>16 zOJDbUs)*Wk)u8tARkAD|A$bwh+g8>P*asn!@(lMx%x}n+V2q5$(eC#>q{97RDED zeqt~A-SJ0p*BDm^p4d~IXZctF~o+=nT?2-21e_ zb%KjA6fz15cr5Jf^e)5FuLk`nz|5p+izGFy70deuH`z#RY;993Z_v^H`H!AVn}e=T46`-WAbGQSj62yD$S*lJsP%Jx zXb>8Ogxy@u+|UhigoAw+D(r&bNrY|j1^B!X844_KjO4lWaMxlRZ4r1;UUjxH&!Ov= zyukmuLOM)627vnnjOE#KD)W@=lhpX-+b=FG(Ga9ZS{f==@`N?y;<)4OTce_dV5-=QB&dBSR4?%)n1GUVKUl(OGo96bD0qsCj0`lez1 zqR-e7ANCeu)diQhAgfwAVstfmK@0mDd>Ith)09Qb3Ga* z(~H-sx7(rxz1U=Z)nwB3__F+NX+q33#|gKeq2JRscjA9HMa%vmnal{^A&grCZSH{E;IqHZTx;L7uB zWRM0i^`o`Ev_dZ6&NZQ(p=KWDD=VH<{;IUk{WCchbswiZ|LdW#2|)CyjC)!?4I6_- z?cN^tMT7`CQkVR-2Q$1A8X053?7XG4wkyB*bXVau=hJF>f)byouqR@&QfdtaPQ={U zPLfLx1ofplu>gY*nCjU&Ett?wDWU)1R3G6Dix+69V-JOrOk4HRvYf{mrnw*&s z++G^38#MjuETG+n~lki0b%h3;82@gvJO)=5GUQl65_!wB3oKr{CVIXI ztpzesn)r8tg-mY%fIA=dAd+GA0d`qzFmc_K+=!*~-c6$n1<4?Qk8)+@!{LDeK>HE<6tJ&@Ihlqt>aIP`Kl|#B$+%K#>pBSub zv%l84^Vg7I1fCuKJoucV=Zv{`j1W1JQz493-&-}q#d;f)s+fV~0{3;s=V1Qp(T8VE zPB??q7rhGK&%m4yEx%ZEzQ<)q=fVlGXMVgA_uzEA;UMI7U6wsWBQ^A&jlu^KQ2~R{ zv^=3|^s1r4aW-WAo$X}SYk&WBme@ShdoNyEzwHa&xQEj3Ayo}m3RHHetdr8>W95I| zqI7@j;iK6ICGrkdhuJva#3ZBm>6)Lehl(15hfOqDNoBVuNCoeNCWzRQDKwRIub8Xo z_VC?4H-_x;&xH8yLGZDKa8<(=BR4PT;C><_;cc2>9^a|DC=$^>2X9=Zf9U9?N)VZ{ zAfJchR9IISmpuG!xGRM_RFX~}h}9y>e|&-%@niXabN}RO#Cl;oz$*oAh;Yi`FzZ;v zsMco0dy5@1kvofPlES=*Lu_ugM)*iM^h~8!Q@G}}nLPL3gi8p! z?r6|_X@u#988Ii_Kgh3l4I`HFBOqyeWvIN)#nB=kz)cG)RF>8j81ifpzF*9NRzggE#9W?~F@*chOR^w-_EVCybD zt4}h?{uxBU)ai@_w@7 z`U#uFgWEIN2_=5}Mp10YECmae`zj*BjS)5FyIuK@(~_)YbY*DQHW;p2(8yb)!N$Sq zn#=yOysMhTRYl2o{d01DO`#_=7?&8hKU1l=Yl$okNfuVzs?6^gEvw}ttpY|ydD8}7 zX^uJU5W|?5b?EfZ4Z5s|h;V}Mdc%Xi55l9!Qg~<-N8;TgQ$3XfSo)aPV zSJP3rXGpV|4fM5%+vC0RyHp-t0aTvy74NDXodfl=)W+6Y!296iNxp3+JonLV?%QAb%r!6&>S}#tViCj(DgC`aShz#+vJFgft z(jRR>ckhNM!UoMJJ7t^K7oRQh|GKE*i!(FrU%TWTb@2_P0dId89XuXw2h>K~hL`h+ z;-x3Th~H8x8rVu5`0%5SZc;XL>*1fzT5l7RO_wlFoK^t_P48i7_yN*Pl$gX2v)vHu z^Bf-)-*ZU#*_rpJv}?0JK|yV3hzJDw3tAlJn3wsjy4U0$_>vV@7oA=0-(xx{@xZY6 zx7d@rb!%Uoc`wIMuEXOY-e`ndDfg<{Fy>o(H=NIKi93%vcr?Ju6RWL)fe-VZL)R|J zOZg(#Q0Z_eumc+D_(DirXhVKqXdFx*ud3J(nGjzxE2-KHd`0t$uG2$q0@u%tvY_yx z3S<2i0kY{oe37}0Se|wd?+3KWcdfTu<|T#SV_QdkylxWShfB7jmGKQIc)TQj1vXPf z&l>C33FGpKvfgptq9Zgh3Y$5#fL&mR1WPW@;k-H6&$oPwh-V|FWX&qW{$2=U07@m` zBuinxt{jZ;fsTfr|8WFmXTki6iGZ(qxUF)4DDqy?SxWTgLJiZuUVtcY&Jd%dF(Is6 zHBOH5L;U&A(kB@GlGvDFDF$==xi51zkLh_WH&LJOm3T#kA(Emb>a9|9m9spxfNOnb zJYa2o)r(u^$feWDP3^_OrZk-(&9`*yq>(hPT$LHye9K7Yh zG$Zjc^++l;b1pByslA>xL9J$TvDsjZ}e&j<)-{Dc$82yIo)fO6W@{M$31?_U>M}vbugUVFn z?Dw6wX>jjZ%G?A^TVvnG(&ZI*#u;`5iZv ztYNm5aw7H5T^VXfF5qX<4H?5BUg886vn_6qsqW1cFj;G@b#`!DpXA8rjgZ!@l_2O? zthkB0k~V*I8D!h^@TtL-HK7dZ3wXDoWujvwSdNHY>R9%&&S>G7aqn+*XTYOkcHI=` z9HWZE&Vfn^UkJ)8jQov!Kzrd1NOoxCQh?@u@kg#NjSi#4A_))yqVd<}4C=f$k+FRoYUtm$+wlQ| zmm~m)c0B$Z_{nZ52Kc}$`KPtQ&nQ_gx`FzPWx-L<|4|B^^Rr`8Vmp>Wot?N;D{NN{ z{eVMISeOeqvd^Bi#O+e)v;Wbg4r4IK41qUNb}sD~b{FQyE#Ekw@}ws6{C8E!JqC4s zW6bx(w4vN;u^}XzKW0?f3w8e8T2h^upf%9ZYHl65z(T$O)nd$18Bm_$vaTfqMm2{@ zW;0mFDseAETm*l6Gze3LP}Ht*>)z&D*c2dso>%YV{9=~1JoOmF5mGR(@{990USq#BZE4LK!0Y7%Ubz0Y{7E#x|_SI!TDB;bTSv_TQ`#l0u zAF-T_fF!FNNu8Y&Y*5<)-I-WJa)57C#qI%ihi1tp39;w~^#{3l-#dC=Ascx9(RW-& z3rL1KQ@;ETNA$`W50iX+2yDOkRGUDRe$6oqYhRR2J{eNOg779cVng|rL;D5HGH|xvvlCN^qp4EUbUMSj z;+|`?Pjcd2+yycH_`cO^>ny*m5J$}5FlSLsACeJeexa<(HjSsmxod{^RysbDw63M4 zB{4JeEAgw_^nq%|d*;~l5(m1`DckUY&P%Z%KNh6cw7)z3HSP>TP;PKN|2)3WT=$m- z^_~#t{sNSK&zQOh>4b9jdqUnV&9!{G@Tv6og%SGHg8`WiCmJv#y&74o{uvtrhJ#tY zd_IL|(t7BcqWvc5m3%LezQ@S(%}Shu&I)aPlqaFWhx^{|(1?9K;L8V~?&x>NC@*hx zZ(rX|y|(D|U~Q`_8qk9+6%b2?zjm+S`v6b>12$XbFI&86B|{0LXOzY6hWD-?oRSSs zDcn?|#V+JfSI^KTx%|l5l0V?e593V(KT=Ct_p<)&)6@x0$^X-)@$gHtR7=Y}^I{t^ z$i_)=?qfAwiWAvCw7%BGICy<6FP%Nl&&zu{aCt0r^WkcB_%h|JoXH#q{bAAl;^_SO z?6+#XX-Dqh>A3FSUmZyEJ&s*#G&}6ee~c>l!UItBxva<9COLp&6ll*kh!(0F|K^&W zmUdTbpMJw<>5zLKbv=JQacKIfq!11Ct;j!WcUI85FE8!a<-LUAZOt>$wu%ggtHuQ! zBNuL}L<8mi(C;sSe`tVnj&N@vM>}svr2@iEB2f<46)GvTPj;ZX9dC|lisA-o)}M$2 zB){`bC)CD@8*ZLoxk6O;$HA}Lmt)TwL8=Z|uI>)LLC379q20`U3I0(c#3&K7sQksp z^i{sMRK~vOb8+djB%n_53EJoh#ZAczn0UM=q0LE$Cz6^OJOd?;l13QOKj2y_Wy1|| zMSr?$lI<;wauM0+iB%G0fR^qYRg-M?b``N(U+?0wzFggK80j0m9J1teG0A&qu@!r< zjRQqx{E*rVs6{V=0~}d?l%lVQV&pGRybf!@akt6owgEfaa)m7D1d+)AhO2Jh(I>{q~E? zkN`+7b7CG>kH=^3_~a~Z4YBCw^Q4*WZN*9miN>B zUc6A-(z+R`2#IZq@0r_STcOgqThp^a%X94qb-j~wL4Seh#7?HFMc=-?)9sBxW%v(5 ztja4LrR(fUDtIb8HK~Id!VZa6O+Am!g#o+g zE_~4?txG_G89)#a7cc``tv+CHghiQ&R)co})NurPXvYtnlg@)W+#brlhS9)_T(4VE ziVSVyjfNOKPwYEnsE`2>b{ARa_kh5x25_WZ802MndLxYh@O5fg9z(!${@^%C#!p z8E5d%mUvRO)+W!)n5crH>CY^0VY}_5bYDBS@NUeX=&K5>RNB!Nw4?eid3~{{Ir#rdGL3D(bRN66H za#{xnTTu_)wsMcK&;~2kIx{iS2SJ?At8|_s!!Cq=WAY|{!;?6#K)GzWPZm9X$1TDf z7!n^@UkMT&#M_sW7BlrFmXws73lT!|^BdaOKKDIfU>l@6!XP8+1%6_qoy%Xrz&7A% zDkp=mKS5uCawF7qcLt25sy)4-oCI1LE5l(Hl3Q6VeMLd@@GfvI=hOGd$gjbTp5H6+ zaaEnm;jb?~`Z2O}yporfS2eqC*-dzd2B(Gj;Zcs4c+kb417k<;9Z%+u>o$0?&QUDn z?l2EYN#22K-tWEB0_9dedj$>JNBZvOwo&HW`sCM}>B80uUH+{-y+*lvLYiV@DC4yD zON*hShPzr@r-=QTS#g4bhW1T1>OGliPXDKU95?~>ptX4x;AXwI{7vYUogyf1*sl$S zk-KCyXD&rYFCO4D|AmJK-lQY`_z}BQP8aJyj(39Nl6JzXa1W==SXRt($4RE4D4g*w zhgcxl-bF<`-&NC`GFC+xC~QP192selMHuxG%P~M{RNjJ-} z4X~YI5Rnm~4XZ_ub7aD_3=_>buk&L__`Efj5xZ#1_CW+B#0J;fOY?p#vyVi9>Y7!d z!>A3M-;J0COk73c%#u6LCKUre_E~=pSBu8Q!K35D`*tLnkj7alJ_J$Bd$6==E_^Gj z1l_`4$MC#Tv!4Hmlhs@wSikx@#ZoE>X`~Owc59_5wu-ltj+0TeS0Fom*7{DuX zm_Pm&D1fj0=?k4u&1(E5Y~Z6^UeFrf|BOjnMjKY^+t(ia=%5+xV_>BI4(W_V{$0#*@I_eOTZ^03SGE?7!HzQm70#e`%~6(50W1li9LNetR0vOwO?MJ zTHE|#4>z^b)2GXx%_@uH9AvUjkGXU%#}T1FT4=#{c0SLM%agvDRqg-6OalND$H*51 zEf~&kxpih@_6LmEPA=R6hc|ZEN^JWGLVl{g7cH9$axBnT7*~W2K|_;=6LQqPAtssQ z4>Cl1wO{sKozsdkGN%1IScx*$O+?)Fg0WsEORw0?A?Z&lV;9lQ6w3X0^w?&;be?Q? zW2mM)>+(uuo+b`tpe7A|+#TttW%m4tvcQuNgT*DY#AAyrDV}T@lsssjZz(3s?02wK znYj>>i6Gd!z&T2sy|J+n4mMJ|&zKoxgJdlw{Kdt0^6!0La$L$~Wazg2mz`Gj{`CfA zczTcmwsihMdnVF&(HFsH7VE-b;X#S;I-9Id!hL-!(STW6GPvW-YFiVQaAJugkQ?fW zM;6WEuHXaH$WNBDZ%?2y^%g^xZkf|;s76Hda|e)&DvJM}JLz$uN(iq5FCoB5K0e5t z3?|m_w@Zs|BhhvL-!mzDu1Eyo4Fx=cbPZlbR97qU^nEarlrdC*5j3+NAB^}rYNcjBwH!9jdfjts@Z@7-WA5dZiQ6B^7od>q#% z1EOvw<#bq>s)XLv5DyNH>L9YyxE-Pb!>0os(FQ-fAr1uEyiY4C^Cul7A+cJj@0j+& zx)GN1)&Vr?BVyD(<&+;h+|Ss8`C-0fG^*Z$0hVTMf2XuFYd2OqJwpj9Xgmg(VZMAb zS2{B!&icvUv<$h?LG+S`eGFr&Sc$hgR!%9MHhZ0NGV;kY=(EBWILRzbLmk9GgXHag{MRs9_XRTu zeq{K<%vHcwI zdW@l#(@T$_NXf^{6zs01pJ7Sx_bg)Ln@sc?HEr)$n}U^ttJ1=MxJ%9q7P6~2HxCoW z+1c52AuVGEFSS=MdxTjUtK9+-HWU)E@Q<_ip=i1f8gKx5X!QI`N8^)cI}!RHwov!q zi&-F8IPQExb?s6j2KzgGk$UMi867V;%0;R~X0|;pJx4IWConRv-zQy^IuU7GvuO5@ zZ1|i4qqA)!w$%l-<792{)+qDu>=r#+(kHV1d$N%XKzk5@i13I_Y0NLXvyI_uAxi02 zfAgEj`@X--C&dzUgRfHv33#dJ#wM9EyNV!+%R3jjG1%F?8c6K?EhBU zlkhdj+%EXb_SIo2RUbMExB^ZJduOlOf8r8$Ia}Aq;+uvD_Of8LDZb3KAfZdWv#lhx zz27>l#p99Re@=}o@Z@P`{D=kynYRfk{P!IJ5Jw!kfIP42NToQoldoVJYQ+)(9hwg6y>`y z$hy%#6RE_x>Dkqh!3+r$UM~RL@e9!+4GmqJ5%Cu1W?{jA)YK#~D*ho6k@K&C8?4SF z>LfXfSRtdehT)S4!~^e`oS2LPQV^AU35%~Uk`f}&VqwT|*cV~@@T70;f8?~|9>Rfp zhsZmK``J7OSZ1DO?bXKqmWMeF-OW9HLjpDAs3OmQy}2BC0e`;&HmXN|N6HJ=-&lmS zYb*(cJWTk1qAb_-cPbyr{aEd=S0L9N_y#Gbwl?vsSUv$5{fzQUky4+d#^tMf0wYro z(}-K(fV1D!Cu?f}gm(1#J28}b(MeuQIgfXg;ccNd^2v4 zcu#Pw8)7X$KffU0029kC&P~lO-p}&ysJpuxCqNbw9!;0p&(;#ch(Dl{tR7S0cjEC3`t-NlVKZ+#2Ld%gCtyNTu)#@uoNF-WCx(Dmzgc z_2~Uh<=Q7n=vl<8UOo2Y37>bwIA~yGyjS)w?W#-{(nMu4yAy zER_f))Dx)P3lhbCRkO0TZiYmA^@lTke0NqREYt(3yzv_bCXK zit(E-#D0g={}NH;zjS1JbVIBnrKc2}l^w3sl znaf3EfdF+pGc(!+HhCZL7OGbNi>-R;e@UAkS{OfiN#9`uznTjETlcM(buY~eAV{L| zk`iO-iQN3q#0X`f$3p{$q&~b@FYxRCmK{ZB6FW@iEaIZ7v5}Xm0f>yDYxu!x`rvY- z9o5(TIzi0H_4cc|?b`0Ub7J6hXNxDCGfeSHOcn&hgelyI!%k;{s_BU{ zPc})q%4ned;rY0FAO-wSsm2Z(G%x42>>6bo{n@aYR^}XOfR~u0gA5wPrm6;V^OP5` z;ZS97^L64u^a!MFl8uSa->x(9Ngq}-PVSi|T9POdO%@v2`~-_b&m@6H1Xz61|0Ulr z?ux^|Yh<>9~5+iSXB137`$Dp2CxUyM2fto;=)NWmGr5Bd5-HyRx3q?iC~&8F(-VCF^`@OcJ0dzbC)C(KZ9{V%{E3ofUwjT?Cjf_segP*M zUh)+4w^&QJ&PF9BJ4B?e$0iHo5$#o6wW~5!; zPbmyX&Fn}1WXDnwtBN?O2fjKEN`K%P`#3+@ zEhM};@l_?tDAW~U`9h-LLIq6p&cG^JoW-IAa2+Q7`eJ+cy*Vu4-XFiV!HEt+@{hq^ zw8XU)2KFonyDE}Q7F^#Jb^qoaWNwE3&sY+)FmtAlv6-1~1>BD`nLLhHmSIs}JdWqz z5Hz~-aZ}^*yILEgQJnZDkb5kDtNzdG#_?vHJ$kpjeXh!7G`5$3Nte%CV)cB<0!3q8 z+C|XX$0b1K4A_v$0X7TJpnSYAwTVQ4By?*NHp?fTY{13}Qs|5e`h3yX&|HM8YabRO zLgJEUiuaT;)Bb9#&AtBc1yT#|V zYBrV90bzYkGD2?aSNk(nVY92AM0Zz0NTF}r=1n9ifxQY+C(EJgig=JZDX1kZsfTFQvb)IE zB}d;GUeMjbZzqHJ=xy=Rf^_ydv#YMLdW|Kk!%^6~b8D8Ti*uHN?n1eZzpRd;0f$so z`2djoY1}G8OaGC%9l7|~(&YY%@u>FUcYLa@b^DTFgop$m* z>(Dyy4`k_B5XwGi*caeToai(O-ZKl*if&ejPy4H-MUmIc0$hmMT1QPfZCZmpF~aX` zXDdABhy!R{Qg=HdEJrmNES8h{aD{r3TDf+Ol@?EfGs^ls^S`{j+DoNBTWV3asf&pk zgPJDHG~O4Sog6#NKwF*=%=o&`oROiDnB#R>jF4YT&B?acdaCg&Pb1z+Ch>ZBhfEd! zp}=cx3$b_pQ7}=K*C|(#p*~2|;yt|2)tshM^#aXXyq8l(g2X<++E!wQV;gxR>@cuQ z^PJc1UU|1B8yumzd!Jlek~AD0|8o8r>eMBV%}wB#ZMU`6b&S~zrp1{1@ll$z=bM4rjSyK zzR%xcdmS=PE{~g7&(jrU#rAdWc=HS?1>Lp$tfzSueHMx)0~!04_}HX_8JR{OzMO84 z?I$MWI%K;LbL(JBO)HXWWY0X@5gGXJQVFjkifvZ*=y#q;fy~R^Ltd8VJviCMOy*A4 z4hv#ikJnjS#r{d=vs{5sl|qf9Ck|}xM+F6a<~8gMwYi*C;~u@4MD#wAA)1=njBr|- zkVe~&KnCwn1}|Io(6ssu6h57VQ`z|i2~p(vQlL6^R>XoZn(KCsu_gH7Bmm`;c>OP8 z`Ism8+^Bo+hiGy#$S%RpPnIBskLsToD2`&ab73w=(O$yEM#|?Ij^&xz3m?r>9EQHl zCR=ukxoUjE`{h(JRI%OL6Wi{z6EJ)&G72I0*+0=&OreM$ttGx-k^#Lo#_8_<67G$a zMU(ANV&!B z&O8pMb-JQ>43JQG@lEDL-bMd~dQo06!_|pFlarIWuM$aOv(gcN-u~RXB{pUuCHylL z<{q^OP|+T^S$Rm4f!vo9deG)GxV3%)vw0~y$21szpEuu@ow)mC7J2FknDC2$Iyi9p z-}~??xNjrhLj;(WzczZ;U&9@`87t_=nO%&CLcYNbQpr!&upf48yCDld4*&y9nfi)_ zJU&|q382s42RU80#b^q%H^kfVCSqZi_ zqJyO-wC>Z)ubX^+v0N_THQ6lQ_`Zwr-?jOYahQ~Om+Q16%VGvh)2&|bIlDcO>@b_k zq*V%)zH_TW%41dvF8tBAYDa=s+#8_?y%KG+_uLK~5+i0yP~(gahTUoK~xF$u4y>h7o0 z(^FdfG>Ods{$JDX7jDhu+w!W~hx0W$yS~HmX_9D0A!GsU7%SfQhEngco$gAm zsB#j2EO>c!M}(tmhoC=Ez+IPEWrc`5KMeZU=~QWadc0emuAT`i=Mc2=V@0wY&N8bC z!~UYCxZ%bv%}i14fptO+YH{DzyV+eOGdb_-$jzThZAC*t3Bit6{_4V?g<$1=@OJ0n z@5Wa^%`WsEqcEv^ie+N`J^+r5LH%!u4_+n@@YCB}JVr<^B4E{mXYI`oevLjyx2M0!M0{O7-d^zDVTk}K zM;OM8F|hCSD~*_$iqG`R~wd&OVCaRG5_2BL}(pDsbC) zs(^!Vt*mlrj7UbsUjB3KjE<`!nXuex!KtkuQ0dU*ck;*gQ8;ucTt4oVRR)h=uPP{& zh#tiiJLt9AWSEf#OdT~CZZhY=e5xCe=k3B8x*7tQ!aA;=QEq#0Lq%hkKCk8rWsE;5 zHs&neViF#BodJzRPl=;9J4XZhi&iIM78YbzQ8r1A8Tglt=laJt{n3~(SFo(bZ8rmJ)cH-R~3*B=1~=@8)u|a+x+2X>wb$hxA_o zHxH_wr4PPqyQQ{}l_W!>J6Nhi`x}*AY&s&r?m6Xm-TC$Du~S~aAlj_Vxe`ZYv;BUB zk&2;-v3K)x*ve~bghC2F8L!M;`$U-_2hbXm_?6=p){W()x^9P$kU9Pw`g&xn^?VLO z#>|!LPJn5^ck~F(fqyeDq%`KcXgFAq+Yl*IH?XQ&#=9ol&^4M&a^#8tLdi^X4b-LZ zH&oH5PB__1^x{nEyQ!P)u#@No zm&-Io&3!4tYz6)@zAQtAgB&@D`Ef05#SxrD?|ajc_Nq`N z6a86&>Y{2m{V<;qyvuE^O7^$oBm;7^K6G*K|F5KI{bZeDKt5?CJM@Tfp}`i(W5tPx z6*PEDN3DK2Uz8mLk_cPG?c^BcC3%D$L9T6v%wlMBkH5#l_-)EPBN2W6zd{nt7=gGa1`)S3e-^g*Oi$HHl)uvx(i8UH1Ms@lcUY=sDFfAu^bLT z8VlNP>o9dGWp)6Lf&aJ+`!|7l^T98(BSv`iJba9}hA?tIpdKDY*E%i@+p~&C~*w6(!d*e9bz&7piwy|$X zqav5R!|}`FZO-!Nidk3f6)KnC^$LRf?S(uG9FT1&S(LXQRw&bO4SlimTew-CXKJH z%^cJxKr$JD(J8_5oSFoN-;$gYJ1OR;6>(nrU;B=oC322e8<#B3-6sU?boC>X&w0g zMaEk6{$zBMo2Q&bFt5J6BI3|mzI#Ttf;<&C4vv9XO>bC}{I{2{*Lz~NOF6M28%7z{ zC@S>kn=(JzS@j9>K5IUm^TTCPo6jEVA#rGr1hczpsAghDvY(HfAAeo3X4^0MBn^#c z7wr&=*P-l~i~6qZZ!>xKRQ-^9UI|!C0>1#gF)(?WNFVX z?^@6O@ch-PsIe8^j0Lx6<~eRu-Hk!wwwa)u!~9ARU$#N0b7UpG9)=i_jL)_lwf1$h$&@ApA!2V&<4ewpM<^YvX6KKFaW>&}^q z=hB$K3$@sz8ip#;d~+@^W}NdwZ;CN^FI!9es!N7cql^Q&$799&4Iz&`{HF4Dbjy*} zAvy^m zz-wS5?iQPMJ*<;mc0VOM%1V^2W<;*JD~O<{=Jnj>QRZ!<1)EjqP_tvY{Q~;3a6uz@ zHuoVZ=86Sm`{^GiqJeh^zf8NwanoY`#AGo^lH7H+7GIzD$2t$Ht2lHY+Sc!7bSZu< z1_ieyf4(Rij4x}3oMo$gT$2;$ek(#B{rpeLRV7yMusUnyinDS5SBp^n-Zzx_!nZ`U zB;U=k`&pcmdI1+94Ayp6TR4(PMnLPkzdqJ62JiGGMTwLb?|g{Cf)v=;=6*^zT*|@3V@L$t=n=U z8DvdpX8CsTdcSt$0YylJsf>s3R3!b3&DthKNbZwyQj}zk{6odTlZCy{@nm7BW*+dc zYvsAOYgH!^M3-Qso$hxcXBEx}4vgn27LG+=yfvg+_kmxAADh)dWl{0P8sUB9oMx_5 zvElDq51SV0ex9lP_SYIFHz>a>2i;rz$4y5q&d>Cg$pGyP2)Hf!GNU{1yQ4)1T6O4< z#p||W#`}dU+@w9Pjf#e8bz=1|BF3%Xm9+gkz_caj{`=u`VEa!xYZyR~ zRHeW$GCj2X)d=WSv=LfL2MeCXc&{}*o^9?>Mb zX8H|w-(pXY8{R|GuYYy8Twvdi=9*3;c|7x+S7Ji6H&f?103e%# zr0-|OM;?Spqs-jGjTYGI+%;}4hGT@VVfhHQlYDv%-y}z^%jZO$eg80In|79lFSjxk zmicGqI5_W#O;qX}m!r<*4epUW!()YF>&gCt2-5XaH8#Yu##$K!y>}@{K^8w_qfl!I zY+|FOI=Ch}03Jv2&G_D@M0a`UL<|p7g`J=@G&x0d#i1vs^#s`sqnq$LFn($p_2RoW zkEo}>XzMFh&>|v2%K;TQ*zruM)hjzk8Rd8(6G zvQTONh5E(LwY=EtaeEEV^K=l!9adJDN1pAAf#Lf>`HP#WQdz0G5%N5JtR~;-qj6)` zzgbdB^U?2ti}&mFAIK=oD+a1#wtuM{d#q?Rj`#h(Jr}Nbco3s8Ofu8lT7)r-W$)i7 z8T5~N0Xf)PJbJuPdltP}tWE-TKRduqyZtd082Wo6!tmnW@wlr6I&h<~IE_EsQx-DXp6ccR3by z!*`kqR2-7s&R+88@hX50Taeiqe6r#JK4D|Nb_K3$z5u%(cfy}&r67#>9%FG|6gcht z#&Tb+*hKoyD84KFZ>^MEL6QP18t2Z?H%`woh=)k=ZWJU0Y|N(;#>SUKK8`KKKJl>y z+iz}rC>V^?-A4tgSniDM(6R$gaUZHpzMZjbmWD8PeBp(}77TZO|6Q08QE-!#WDNb! z_1+sv1FqKx^;7_Q5J55M1SGX@OXOV$6_<`_3+F!iGx18P=nv$GuPK!QD1^;kvnm|o z+6H)x#QCwU;F}Fijelp{PUw?2p&E^L#{<|)jM09d-?~OvGfy^;G3I_!K z!cTPpdXV!Psq>_LgoQ?_mXZ{A0@|jFrab^?RQ)XD@KWorQD6X0H)9%ZNT{}mGpCn2 zA>!LWEZn8^N!DLG49K?%wBF2(>9I^wJOVKzKDE+=0$#NwX|7*MN(82m|ePeG7;8rMM$x6!DA za~)X1?$B)FNZdXQYptUgCvC?G>fV=7JdR0>2%5<25bZKnDJvBJ*CO7gVdQ32Q_5@= z%$SbLaXY|(#MiuW<_U#km3o*>**EXcw%DdS+VP{qMS z;qo}X9|>d2j$lzHAlJEz<%&0(zlEJ_HT-17SU%O{skJ3W#4uJPQ$EH77lex^TGX|< z)G;`J=?RTu-RlZth>~!i+YBep3!lp-vx}82``~#VMTEFoF8m27%~1e-K)~ZLyXP)U zPM*M=N|Pb4{Av^xq;PoIvFTiJu&2|%g96ss{6XNALt}oOYs+@WDlhi@#P-gwI6=f% zeW|GREWTtA&OgQJ0;IWF88m11+>{<`2=PZ@)U!Ic>Q55OnEQn!$a@0PZE<00Z9j7> z?hNR^)bISWPt&L#HQDMdo(#a*lx3e4qSdzR&LU!fdctN$L`aBXk%<`%&|Em|T^^M( zUTFCk7`vj@QcIXE^?YMAMap_#!Dp+mg_k^g7mi^RX#F2_^?$)|+N)!{7-g9paGrFw zu0%T_Jt!Hf{&9mq&%`F*su&&dnE*x=v3!bcq~2tlE{vdjFy3PneTf6X#KNjN z!~LKq(zMHM1^XYDK*j#|)#nfqh6-YhjfGl${=Bl_5RuF+&faLFS4+E)wSgZ5CTvp3`}E%1 zXq(HK4puQLnrhZ7eU5b$B>U&@ zB`_kpm0*yyv$8Mnr#%=#uTqRZw@)dyXq321;Mb(!RctnL*9{r?80)!72=iUF>ZxZ^{N;B=ovm=oNx`&B`G>#Y8|!%nq%zCz%ydAJ z`iY1M_V(y@=^?xF-q*3P2o65!GC`=ko5S)q`+Al>QW)!G>I9DZ9u*BDhV`h3z2Tt7GqRYU2PoWS1m^La8l? z!C6PCpzq0r29*BI|4Q1=Q>4sI z;t{nsi-#w!9U%?7@JwVdNAVi{3je)K2Yh0_mE1LC@e`P|&AdjAsQ`krSRdY@x92@t z&*cf8cgu%2>bweFyF~NWoStRmS}RmkgvQrx=cmvR&d84%oT=CYvQ!}rFzJqn`u*u~LX5C#yQQ3VY=@KHbn&C7^;EQ4cKJY4z<9u!oySr|=?i!AIe}wg z?edhSukZS)<&qpU>wd_;uDr%9fz@+w0Wq}2fq7L{@-sE@5a%l6N5Tpt z2Pq3rLy!hSbv*cI?^1RNwl4YZ4K#&$|agFoH zEcBgv4%s17-S_`Cm=F;em}1x;)@fXDTOcXGJW%U1O4?|_tJm<1Uu+LZ>s-pT{mVwc z%Tt5Ew&Hdk@wy=|=I6)yaGIo9W`tkWg7+61WBGDs3T9KyC1cl8(L!NgTTtfbMMgrC z0v2T-_pa``v6+KS{KDoVIvVbTbn7gV9(7e_7FaYnK*8mFjtwJ%a70cNiIfi?Z#oNp zgraU03N@+?SlRr3i>p?H8P0#=R_lZxU+o=oBP&g(_(cAu{eH2Y*m3-goUjBmEh;R_ zp%(NH$wfHm+HzQXe#Q5ZYiFX##bW;Il=%DKRzTEKuL`@mgaq=aoCXtbaL+gYh`vnD z)4m5l=jA$E9&{on->Nr{oKnfd^@l~#h$^EsEP`LinepyS&95-zx zWN@j%CUfrE&oyOn_gmvZlaW!E&r@m^1OjVf0t794$66;VF@29_?!8fJe+`5SrR}WI z&_HEUdCqqsAvV81pv-zceDu8TMR9yPK>mo-B}7N{xu?qQ#$-%wXSR(rQXD+TIW7%x zG1B8Z+0l<));L3F@q@(9nLUB%{@=|DM9E+)skii-Fj&vnI>iA$fuAGQzf`x{ zhE?C?BS>dCNo6*`?nEP>5!n-dP7(9=lD%wWJLiIFcl`%qH3qz&6^1*aG=CPp;ZeN# zWy!z>WluIZU?pfF;AzUppWkH9HG&&z?5zi73yhru!aSB?x|_@1OlCBCd6#jO1Jz>M zen_O4?u6*~gNIny!w5S&sBC#k0mx{YQ+?{AM zsg-7kHuf$qp-{0Vp?vmjGvZO(J+lBv;O@z1S{+Xd`I%y!(_*3Q_gTyFEzS}2y?ZWI1!@wI)*{dh&oM2x_u=`*pO4BNPZ zb_XDpfAhk+ZF>Cp@w(D=B#f-5<#^P&u>>DRHCS0PX?&R&$0={tjh*uqWCmCiHZD9_29)@4~@E>&KQwq@4= zSxqkTB=JK?Y-Us${tg8>Lc9s(AnllpFogS;r~pSoNCco;6(*dl$w)FUEd$gsA2Iyq z#?Q4{I9ITpBNy~9dp>(2GKJFa@U%b{M3_J=p5t)VV>#^*Rw`4CW0u$@1R)XoAVU-? za7JQczPU&XdQN*Ixk}__H5N$WGQNylkE)x@24!QVaboL8z@Z`FE6{Gq-dZ}+fbPlZ ztMyfXR@ggA|5b16YV7FKZ)k_r!?MJoX`^`9kc>RLJXWec%MF*!W`e>gPbO?TP z)SV&yWghNcS4%XzV~5c;3mYc9abO$`9tzsv&*;E}Oj39J(uQHZ8rl2+WAiRd+vU}U z!A~pvRYMz(S}dP;ImoB!{~kvX%3!NGAkn2b`rEMLp(C#qP^Uj{U^wI}#6@%OwflVJ z=hxFpO!LOM94!&`|H~I!=?t)OWy>@`L44M^OT)Cl4PTjr|;!Gu27BVqA zJvsvuZP-NiM1TC_u3ULQg9l4m(1Z;q{R~Yny@3PSde=;yz}cya4p=|lUB1oyydM1{t&be<{dwtVQdVdY4YlvHc67*Um(t#Pu&ze zvIeWYv$@^14-9h8_lHmE)8sLjx+HHb7-hQpSEG|x2aH6wUF`oxBav`Jebve(2b|`J z&dQ1{$N=4>Hj*+$9v!5p_R1Kk$szcj_~H4UXaEX4j1VLiK!L}v<7ZU4lvtH$l&7Qx z=oYzT@-1}?lFSNro@Tl1I&CU=J(^j21hrjSHQ&Qkl0J_T%K!GtxnF_3E#>!!YWZdW@?8Ol5l) zXScKxb?FyOZB7Mfz1l@*DlrVS$p~pQxf4N#`~NR!D4OMcw441ZC5td`#TFpaKf6JT z8u{TXY%ja;k->i1OKt~y9Mf8Sxfh_s*XEI?c##*8dz)1M;y2lM!z~#+yjms0o6A(R zH;JRdmXL6Bvt}?a^S2{coX&)v#@Va5o!Jyk|&p~0vm<|KRysRft?KW!IYITizJ$p9c(oo?6 za$&Jfx|eU!K)QgsAxx%IGJMDO_S01Fb(iAO5G9vD-2~TR;0${*w~$xB<$cl;(2uF# zjVq9SBV}sdSJBzmMFzXfN#_g(z1@Ly(o#wz^$0aSoPVaYx?I`f1vm*xF5xIGPz=2`SQR!3(^tY_1^;*yGwT9c@LKZ#+whP~|$KH0C zr3jkRm|fbV)_Tziaakzp%;T=iq@JozFl>MU-!e=s) z5DzN7`Jpt-_-)r(>V7!e%Ti0+klI`ci3$HjlP-F;CKh>0y&OKU3Z5r z2Xv||`v05M`j;O}>50g5!tSDx!{WGtvfcB&y=ntxJ%_bo%!BB)H)5E4Rb$x`>xPd4&p(iHLw3+dmtgjELUu|(hSMaVG1*fF$rPY?`QzOMU)mYs96yW_=Iuj=#{39&(rWqGfI zUFwQTGx5Y{i1{hdz<&061AJS1ccK0hd@P5&kBF78z+bgxER@klgJUhGC|{NoI1CsO zTVR+^mj0aD239xiGZuIkPgMK$U%aWO#V+N|_88v1n{up}TsZ&#%>q!F3yZ6Li&od< z0#l36pqA0+kec9PJs;8L)a@0)y5VEnGXj7(6st4>%$%Y4dc=Q zJX2I{i(7m^8-}(;7bDJ7NF(}`JRrkHm5-MJ^z$EH(saPDn(LGf?;mB_r@w`J*19Y4 zOI?*sm<2m-_;@qC=CX<$w%b|HTH`Q-pLeL%506*eZ(zx2D^4qij>_A@zPfoYK7CV; z2{E{v$QpNirb>FuPY7tElI)PZ`2)!HWsc^L1k(I%{~^-z`&!b# zF1A;=z}cNIFiZr}W-d*TV_$2~`B2uIT_S&(TTNDZlTt+w9m8!-w;0m)3Q*#fdCxo8h@6DaR(z?omt!&@uQQEuS{UoE5{sfvQ08q3>$G1+h^rvP(O)|E> zO;)DrV1wypu+vpjs(Dlca|u5ZfCRWVQ{E1$N=1KG3;WBH{5khF28=%FWUN=*!bV%9 zuYNBw?8h#nekD|h=yO{BLHd1dj~oT64^DrYU7V`+DBPjl1j;=O7Qb2E|MNCL>m>6| zN_MkG{S6BV>-t$vat14kpl_ULL*$$sr~5a$hA@ zS>!wCzG?$hisq#*V7~TSky7PI^V407UpP0`V8Cbb-nbO8$YERA#R~a}2hpUslFMvJCQJI?Hd7a| z@_dgzFVK*CaBk$9yK+zNOmo3D?96BuKzFL&;Ga~aVVJ!-UluP)!hX!Pw+Cm>W|>EH zi&lG>i&%BfxwWXr*dJ}Q#CTZ*^!Dln->OeI*~ChW12lP;Ts@N)4R9P=Qm=raxCl^&cx*9M&HpwJ1Rl&4GQYe2SU(85P>%-<(rqaHS(!zxx zq(eKIsf`P*r+>fgE2?yaEYcfUHxD`IPSYo11OY}v$^5T!4bA{4b&5NkUk>|S+-$JTR+o_5iY$Jt3WqHgvEL0o`CC_5Sd+$xu+f?8@U;6ITqbIXXY=e1##hQGx8CYrr3Pgy}V4 zi!*8f?nEM%ET{xqKCEdy!uNOZ5GI{$45DAI9v zSojLy{gCeb*k_FQ>GG#v4cBu=lm$hSmJd8Ne|#|UeAaLotgb$nWHM6}RT)rM1yr9b zq5q$C?*r?uV>s(!dQjqD7Z-tY74}E_oYn%?@`G2xoUY2LwuqUXU6(=zP5#pN?;b9h zVL;snePbBIwg_Q`r}tH&twGe>H()HN&UFkPGecoLCWFHpB=LOg%fQlj`8M7cJj8;e z5|wj1TJ}h6ADcKwJ74`%n7#vmjG~#(%b;k_J!sI}L7+PN`qkF^l*SO#h5YkPvz%|*j&-%YGb@fxU$3L;N$b-VtIR#MvyQrV zI-Dggz*u(#aMjx$6`ofPZ}9>KGJ)+F%u|35E#%5cjxL~MpOC7%qs^i8*u*hnfSb<> zsL$Z#;{zXg3g16-VqV6U+LRZHddHZtkcT`1Yp00iMvEP~i$s5K$!oQ~@4M-QHc@=K zNNewR@j>$TgCA}kN#_9v{I!io>OOg#c?+%{P3)kA*jS3Ou`vcFCi3&%=bW7ONO+Gt z_5WIQp$v|I{+>1u*D-dX@}hcnLOfht1p%A=MI&3>C({%D+YApdc@$jZWkr~0Q-IPl z{7|>!ynd~m0mzcj+N9h(vt?rX!B%aOiX)lfW~k0nRV9tOKFxTYE)D2|4O(@$B$%(K zGnxtE1d9P+Qc~#on{#@43-1??&&vMxOi%*!p80A44%S#C%=!`3N%nwK_gG}`Gf9)Y> zXJ1#Jrb%^*z;muS{^axwJsnte&ImH#t!xo*7nPLvn(YG#3W|Oh^c$JX1enWJw^(vQg%#;ak z&@Vm=XrPaHP@KGiCEV9h7BATC8dLOcg2KK!>QnwrE;lt3(;-q_l~K(b_{Oyr`e#8n zH#$#H_~YcTOy1a^Rvg%>T;P9a>klNh%&GB)eEOso#&UOk*zSHw%Fr#k8*;z?qz@XZ z$jckYHlHGvK6F0&*&*vZZ3<8%I)L!x(WBcpbhLm9NfY&#w!eD`&lGM`9&(=h_U%p= zB5mi!L$dS{nGU#&o*!O0GTr&rF2=o`v&WK!n52TSR3LBUaN9Cd zzxKSY)a-|aA*^HYrP4O zxLmq8J41(OGxNX|4w=>`Bdm`ecG$ms88?t};||J{zHJj>vG)P-(Hz<@4^RbA=sV&# z!OO1%h;uTaIi#iKwRJ@ohMzKaG_FM>5g7rtlmpE78*HutwCR73$g8As=34q8cI=m; z^cwjD+~HZTRk6||T-5cS&3`#>tKH*Bs1m*h;$(h{_0w#-dGzp@wNnh9qff5hdX))?nq1B-L7HOdZA~H>i$1SKvJL#(ZBn8!&O-XE0);VX_1cXSD>wX8$LM)Vd z=8O`;!%3^dA?s1<6M^5F2x53a+Lp}7;12OQed7M4chkiWnM}9V-Q69W5@beULYkuc zcI{eBhg`nC=v6XqwFbsd4v+1q!ijr!a)`Q_0%fP`81hyNS?-(7H>X}90jA~hH6&kN zrqDdcQ9dRqin{Yf{>R!AIyE03IS+>!7E!qa2ki+NehCixZ@21C%eZCEYMHNo{rc-! zXj$LZ$GWet#XH1sKnbgQ!xX^qAp-;Le_Xe&l+%4#zHC_*@V6E#3+CLxQ-p<`S`0)0pBq?4g zfS+0A)au$^{j$<>e?b_>14khg=h-;fR!?%?aleE-)$gENwxwRsnc~^iP71ba9B^}L_C5|4r%w4)lV(~@BP=9x zHMyQ}**2zc=&bKBtZT+MTwYTCdLAUMKTfQEO3Kt~V9A$l?E(!0yL9fQb6v zsFG4`Jcp~F5jBsRl~V`{>ioc}qNNILQh)bP+wiUEIGW3Kh&ztx4n%~cG4L?cI8ROg zHJ^UMZqY4icbsqtD&#Vi*}gg4GR|-H5Pr03c9ZDFdcNLD^YLFXfu@<^m)$^1amzqh zV~ClURJG{_UK?FCWqnbs@s~B{oi=!C$bDC7GZv+fqSPe)1m}lrhrV|o8(&^LvW>{t z0cqJXIsBO7C z_FO}0k7w#V5E>WnrfO4CWg5IuIT$KZTKnc1xAvJ3Bc1hFOp;qr_`DUrRsHWPpb1_D z@=YGZx$7YK>UV=~lBW}Hv-X~6tD;Lc^QE~OmD=Yx`zS=I^+EQL!c1w zH#51?L-fFFuguMlIh|SRkCHtfwo-no_+4>U?9ILHd1G-r$T5%A#I5{RW$PEl?9lHG zw4e0JV*A8WLn6KGQa_nTT>yo+<)qH2(^7qqBD<4;Aj`>+op6mcUC5fgb&_E7t9n~i~EJo`paUd)arK|Sy z(r+(I#)v$q-SSA$hNAaR*I$HAgHWWl`@(D<_mmVrt3{>8NLeKLc)N^M?^!c< zh+A$wPqo1@3NdXTms0ue^YXw%Bt}#x#Z-?cZtcEY!$x%ocq|kM)IB#3T+b8myiuPt zj4-S7`G&t8vlqk2MXxepNrO*XWRIm{9TUSHNZC-0nS zw5rC0mIucvm_=Tf%K#;?4{t9+j~4Hm?dB;~TciIHv;M#w;`@)wYh9`WwwWa} zvLrZ~BF*myRV^XicBxau=#_UXO3EDFwYB$Xly^TjTTQ`q+?r=dqGC!r@@}U^8kzd1 zoAgHQ4Gq7#92^kRz4@_@6B9|qz9<}o$1n0X=;Xse4S4ee)7;~CV{3s`NpGnqK1rd+ zNwR0%EUV~nXtngIW$O+3-OoSZpszdjDUPsgcNaR)bmYdp8*6sTOnB zj30S&U=$bm6CT7Q@EPuSk6s2TO@F!92fH#*^15!)3pa%tq^Z0_G*GU=)ln=C2p7h9 zC+=L4UpW%c7JRnuK3s~S_Ar`xF;?Xm>U)CKa2v|~vD@*x6 z(XmB3QmkBTH$inwrx}MWV3CIvUA@hm+&_6aP)}|K`b=PnyR~ZyR`xc>a0yG=F)XW5 zN2PP}tuWj%{lzF1>q-m%_THd78b}3G!p|+2UuK_*0(R6kt;OWW>-rN_Swz&mGY=7>*i4X&`R8BgR%noKf-4^Q$ zX^1#BLsvVgrzo^Q<5yMyvqaGLp`u&#*B<4_tU-w!KPYg5&kX)7K%O6!boJGX0} z+M++Y`~lA5cL1)J%WW-+n5_6o+$yCy|4H3xmNCj!w?xGyQ&0EX{k_{}_ks$q;c0d0 z4Ww3M;v?Mx=Y&}m=Oc``#XWxt%;FNy?&8S_`gY=AvV7xeU72|C&80Z8^|@j6Spa6@ zDNLj+MY`A^H&^H~ZS(jz$I;S}MbrF_dH&LmE|8?@h2$l#1oHxua8NjBNN>=t2<|O7 z!pvdI`^IDgFr9gi>|0@)HwRL-t^FtSzO0HoM@NJZT!j(P(jR&fnkBpLbkta07gN>V z*i8|W&AHlue{HHs=F5+82ET}cXQN`L0&_|>9S0xUH7~{A-Ic*J5#m~own8+(GL8l} zN>J57DW?mqx|O?i-0XPYI<_I8agTXrK>zZnxK``Txcu^ihl%YOkb45XwX^l)JS)eW zEjW2>(ap*f)vcR2K8NhY(~tR{H1s?52iUW35ea4JU2Cn3f$**4p;a4l+If5(kYrAo z$g7Y=e_`fmM7ABDqUc)Vo{0ONLVTKRTyIcESkzPWTYdx`PH{#|Y#wGyDqZw2%+`aF z@;=T{*PV_wBDEP-*uBZ^symGc2ZuLf^x<%H{2ph%erKhX1T=*J`lo>P+hs zZ-)Z~4wTbP5`I|_&nZdMcs4bCE0}Luon;8oa!)Zuik`{B&m-cDDy-y6t%>-3)jraA zNu|z}D-=mZ7UEadt%GV>+4)YfJW!-$mRuQeajOtP!2|7L|4!@dr}Pfym{*wl9o&65 zx7_tn_KG-6y9g{Y{f_3RlPeR}R`^AU8sZ~K$@P4pgupbzlA0EGfBzW|R}R$5vJcW9T_ft= zm_k5jO;M;O(Y`^yNgUIw%QDcFm+)(foXl-#G9_*Y)eVaCJ=gD#+OH7{XtHxquMs1y zvSv&Nq(1YXtLbG!wsF-y8K_JCJ9%9VGUm{snSB<{blf|Q*SS{re80#Jv-ByY8agW!MW=X zekNC>kVYx2aw@5sG9dkF-rEz?r+zlQMRmx|AO{2m6^%>DJl zdydTFM22GSd#q=fIK{3xRkYagrxjH=YE+xIS7rre^Y|+f$HIBeBU)lDn#48rg2_6)d1dll&9yPQ?tq} z<|=bvFAr(HXBKbCT`^C44G<^HWFhygq>-Fng|bP)PEr1t8DTIVxN4NDpH8tU{F?vj zHL-7uG0Gah>_g&g(Xi~AV)w4Sm?>e|c=O`TJ5eALJJ^VCB>NlOlvvU?4#}y&@Hf59 zhyq#X7m4=c<~}CQE-soq#N%?VXDms^+b`zIyV;5aesce|sauTb`ExzxxCxWdQ_@`m zJ>W!L;Cica@P%Hn)@etkt@mupJ12GN<8s`pLlyQtPhI^7LL6bXMPijr(B;X^59(~F zj)K~}spZiEH{8$a4DUZ0fC#7!n452(;wb$#qS@1j zHcN9Mvs~^&*WOZoEYAi=9*#9itFPOOgwKUP5~UeClWH^ZtT@12?q6G`P(8B1t?UR} zK)*t^(=JA~J54H3AM#K!eNzL`8Y?E85VoRv%xg%~DxRx=Ylm8#`l0V=JjUv>kO5COM*88Wd|)pj(?PY37L z6I0!PNFnW2%DYpwT)DLIL);xoL;tD##V<&oo09F{OB(-?6Ttj>*;q^Me`!@6S6TUZ zyySStkx^7=(6KuirrGNpDrM!!F*na9KwJx&@`;j40-d-G-dP$%Cf#x8OcU>d zTR2%}y?H4UezA~oT!KDsZlc6Uq%P`1s%xG7_-oFt1olH%=#=h`q}7Jk^!E1WfYmSy zcZK0!?m7%y_yOYsSE=qzgu>G8rlSl7!3B%%ig2@`XH2{TCa| z!EhJu(T7MfwTr{)?h1rQLEW>(#}2;4f~p;KEBlM$-D=onMCqacJ+AyS!9mf8^q+`%^p?ov|5E$w6!X!pV0F(O%L{i{B>&aHCTAJ_W+#&GWawjzrOOlP&$?;*-N#qe<9giiKTOAXbW|=COIexhTvE zZDTK+ZO;u7o## zn{7KPe)A5=J-EzF(?po6wr&VeKB(X`y0vt?BVh$F*vSiOr(wRA-b?g7#=a8esB*p_ z<2-e{R9YJKX6)(I4@*iip+AH*(e$iPL5?Vb3z;6$SU{E3f=uMY?w@VN(WfuRrj&lO zl|B3@rPdE-sr9Mb-xt%;(J6IbKex(EU57n)UqMdmnrdR$7v#LWcB++6+$~&H^t~E= zQ?v>Oxa(6Su*0JMUNz%GPa@HD_{@PY(wYY9w{L2|(mK7bL*qk!{TpObR(5C>Tu0cZ zaqucz-A&?(SW-#{I#%VRD0(iw08S+A3NO}CaC^c2%8$^L2HW8Zr`$-gbz+AfrTvOw z!{RrvX=har${d|pYckWY9nu74{B7`R(vRHFp+^lzm zSAk_g?&lR+>7*g?TTS=u01{amLxk*$vOb(Z0bZz$nHxsFo=sjz9t+VBt*-yvz zM7sCB-oD}+YM|h}dBc4(_G;^4_3rpuW>wXb9^&L!&$Ag`+OY&}KOSiw1HkQZ=E0Gu zz;Yc1mTL?!w666H!Si^}Y|%v1N3mcOm$s^K(4*fgD`C?mara2mNoO@i_uw0hOF5Dp z$duz{ic!TE)ITlxDf0b0M0$y`+P3UE=g>6*p_ulFk=x3JTSUk%hDO z7lY!EufekS_GtZnuU=~4Re*&i8xfEEaHT^J@9N}4+NksF7<6){N0v|R=;5%U3VV&M z-33`~Tf;rV8KK-Lk>@vq>hVsEZStu_Y#%i&Jarr!D5)O22W~$H>RJU2-w*P*oVaA~ zrlKAfvPjj^SDTevja0Yq!tOMLeit`8EfrJP;IwHy4V^BY$|~+OIm3FQ254$w<@^$~ z(hLvU3~dJ4tCy{UWk$oyebVra+C)kzPi?JG%K>1A-QPfY%3{((+^EMgRy(f_2V&mp zfk!s)gz-cWe>_l3j^ttLP!ou-ue3#Tqn-%Lre0ltGkD=|XjKF2Ai(nyAyx0hUWzme zOZs>|;*5Wp#pt*3>wxYyjk`dgHPO z=0V4HPwnfNIslS=@Q-8#%_0pnwb;1)|Ato{sJ_+YMw(~kQlp=-aZS`@>fGz1*M6y8 z{8mPRr%~w`-JEm;SYov62OLQX(@L6u_b>@g(mab*e3_kx#{|V00#A;m_g?A}yK_K< z-38T|F^aP}=BEx510{XF6Xf7at~WpYzV-{G1(tb-u__w81ZJV9o_jb836J~9(i0o` zbGq>NpyvR^XTay7=MzQ`#+#C|RTZmt04Kzq=g)uiXoNW?shQ9#xF)iLS$OuFq93>I zVcS;V!Q~2l!Ys{4v;LsC-w?Vhq*#4`1>mjFY2TkdlgUxGas<42Y_aJDW+$8)#v#qB1p>I3uvoX7ALRbqrY^=F zG{EtwZCjivfN9a*f>Z)YtQwSM=K}Njp4AbJRnhnX{>c+H9^sBo<;b##(%k9Q;DnE4=(>-y;lYYTctziAbP_F2yCA^T<9*pnL_Jb80G^%7Mt(wLa!m z2KYH(IOxcpnY#xQ6CrS23l8yD)vlc^Z5aa~YFl*c@9kZLVKwMvK1i`2PeUYL+HC+w zDj?fxmY$164R-w@Pt<%*{uTU>GUiQv`<3k(xPo0P?X>bvmvQxO=4Yo18YIao_BS1x zzsUbxa1*jv-`^tRuzU=BIl3f0F!r<;DdygKrT*|#?8$TMOiY&PBQk#d3;&%5VQ&vT zUF)AzNb>Qm#?+7kK8jmS)oxwYZ}pXLVa&szRR-N%zWM&>mVuPoI0OaNdw6=v>kV;m zaC9C1q^Q$pU$|r9?2KHo9$U%aI>8J_ME0`tODo&tMWxO=~9W~BmHmQe>=PE&!cg=W#T+m@9gc}VF3+g_X0f6cu#HyR;kX9*5~AW zfjKbv7&`^;ypWAIvbfip2yGt={O!LLgg)9Ho_p)!V=DHKP&D4d^6Np>fhS%XgU{<< zy?T}S5w+h5uhufyr|4{6WhFF{DuNo$aXD>as|jW?bd$Z@FCLJ|fh6(x4+-I#O)3=? zBOj3QBWQtBq2NU2T2%Ck%7UYR5kFPDi-Em%Z2J)?xIcjrUn~BLz{TsD5&)D^R#5?glFs~UWSPM6078GDVjy1YI`4-J;MOKUz%O)+YeYX9 z(d{hIBiAB-y0jHeEis?no)LKAT7kZ&DGL++Ez6_(Q`H6zshg+z%~7;!ez3ov2@_F^ zg>bnwc$Ew;dP^X?e$jXY{oVhe7abB4dGcj_sbC_bEbGw=5tE;y#-bhUPto} zd-P?2ncfZ*4IwTIGBLB2qZ`79VS}RhRAgbYdA97*$IsU*)!~wt` zT&**n*KNdxi}x@xj6Na2pWC7t=MM+l;cF<}*% zFVC$;2tY*-;sGiGc!Hq^4JM+%LLsG4xfMj6c4`kfN7n8+mwEt}2?w}R+MANMX`sA_ zThb8X(ng>`LmcKD?*SoR*r!;^eLc?1vTI?Uo4!-ibkf}~Uwm#~%5wb#mA&w?F2_+= zb@ln@ra5_F%A4m?UIcs1o%-InOYMFM(fiSGqe)&W`EgrWX?f>p9no!QP8c6A#hq|| zIyV7<1@Ou0j7oVo1z6Z$fVlK9+|AoQH^4Fys$#c40nFgSf6O3M#5nN>Jd?&an)!jo znW4ivgKLsE@OqGsnQg)i(zA(8GFsA33AyjkZ{jCD4SRB|LgoH&cXD~_{>(m$zD0+0 z{dD+@L|JNcZJrHWmR0rY{6c0M?)(8xl%IhMAR3H{d9FH&OVrY1T~dYl8@p7_=e#VogRvx;-oyu7Z)6k<^>9Gx`sw>|~ivXs`A4m{ihzdSg*&@BNP=ykBq^_Jbx%zr2V{yD!K|(U0$mN(+>UK zqN8KH^0EW*uzfTQ3!j(|t{|;It7?1r_I|E!)QnJ=En(tHV)+X39GE_tH&YY5v{hYP z^grv`oow8nj!@P*UHFqarF6%y(32l;yfVtfCmQ$E2-bS73R~4hSCn3x#H|^-5&%+1 zUda{(FCT=KtSCg-`*{W>i5c4Bi+%a%fO_mu&i6Vdrn@=p5o3=-0UAI-#sCej`6UJNhcsh#ey9?9M<4oxVKB6 zo9UF78*Nnv+aXbEsZ!(hflMQdUAtR)dVEn?T#T#|Je81V8^>bnQb&KM8MUCw0q9$DKfDozw8qw#~c)(Onl(bx7zYDJv_}1+7Q!VMaz2)*I-92(<58ExR^l zAlsgCbJR;+KNqFmx>~y2{E)#gIxKxV+ia9YP?5FHGp1(suu5&hE&cGC;6t!iZh!G+ zkIGnOV0o7~wpB(uMJ4rOm_mJhy+u`ZoAYw`9A(Bi^h>so6J)M8czQ1)Np<^^#n|@4E zN6%Qd{g(b$SiXv(gA{o1^$m}S&mquMTJpQW(BIJeiX^6* z#5oWLDga#ZZ2)aIeuy*ja7+k1&uU}x$j_=$7|f|mCV%sAb}ww)$^`c}6+0O(ho8yn z%r@^VSDrNeE@kl*wq7?Ei1Nk0$dPPrwH)PI9&KoNKS=rrO(jZCl?)nIuj_Z3dx}`2 z--KMHn|E|{lz5lCZP|1`AtB+}nx`UdQbbn};q(RmSc&6B9HQa2B)UMC;05rwtO*Oo zt?9PpnKH+T`#6nb@s&DgDtB`rbjku_bG5OzCuo#tVq)^_B)EPzcr=)Yw4QJwQ46OC zV1cdJ_V1e;b+6o04UKl%4YMS$$IFa9C{II`-MlV6$hWA2l&5#Mm(;!jjd3~Lai(s{4y%^k z(a~{(*hGpeFYSawYISo}&(@W#OKuv?N5PIx(_Y*kCZfzH|?YD=~>8O_6|8>cg@4BGGMM`<@Kf{L46&{I5 zQFWe@08^3nRV_$OX3`J$qttXcRt52ZV;Ip=`O6YeTX3Ny`L=t$NiSY(N-gb^nuNOn zpNA``PgUu;io55ZX?3K+8#Qxx!yr>stH<~EVi)5Ow!dCHe}SgKt6g&yqs2>DJujb3 z#k9NSSd5dNZ4`l3xtfoL5fA-vy(wZ-O9lN<{AH)Ofn|BXuKlhy8)eiJo!Z|;&#L=M ziGNF}NS4a@E%%ee3#)r>gM}aA!wKb>ohzRTAh{MVvtT0gqrHH_K%c=#V=VFsJ&JS~ z1=B72eNxC#TTyEAIGLC@YALG6ASlcyCfEn|{h;~97RIj5YqH1_tPg?YcdBhE3iw27 zO~vOa@Ipk~h9wQc1@A|vmF!J64-KtAhI;kP$YSmTM5xYsQaWG*S zartMEa%hi#NM)ZGjqj)Sx*&aG5m=1&8L>&Brts^G*!;WQRnkwda=1azZ&d{=4a5Bd z_aG+Ya$$Ecp;(MFI*P7fNK08Cy_SLrGAs3Ngj zi%;uIh&J}&tC3ve3b2xNBFy@CP;Peu*JsEHuR`k#0H(na@$Epbv#FTdSyq<~1@gc7yeZq;O z4Oi~6fnu{}xW75$a=9snRiR|;=H|Z54EgI?OieA%?t9;)^$JmlCYizQZJY+7^K3G{ zF0`>-I16oK(XtbjZVK0)ctBQm#qLHbDYfxyg^Ve2$eal9-bJ5OM z4?h1;@9n}Ljfa_w>h7E7OHW}*JHKBM^~{^gp$Yx{4l*H%-e zFT9?f@;SG}RL~Oh3S+KjF};0S&JH?h@Wm*VNG@x#`da^#(>dst>z~-JpF$Qg3T3Zb zKg>8qfE;qaNQYA*Tu8%^Olb=X!hn9pm%kG`QT0 z=K}VqMvp^=a$%sZQn37-vid0_s}w%fxFU3hCwb>w9E=Y?5{#X&eZ2ptU2wg ze9R)DOjB}cqg~k<2}uN81+ydBg!cW&i1xvW)`+nz*d4dkYl91p0=thL41u0sy8^Q# zd`TL%XDd4I|Cw^i!b@S|V;qpRZ(K&TyVM(w-zZMv%<(N~f!@btmQ<6(E|sIsbjYr4 z!pdc*3GI{iS6Nra-sU}#JIIvii#C4nmj>RdKNVkpf zDQ;$4R83B^tdvHsNT+Nm?HuwWsLALZrv-wbKH8*Gs+qED@y3b|&0WxjzIk?$pq?+} z^N%i!r$@CqDyy3Uoqj!>k!_8x8x@kye`jBN>-KIk!X_tif-Pq2F#XA)4Atsx6D;La zkPw06O)XdXK?}R53URD5hCEGgmQ~ifoo937he@yFSLmB~z$4omp?jbLRuuu$l=?>*FFyB;a7OTXoaMZ=`@HWpInBBXZ(88djm}HEC?~i8mUQM={{? zy?V`Zm!nED2@|zAH`_4^Xu0L1)wpjW>**(U5>xo9L=e^AkcQr~w=`q^Rf}cFlE1gw zwFRhruDjSXFL}lThQsBVaI79n&fcV#9{JB?JRjN{!nw62A?BCT)DNlE} z*-%MCMHNhJgDYBU(ps`EYu|b%szR&@N`&DJDW}lStdOS#MPQ)$jmf5O2It8#^d~)_ zYTIZ6a>nI^G0=_l!@oPYg#~{Sp_Lo2dwu)%1=@l30u2S{HI|q${HRg+AfU7tKAQzm zdxW-ss$}7`LNVP=NI17`;=HNr{W%viJ3XiA1`CSI&xu%V)}|Fvdzv!kc7m}*KT1`K zgS{pnSc$Fexx1tg*~bE%(!lUBpPPd*O_NU6YhV9Olnq~uQI2E@Br}Q&{i<476sEvJ zOnYJ4JU_!jvU1gaF$*G=`{=~T4*jSoAntY$ zS($ld`G9Ax$xEawtw0{+vb5C{`$B*VS(%{yjZ{F^tTWgYl@AuLN+g{lfPecL1Z_@; zbgIM$aG+d8RI`p=lL?)^%ym^X-0=>rs;w>HHU{jrB6SmYDR>_j#@g$%)Wn1xw?}-B zB>xAw>lO2x+(-QpzyZjya1R2n{wk2?C)R!#mh%yct4_cAtp&CHkk3MBuxd;ze0$=d&2-em$C=$0&QwWSbr4R8su zG7ZMWypJ7OmQeI)o5IU|5glZ#!aeKMY1uj}{N&Vf?>hNNvifx=I*e~2;!Ua&M zLK-41(LcFSXElJY)Ue~z3R=5CFM31SGl4hNDVRu{ClrM-m+!Z5VUB)TRw*ru4>&e^ zp69x;c~QVbJVk_VHF=POUfV~^5UO+#`sIy9r}I$Uodq`&i%HicdX6lua&EpU8Nm5% z0CEwg8B<+%2cm|$7=XyH7>Ha4)KSA>^ZfZUsAFtjWN4NG+Ev94a+q#gs8>38`iw=@2KPv#5)gSiF}o{V1_YfKV1d?V~XuaCMSkJzl@ zAM;J!Li z4HCG!*h!G1XBh($o8+B1t;tqA9%Jl22LN#RB5MW^`)SvMt>Ygk5BhntOiff{+>_5G zU8t-_d#1Nndm!LAeDy%v=n4Rum-D7MkJofy&F`*iPl5FsMV?LK)SA5BJDy3t|Jw_I z%;460`e)EHByi_`voH+)`1al;c2cnd6Po1BBPN7*nxazN>IwA^I`Hd>QZuBQJbqap z(7uNN)TJU#uuN{|Uk~e>vm>|t{ST`C;0Z4dN4u~s>$ zs$~yBY})ef9Oj8%VnfwHS5j{Et7>OMyoi|5QMS<_ytLKC__&pWLst$YTPNW*5V-RS zOaL}U-PWr$+Zg2G5xra z%(*p4W$>|;6a`PAEmfbh%vj%FeRa_Jw6nu|iwA{kE*>KAm2zs>O0Nac*_{y9_MyJO z$V=sq65UGqphH_K#C)ZEzk7OGc(b$~;|1-kPz?%d*>!%6hVBeZ?Y7ZlOC8JVv2JIU zm|OX(A2B@U3Rj>fgbjBD>`o8|ityaF1?zKv{Nq+E)SpwhnlTLul$Z9|Ts)vB1ryXY zp^ZoVh$NrOJI7Q|qIQNrGOZAD<1}XRtLk5ss9z^j3`}dV8``&{g6gjnbSp%`b(T(} zbW{&gOpjuPRDsYTbncXR8(goS)`tWP|wiaI;d^9E|1cfIM2XFZcn?rN!%QQZ zVsK;L}N6WEsz&I6Ih->deJPtt=i7c?85%fX{Wk3$S%uHyg@lFc{`AAC|!0 z1p=Z*@}JtlK%9;#83+nBN#fWa{~PQE#DX0*U+!w2yWrKxLIl*nW!qi8tyT;`R5aYp zN>uZg6;&VN?pT^l>-iJB*8p$%?KHOYz$Fk%GzP*ST4l@eJCA_?f#T!$lS%&z;IhQ3 z_UfL`$bU~B7$eXY=?}!)5y1Mf!20vMxG}62uyQ~r{0h|d6MoZmjamiTYGsGAqS1X*v7P92<-O{uE)&4051 zg)aa^CfdhJ-iZMG5$-GE8vr!xpicXL`_|+g0oH7-I*jxAw-Mj-jgYSMz83*TcVEBZ z%?ChoO_P z5i$SG0b*`00@1HtL2(+Ai~l%5bom?=eWLPN(#8lEkaXeY<0D%qB`$u~($X?Qm9;+Y zFp<54oe-kWKUYWiCkitomJ)36Ex0^7Y|js0#o1?ta8u&TBDjlc5{9-Us}z%9NxO;r zgKb%f+*4|+AsVRejwR?M-Z0vlCX}xz#FZ0l@$@KM4+i1t?3@T%w=Uu`C0*0RrKDCW z*{;tK=1?n34~muQsmz9zpx2iYZm8jBy;WOrkyyPE97+-h_dgs(Abtc(SHfScBmw=VXYJq6r^qNxL)PVGg(lFekkm!hB78<#-b*`J;iv`{rz?j^R)lz++ zg;zGcHxK8)oO|4*wBkNI?SlBI9;NjH5h1YReoYzwF~p!L-`b;JsHhIadrArR_zWHV znll>NDODGQD(kF{CM}unuMKISGE0Ily=@{atRd!mw(>$Y-{}le9I%O^ihad1b?~;# z$kEl3MRhdZj93QN9{G(!5X;{G<5Ev?rQ$SZQCOoIs&C_q7E4qCL0jF@`u=3W@3_W#}khSPh8XGw)r5;UG52u7JK+ z+j3Yxmf>}X^&7}i36B9sn+t5o3jto!1-#Zxq0R=PuF#0P;F+rw@_Jgwe2KUOnEl!F z*XM4iK!Q@*n`(FQ?nS`snm2V8!l4oOAV%)zf3}bUeR>13)Lh=&OZ30N10D+h)cp)f z5rCNcJ@-7x3Fn>%vh6}*vu%7LT{1xRNG{&>|9%m$aFyEmv{9au+>F6Dc8$j*i3196 z#d+Eb9xtK*5Xf5zODzckD@}KAmRAIeu!5wQ--_-1@gg{Sz%vVs!nArPi!cGSZ$~Ge z(-Q_nQ3zVt$C2F&12h?4w_A99Og5yGJUf$Qfb?^O83LiJx_mFyzv~=${vf*p3aVd@ z1p}UcSa=1I1fuBBA+cp|Ax;)?h8qldl<)uXC8R4#N$q^@W)DJSS6*}oIVRf-BKxa+ zD)X`0k$`rys5N!k0(zeRjXVulgaI_*yiexalSODiiy-HrRXtvW{$(&E=6MF6&M6XW z)vD@h6InUA3<1d#0ncCcgm2AaTc&Am#svXm4tSsaMC2}iBy4^Yc?<-d&jsRPAY%BJ z4I!%mQ9#Nekt5z{$0sl$F(EE#+Np1dyIGbSPVceoD^1eFaG*)d&C_G!;$$#;prhia z+FG*`tTN{wpOTf*5k4D<_Qn6PLnDv%2#{#bW%d$gE@1+vR@N$Cvi^~$XEtp=4!_46PB+K;J7~CCvbYh6p%qFR@Z98!pT4 z)~ZRCjid8&@s^GEFZeqmPtkw;p2M_4KMNC9|~5;ms9<)bco4r-8d6s;NHk(y+Vfn&b@tOLGq zN>+My{K*3nqxEa(9r_sX(Bs#O{($W~LDq_kuJ9Je33Ye40GHnV?f>vZo4ki?{iRs> zyA+Vgd>;NwAW``Z;6WWjCs842gnj^^Fv%)a3vB4ji=PA5SLI@b()yLdr-70?Ho{%;?f)6XgZwD=aU zAR-$pYs5vnEKWA#I1x(i3ZIzK|L=upC}FbJp@aM6nadD1Xf9hwJ&{OHJ+RjG)U7Be z&&$Q>t=Kb70Kvefl-=o!1zHH9tO?d@ezC1f`}pi0gGK1L^oiQ~Lt3;a)E@wXup_V& z>DG<%vL|*p1X3$P)@&z`grf;sYqC&6rUkSU=2cyc6mQ9(g&A~p@jxSZk^o7qS8s_O z^TRcW5Y5YCVyWlA^V_H1MM7y`eUPyh&{-Mg}*BiLg5v%U&e`Wu!44#-)8@y2z4zbo}6SUP~nUkp9J;FHLry_wiL4_ay?ZY@xTdj@$()_Qa??V(B|INq?-m7_bP99g z2mot9m)MlB*Rjn&hyg>p0gv_|=0vRCcX$O7%j)nBm_Q~6JPIcik7cJe!=I_UO~^iY z5kE`&P-xchTZj|-NdY^#R>2YN98Nj*zyaTlOQ$v*B=>Nl>j#&kBC>qzK_MXT{JOZ$ z-t?eQWO%x;sl1>G2&2z|ee#C1&%pzO-&k{5ObH-zODJOtGhNS$JTfB;3N<2M$#PDs zE{tH~+sg|8z>!SHV<*UKl^|SENYtlq{Y+4O==~UfWg4eNZAN4E9e1wDVOZ%$WDr&Y zR{0oq%-aUMzwOZKG7Z|y$GmZhVg%s%VXI%#|9xx?3uQvmhCC}1gQ>9ecZt7Iz6@k3 z?)D8~Y9Kvkkg<}GT|W^MIugJT-1_9&q{oB*rwNjzA#MsG5kBOh^97I0T0PfRf*yG* z3BLGjN$665KK)M*SPOuMV(w%vf)p_{1$ac}2l@qg0Q_Bk1sIDfY|-h2vHqtCu0ht9 z;;L-9HmN312a=kff7U}Il$Rh(5McL<1;Xq9PY;Ci0G)xT@D~Kqp-;BEF`eac$Zj!x zt*JK0NH{7e*Qw*}ja11$0mmu@P{A0+0oCEu!I!e*=VN7QPw*{*pA_7(BUb4{n#>8< zxGr|=<3ItEYb~=pkR9fE%eAdcD#AslHrN8=i{)fqsHrz2SB)7z7Sts=bY*!4cNaiO^&1Oy*n>WL-{4CtsX}l#->LNK8 zPNnMS+Xk@Q8%=dNT5ROPFqK@}GZzjx#$mFwn3Ic5>kDPxjE)M$yMl0Hg4@BAH|t;- zM`;c9$;n;^>%IwM5V`@Kz-QB@<=crTi4eH!%Th?$KYSjkle_&?fkF-1gzIlf2b4)E zxPM<;w(9hJy=K9Lv|8G{7*t|sb?C`>@-}&jeW#@9t)FwE$q|JnvPa^b?zI?^n%EP- zU`2Mc)8T#q^Gw!K9p+~Z37D^*SYd$tmC)uye{L3kt*lMqM^PV`8W@5!!*ePwdpqQb zMIFHs>^3&8`t8YAVISJFzwkIQ4}KT^4O|bbW$n4M9+Hp{fUnHCE*r69)ahvuEq@lG zN9{#umU?Y+-6CIN00VMAA9I@Cfsk*KvR#AysZfI2GA~{M?s8+GL*t(O6t?7{6Cb;gcHF|B&vL0>qUNTd&Vsd4myAJbNLB!-l%c~`Yng{lS zf7Ob;b82Y7+u5U^ZxAcX9YG9GzjO7X%`xPg(fnSdV6(VpQMXr#shzD>92Ty=a6sa~iC#nt*fv)Z?0AMbp}AlBB_-u?Qe%U5~gexkoQf2`@={@Os@8UYy{ zy7;j9d?k=@oq@A`^i)4Uds0+h^vxH&iNnWj*`vixP1B>fxw-0JzI-tpuj5`!$tz0{ z^gCP~{33j~qI=`@v=GpwH*Irl4GpXoJ~cHqPK|0;_dRWH>=Aqfv|s|(dT9{;kk%1B z4VQ-#zub8k0&=N|L*C?(5s{zs@-P&8F&C)BmI7MK`?BgVR2^8_U0KSI!R9bw&AKigec1tTV1XalO&*>e zBQ)19UvBQ}Ggy*b6RGI*lBr^3U?86$H1F>2v@WRB9%VIb{T#@Zjd01`O>Zb^|6qOh zH1Ddlxe>p2rGz1|koNPM5OrAwD+-Gl8VYY46vFwIZ3+cbk5*Ml+TM$mq2D=yh@-F0 z;S-?CW>)_`e&lvMj%2&2s0i$*^m4+I7!wTjDc<3a7OY;D;-Gkd9DJQ6)rrX=#rW&Q2i}mC;i7t?!y&4c zWrSdRn7l^Z-)HZ=aY$$x7COo<3u$#6uVR&hVKN(jxz~TJmv_yhcgW$wSHq_j^AsfQ zZ-080*;nRYj*^?M!D=l^5K|oRZ8&~GEHFn|_tfFNrrqq~@LrDtm6dsm{1uE_>)7Tk z^QuowUZ-=V4BUyK{vBrAS`X7QrjSN28aqES-h5iqGg0yT#>zrfk#0_v|BoP$C8hg? zGbWuSV~6+DHj~h0xXuf?xr12;@=-hVmhC}buh9Ds?#}cxxIW?Sa!3fZ{k zYcUUD5&q82!z71$%%A<-t+OiJ#(6C#71UeowYHz1xsmbQs=%JJn@NMPmwHy1cZ#@0 zNZxDO6q);%K7@;Dn(qJ7WQ@`$;FK?5eT|HazMK=7(w`WS4S|R6n$E^DzFuQ_v0}ov zGc!3gB`D{+@wz>N5$SRvLXCm_(vKqB-P3oxj%>hgXqDt}*pw~n0SI2ga(VCF=`B8D ze$!UrOu~8HS@O-ilCB-*3u_|wE(1pg{rwffcP$xeU8_>2Zjbv^XmvK95p@_ASmaf9 zKeftCsw17Wxz{=EJ_v6LXb1?e5Anw*bzr}ag-?e8-ehHE{iSCc;Z!<9GAYI^^|Pz} ztXZR9nXO>}-tA+3VK7Dzr5|yFQsDu$YTBd+9&alXDexO{h6Nd_di7<&cm?6RVxiJ` zBY3_~SZ!5PDHZOs>iYLVi#nSe<8;p7AWwK%-lBEaBTcqz{KiV`)!DG?RCd)qHf)$I zkUKylLoc2M2qK!t9$H(gO8yP+T!TzZLg+056JXt);vdq1dPTwN_7ajEoLb4PO#Ne6 z=Jy!m1Zl5u;}jF)N{6?H_R35a>E-=dPXiKbX1<8`ZliYvG8qYi%P9zn~=#|9s8h7P8s{5 z3uQt1`9XWDYr?d5r8>93{;vNPXL%zFH%ck1EGBq^PSfg)%&yF z<~ujnR@dVj_PODH_{F}wOv#cx?PO4VAR{Gq^B?(|bQQ+GRptb`swF7%vbNwX19#C6 z#N1Z^#|yUKU?`rzdafwUYcKxk3Jyzx^0wxjBB4J^yQ3?Z09> zncMwb_u4=f{;tRS)a*p6fFri5pwt1C_F;ZsmIGtUm}i-(yD$hht!~+^LpKfMBacUM zAgB5fy4b3EO1|Me+iN!W5#JGUmG`sY>tS5pXz@^R6nw3CxM_7pCxcV1v$tLzB$74#5Yq z>ql)?hF$yRdWBHM58p+t+6qkYVG7?}(MrB+>FTP^s+wD1_v$*X)&6;e%j1|NM((g9 zI{iLWpX-n6S=9ySK|O`?U;Yvob6qa-Qt>v~l>0Z=&(QtAR90>-(@2zDhF|-EIOc#m z98vK$e>3>HOXc>zJhMP4`xJnvp+%rt@z09|1qHg3r}F=ef4F+K&016VI9uJ3 z4pgr8itZpRtNdOMQ1USqWLexq@a+gZu@at+eWlpaC*sRJTx2&`_EThW>iACUOlsrh zvKA5dis?3u0J~^}0Nc$koavgVQrb$Y%hGpVL%=NQ{D2zig=>hVv!~(|Bw!f#DK1TM z7_C{ov>h5ZdCGCq;OHIMiL(znle4#4dgZ_an_u!=(YXVn%ckPR=DE+ILw>8w!1{ty z&SAK)m8#+NSz(w%3hmp`2p3t_7R5nP{oj|N8l4=|o7$Hs)ZT_pof_esajbcgAF ztLT+DELrHG_zy&-JE^|cO&pVYg>quQ(iafMxrBH@Vyjf z08v?~jn!&-Q>UL|bIdf?!-Qpl9?$`A%fk%$Ga;+uKkOT~h{rm<7B#!34c!gs70Y!= zZOHTYZP{DBt-4LSoVlwoLDDc=XLByXU`uiyX<|k=4Deu>QUU-!&LVQs)wK9 zV`q&(fyFI}C#i29F<>3HD_V+@@wns*d21j-MkPTf4J`aQl$ zPA`;re9eBvN2iX;w)@(R(tiVz#Ucs=L2xg%$Yw0N?T!ko){))|M!+3W6k(;ZRc-?d zYP*PV&J{M~caU)B^sr)3^x6+$sMbThKs@E@#VFx0VX0GpIqM2sore`*Qd43AlXbiE zaq69A^PRU-AjL-5q-dy6p&CYI%ZboFt1wMGln#0M=bj$ZTDY)z13H@dCMwx};QyocZ zAc~7d)n|Ly70g~8RBhr@$k@%Cd#R`JwW=2>y*EF?)8X-!5)f_v4RH(c4SE@@XpT#u4E@wZsY%Py~GW;mK;xaMC?M z@!Fz`C767g`jA07lqT;w3)oAjgZ&tg-Vzd-3b0XL%D6W)s1-Usr>RJu#8oF(!Ji*t8zf#EgrE9o#BnRcvMW=f6h$ zHD~lk!fAT#d?oX)$nVYCg-T{{Btcsj|MM`+-I1zSzazrkYAa(S1UBIQ#Lh$d!F%=aS5x?y zw9@PBarFI&;PGqrtDDg`q=SbO6$B>1tf8FFE1)K8*@Ok#i9y^N5^Q-`Crn&L{xT`1 zsrS6^d{!6pbLH%*POlnLEZyjC*$#HVN;8pd}qIHO8RRKN6X(B{z_P3p)LAz<1F+x!#=y(Ghdscb(#c@lA- zdVrJiUv?jxC!b{rS6$G5?kGy{=QRG0F|?^Og-ZPx_Ai#d^F3Zxg)AY?I0k~$*xF@I z=4+}|!&E5Yo}g?{ya5jPd&jVlT)g*g>xvK_>rlQGooMZI6hv(%ga$Wt9{n^jO~M!ZVr zKZBA_%)kbB4GYRyjl7v1Jpq@>${-!wpqGJH zh==YpDX8mnEFF^~trdm2<;}TIzC)jc34c}v85vomoD%}P-?40u-E?539=*(U9C**>p#ZvD{*gtU zhmlnPdTMh((J2^qwDjlwyq)LRA`O+6M@yRm(qW*c{YR|A!Rd1mR2@SMNMYDoLckK8 zykq{19=qTewTYb78+tl1onv#t3+iTENBYx}*^mhq3fJ`}r2o zX(#YsQN0@X0RQ530kRQ8OK0~2OL!4TPm|5ks)DfS5W<-`Asl|{unb* zhPikZ8dI4mv8s-af!EI(u4_9WA@{k@fv7JJh!NVjpK?SS9zFB?vw0k9KyZV%#YU|9 zxtq21s29F@bnNzQ;{?m=;H%18j8xS3TSvfnJn{{}CH(+`k%#iyo-BoZ)0v47P&zVsb zGP>!q{T+XoBN-U=g*UtjcStF&(e0iE2?cFRAC%76Ls5%dfI)y(0b%~7&%a>61oOOA zmIT$7Nv_D31eS}5x0sNxZrlUAxTOJV9zko8XgHx*oh=mtamO@49cv<*I%>{A(0DH4 z{{|mfzLxhC1b*M{Gk4ftkX_UTqrbzbN6nk2`Ny-^yz>Pu?hEFH9DO&*-JJh5HF@~n zTSp_g^UJ_ZqaF7{o2*!xHQxnVf0b~7K%4MO?2HpaaOpb!%E?y_!)Q*vgc7Y=U35!p zE6IEhN|M?r>F>F^>MYw@;i9Rm!c;7*!~5tWD`y#XTZT?9KK>k#p>{=EDRMXy6zAqX z`79Xwx3moy)duX0sW@576???#49wNKm*^JNZTOzPLYa@y9 z%L6ZWbS#jxtI1>6b;#JK>xq9fU{kAR{ldZD{+blrM*;j;0aQ$$%o&saq8hKjh9nuO z?rMm^9v)og^IchIN`hE+Gdk^tXNLw&#ZYFwjeeD)NU;a@S#F2sMzQ9FKZr9oF2^E5 zp>`HRWZ>{)$(pB5X%8b5?iMBpN34#XQzP^&V(_r5aPf%Wm5p}fG&Nr%ls$=Fxrd`;BN3A zNwfMA++1E}gv(ZEiVRPf<|Ux0gw3F+r1m$o-zi8)IY?r_@bn63yZ|ot#CNKlbaD*P zgH<5jL7~m@|DsCes;a6l28USvGM+RW$X|U5#g7x;PwTm^g_-bT?N4#G=M2z!;GL&y zjds1fp+?Q%5_Vekyr1!v%ov)0j5Z)}i!_W^uLht=2jP~Mv$n$@356v}|Gwx(e3JbN zVILSi^*t_ePp_{AZoGE;+E))?Vn5gb=W2gnAEz)ZFfzjK;7yxo;6fM2{XLa&c?9t7 zzL2C6F^b}(#NlwCKx+>FwsMeK;!R}VkgzP4ZZSUgO(3EF%xva))whaGCD&m%r--RnS1f<_`O zWO2htBytcB|E4aGvi$#W-3XyG_)xEsGDxBr7fy$h5EY#n{If4TTkpRO4Q&jrYq^8f z8n*`e&EshWAJ~hp47fY0!-OjjJqR2JF9h0Y4TXN>Tf)U*cLEM^^RI@!+HE$68=9MQ z@0t`~V+NT%9yy|sD$l3In0ad55snH=^y)=~Pao2lRfkIICp+;S*32a%RG_UV{L-6-|UiIeMSYQFc&*@l-w5Y{ zYfK?RpmE}rke9B#C~ce_2G=*psViTuP9syxCBe>fu^rKd1uVMX!B))Qh)g~-*%ifG z6|nd9C*)I&A__RaM!5U}RvI+Ye#k2t`0Bv@&A_@&4syfn#(!Gqt19aS|6>i5c_8Zj z!aw6mM9kqI0Hl9!MD7vm;VNy-hsl2@n_&HAL9}MVdx)Pari;TOjBLWxVq&-E5NBwB zcY3enb3s#MM4=UMgh9TZ0dUQPNxWYL2zN7lOGRlqLZIKJ>7p%a`!zdTBBb>1awvfN zV}up2vy@xMPX^?YQPSjCi}e$#Q87<<^zD)zS6cJBRX?8_3sxt(HxV*+^PW_rKR-=6 zPg^h}aiM*~BJVK(c&xwU732=P4Ln=sjx+M?6s-v55$01&$jd|qs|9UQ5Pf?NK?)|M z(MXiezl|_6C?|~_7-4{$$6=dw{aXBOAbsR(eX99IbTk+n`V&MR2_DM3E==sFdB2i5 zj5rNBz3cb#@{cH@v0??uuB%SaweKAJyJsiVg&$i6XLNC+>)k4O={MDdxBT5|Spx3a z<x4b1IJg$E9te&zqS4E4F{ z+rx#lKfd+A9pz0mKE5Ul0Pg#{*(1OLU#%na3xTSKfQe6(faOHJ1j$WW;Z9J3K-eQ- zhDU&Td{l6A^#Mp6=uCGw&cLy_h*pPgm6#Dt8A93sFV~1Q#4wf)&&^RNZY^gW&j_J*a zLK>a~k{OxMpc?0B^J5+Dv^Y7fS!kOw^swkD%kjBuC@D>? zf#%jnJ4~v>dMJbw)XC*>s4#aKl*cgKhH6bzwjuw|xCnK<`&oW$4TZrXsc7jjE+f}F zlLeAS#T?GcX|`4)m6nI3Z6mZZWQPv6ozV~uW!rQS^@k=#+$B}tQo9o`SiAPh zN@imRG{(O`dC>{|D_u-3C;rdvQfy~D-xe3>H2S}sM*!OX)scMn6#pkKFwcf`9JxIQ z?ks9fqnpjiY(4)4z#pLQ4FTsAbW{2D6?Fdr5|;gsOmt~vJ=5Tzd(G1GKuLD)E%%o> z>Yk!^-)^jl`tGj25rw(3s@@m{5R}sTxjZ+8Hi&Eba9t7vQ1R&nv=?x50QZ5goz{oQ z!rkkJeHi_4E7k0ZS;30;AU+Op@+lz04{ic~+pThdSLm~A{LY^kPx>$~S)V1#NzbK> z3{l^9W0~1jSzGi2=?wc}_o+92(5;E&1GqI!geRBt{4W*jTA1I3yS}F%?IG@~`aFHA zFIiFbGy1cZ-EddCpWQHDWgRG!_6D&D&}9yED7qNkM0;HjN(l`kzX)J$Z@GhZM$@Ay zc_*tHj^{GxvpzKvLi~@GhLBG|)XK^l-uv_nPTxRP{NY-}S5{iM{kqC=j*YMxsg_^6s?5)USHthe*2zsxP`y zCB$;#IlCc@e0?qwBpCiLlZ^E?^}_QKkYjQu<~VdWcN79*pGHma21ey5rE1O?|F`&E z%essR7sIo!rzeJa>wGp|+r>QmR9`PIVgFjvO5$a24q;_^`QA_#Hun#+{bT%3Tq4%N zb6Ian>CP;{ppw$Nti8djFb@?()+QhNB8fUa{+^|42Fte~vy_|i$nkY?rYf6ldr%rH z@W9xZW#^HBMB#KwRj-hvnXRquGC|3aI|(FvLFt_O6#Yu*2KdLZGmX9qli-)?$N7B~ zC;8mrHVyUIaBoAgN^4nJSr&YnV(>TPR2m+0--=H3NIpy5lPf+k_(9t-Q@vs{ex2=w zdkvd;t2Cm$CF|?!Z$q38J0a~Hp#mnkqS_QhJt}Fd_ZXKv*G0lo4rO?WQx7 z1spnsN(Jm61U;7du!*=-c0AQbTM!%-eG+Rrc&N(DGA`uV^@aOWKSPQ~Rm6D@D>-Fh0j659vlZc-@FmAH&ZW4K{ zv!6EmWUgB%Kk=Q;Y|EwWBMo)+_p&V5T~Z^Aua9@Su4vsH`dwt|x!7~EBYu`j9;Ol1 zSLC`>Tjq0L#JaZMF0XQ*&g_sIvj#xtLjY=PFY#zv@0)YI={#tcm}eY)pCgSWGwaIj zK{rP{6Y@#N$IUNrVOV4=r`%(z@iPk)3p0p__XL`c7u`f#gjPptjnWo-lGB#I02ibr z%wzinr~x>_tFP@bFI21H|6tPxSEQoiM?v%a4R*$Nj0?9S-|^E>Y>P!tRISzdcB+dd z{jN7FhqdqED5%Z;G7z$@dp%p7&`8zua&x7shAADfd$b32@S2q+lG-grhO%JKF%gL^ zc0Qft*(76W_aNo3|IXn(nUTHQ-R9Ll-xe6nrEtefqqC`ustiV|@=EkFEgIDAf2DAi zcO(3)A_|w~A&-;A4)8er59AcB`s43x;Ecjl84EzzA+3zuW9|1l#vMVX$!cpF>#}C2 zQ%?@sZ>)-!G!}-~XNaeqU45TAYkS2Xi?}a#)h4Ufrgu64-{H_3w2e!c!m?Mama=vx z%;z=18`M*0#6(M>A}|E`}g zyKiRGo{*8wl$W|6cvK6^YFFpZDNE^p1OoE9bK1qbw)(7IB!zI}@APpm1oPx{={pCgs zMqyL!>h_v_*EcJ%Tv{e%Oj`P5u^^JOanmjV?N#ZRpyek|pbKOHo2uXP7b#M5&VTDl z4fjM}HMyaVgJE|;s$DZ^TFzMEQtkhJ#K!8-MZCc?Nyto~H&j|1y-~I6Gx{w!lF>8o z_CinQ+Rk+(b7{!Kh zcnObIR<}@@>vKn@Bna9vRMlwjmu{(Vdmx?ElNWkY*QhuztD!1fa}Pcx-JpmtfHI}- zeg08_P<~0_CjonHLEbn;mA}8TA}Tv=mpS6&vnO5qwV?QF5Km!OtPX3NQ)|jkzvk!? z1LoxWOkD%_cj7CVi+pI!t^~T)FV`=`&6Myk949w_^X&?hK_&R>QNL5*S&< z-2JjG&D>+>MsqP=fpd)|ZGKoJ1M@ZVVJ;7YI+Q+k{oUDt+oTuxvZY8s`7YB}Caz+3 zH5ChDdvCTxJLsfV8k=@@CPXYN#>oVeFJI+<*UGP^w%|G~;o;}$+B^moA))kziEM2t z>x~NLQl{!a^zDhY^iwP*XRKA5o_WYXNDHBC1C?-#*;;{I{-IkXrFqpoP8?r;&2wSX zed5t$MJ-$BzxFAl&fZ4#CXzQi{$N{WUl3obS`BpBp9*|V4y4F8Y zy|Qgu(kT(&s~91+y(%KXCcX+9La`sU>H4owF3%6~vG_e_fx*q+U%>dk{1x}T{DYjE zl$*Db+~Bj7O|MMOlhu1rJyNx`fu4*LkJZUHCe_L||I>ppoTtfZ!9z_ErBaL|lI7Oc>&$b!B+F zC{XeSqmUJ5uFqtoU!+=U@%B&{_EHcf5eO z246KHU$MZ=rlEcE!@6_iu7|`g#Bujom$&y`IB|wWT`Fxe3m<2_{nrNlxHei5sEBF} zACZzoP^hcYJUuyd%U528V;YWODJW7DUboB*<^=t;Ca!wy>4g3Q9o*fWQIn`af2gS* z_-niNXeG8t!)HRP&Wo|iNcBO&XHHLh)Qg3ko3?1eUbm%ZOL9waA1lfPxFoCC3fxAi zO$vQ`#t$7Q6nSU<%bwuXd(>6W5Pe*MZD)-AZhEyS6)QD&rc!ZKp{UBnkl&{IK? z2_tTs31YGT|1j{Qm~yDmL3EwMdZu8DBq&?AF8|u;rRX@)SEr}A)wH&CbNkz(oVB>Z zU$-!3q+1^<<<2+lGu8l$@-)Yv)4eQpQn7<(CNXyqkJl#}=RsZVP(HPiu#(0SjSx_s zp8o4+7AK`be}n+oUDejj$e*kWMaq7UjzR1NsIggw4Mnj8?Vrq15w1K|bN-TG-J<*s#=Y{bhIk<)h1nO`f1K_L*~rBtvX022g*6FXl{-6=q~Nrj*7n6PBjf4wk0B%?)oiGa`2njN#zOj|xDS zlfYEBNV-J1ik_B}JYr>GQgb==Hb(AUl5LT@+ubpkG8svumi_)ORlKPd%MzA+tpxf0R~0Bl(V7x^tBc9RK#!iQb9Xmd z8FU(``h(lOAhU?H>>p~fF5aIbE$ICBTF5+K0yJ{SJ5-hR4)m2Dmw&2sLgmq=Qg01# z!XB0jG_P+@4ztoG;`rCsU$ak+-0;oJnnGaZa+A+4Avth%?q3&A8V?-rl0sNu7USC{ z`4N5$Gq+FMb5B&10)AiCiF|X|3MZy*vGQ5v%l(muvsNF9t5v~X+T@y8SKL=T{Fdt3 z@#X34%vf5Ehn(8P3a!(?JQHSTnE&|tp`i!u;n{$of$z^iZ%T}e&vG>dPqoYWlj|BHMjaXf{qe7>ztn&L^ zC=W*g4=3xJRkwgD*v76oVKFtmTrTk0xV?L7?RI*PgYTG(*kX`g`oW|_rYh+$7oF_z z-(a&yH;r-HWPVn~iP~OU+7jvLKw@7seA<9M;O0!or!sYRsS?#{)xCLMTuP(O-$un> z632$~`y*1K#q=w_cXWpxNrD3MH`tTAgqqtZqwhD#N?VBT-bOJs2(9$4^#*Gu9GE)d zc4~WP`*urk1jV5J2}6r~hSrB%abma{f7WlT--P!1Q9Hq^dsvTR&F}ZfuA?6w=Yo_n zl58uxP9^m`hV`Ky7vQxY1QYAVL*W#$ha46|EjB~%V?$_17`oy9B0vZuzRmoS8-R`zB5T?w;5 zB?s5SH8uZzxSf?;IW_DgvQbX7C87vDjQA`!;)29@6Mn48-{sAgvy+oZv@s6uPZ(NM z#az70KH#mKz>X}L?euY!mdQQB{-8aQkG9CB6D4_(1XTVtkJnZo>7#(zd{scR)PKVq^r9a)ACtTFcvu^(D zS2iq5ycmziY#yolN3J`Ec)mX}L4){Y~+!4w07gW~x_5mfSa+0|!90?FpDkR7QFX=*#q z=#y+y102s+|5dU2l{%}1b9R0ag7;!*7{*XcI)EKaz_TC1m)Q4@U|EYA3Rjm1<_XqY zM_+rwi?IyZ37sK-_b>G9WBD$?vY!0->Wj(H56YlnjJds0k~NK${*1YZsxR8}CfH?R zF^X7|4y~(6>{&|tN3Y*6Cb%Bro?r65fgzI_6`W5}Hf#Oj8%>dOuJOB;@8Q6PbwB-G zqAMGG2YN|+{jI|fOH#+=Gs4~cyCP+lE0@xY&Q2>mu#gej#F`|@;uk#bxvx~Vinwdx zx;L@=e1B%lZg2PO4-#hzq-48Z4YQv|raeOA)WViKg%#PO-tQ{I*amYH=%}-XEkmv71E*zN{4T?Mex? z3W8c{7hQ?P&)1vjQ&Y1$S)-DjQ)fFvN*J9dyaeOS(u?=~YkI|1NgU6&1q1iFSvsm>|U7M*FVw`Tgkvy}$bg1_mPNdDBlKok2%x{Yn!@VuVv9TMk60MS_^ovU^tl zAaOuHlwQ)m?1_WaRoxEM9Z`d1T<3#JXEh!czJW95bj>E~y8j)y@+~WB2TlmH*Jeo# z*RvG&JL+?as-p3QZ}0PX0F{fK^Q+m%1<5W^-?bX4?=OVFP;=iiH5Q(C^&LVffn{!RnA-bui7BwG2C2!H?PAJ}?A z$L(DWy{g~Xz~#tisZk!>ab~aMHR|c~jxU{OFF6`sA|=}DoXdOwucO7IEPZpIpHzI??uNp);4Qk5S~7PB4Mx}GBxzZ_|<7qFu?kBwQl^4RnK&d<=b zH+z2V1%Az~iPJ&~PswdNCiZ9SxMwKXB(>#jjpc2ct=^y0{Qej+V1>#lEEmz@q_3EM zU+Vs>S8Iwpc;>o48B>&EkQmAQ>qeVQ`FZZCh|_R2artAnGNT?xs_p2D8Kkn#a;2>1 zRphs)((z6gEuhfEseebz8GcXV&IvV)ex5qxNzJ)?`&#RR%d8vhl9&9hJ!2UNatrxK z*FkAna}a$m`1%Lucw36LpD|{kr_PvB9+$N0av`E4!#@0bYuyH~uJ86^s9UahJn_^` zrah^S8+V&4G*LJTlevOV=d| z7tA3z2quC@vb8TNjGC{RWHDD;%t{XxHpdL z;2-dn23rdeLbRjSRw)<~y+EsQ@@J?;GleB5J!|>d6x}&jTK0&}cj4nxtfRM+W>a$8 zBq;JXCl}#i9**a$w1c0l zoEy{MYc>?7I_E}18}df$`-X`dg{0R^Ej6p3N-bYL`@0u*+c8f+;Jy~4b=SLD1F+-l zGS3y;@bS)Elt`L8EiQ@5rlgl)Esy`G05RgbL??B2kKe$jElJR#yyh|{)2#WN6rTzC z!f{pWo4cXUT`vCF>IY=RbCaR|Kk`XgH;WkvvxxfsuV5i@{M1WU%P(m>9xbE zdQhVe_$c%Rxjw>M-$v{!m`szicYt1)cPSvmmM_VDS#1){GV5IZxt#K#(%djAvgxma zp7^YO9qlxHI{zPuA1I1{LrEU953_RWv5 zn&Tf^0Pp7m3tOdlZYvJ<0Cj0(us3G$?X|)h-jVT%$Y<|P1ro}dT%?L@j4V}J@w&F< z^h&r>XGV?77xAl_q0b8yI^zmJsOGK+iGR&Lek;chW+!Klng7%DwsQ5%6VX$GC#S|E z%mQ{gQZ>N?z_$15za5j=g|3tAp0T6mUvJ}$|M|mEVz1@kS+uaG>oZOEFm|QM=Nad4 zPdBJ@29n9AQpsG@z+7?XV>Cg4>aJ4QeYv{LY-Gy2o)1^`_`eV3CI2pDJhP<70IHy& z`5tMJa-kKoL;eNsu1HjAD_uIy7AxQ=U08Fo^%N|NaNXR0tM#0#g=tsb?m}l96IsZv zHov6g&hJHbM%y?tkt;lpJOI77tdzCcz;Q@WM8QXuZrbd2x3j%h{CvvfyT;u*e|$*Il3BL5h<8;Mo>aqgz4Z;_>o|5(;(#>~eqcdqHGSoKiK6Ip z+y$)I_P@)|#tYVfkpZ}kJgdhc^jY0xegWf{gK?~0NTHI8Qh%8AXQF9s5v3mgSF^JB z86QI~EBeZo8sq2ZN{Ms(KQ_e{j8bl#$^r=Ncg5m4h^8LgMg`7&neYR*v5ISQd&W&@ zXo-9$7utOoI*Rr0xEAKAmHl$$i)VH1mzR_FcuhFof6iOUYV7Lw2Fx}-aziPINHJ~@ z&hm+anN)29sj3H${B_Ro{I`C#HQv8&PRngsN$lU7s>t-s=1_7Dwf}y6uDp(pgXfy; zDy_mth-JibTOyy6JyBwZ5c9ln@cr**oY)R7XY^;<5xOV7hqfgDRc-67AJ0}4p4K8u z9WR#pAt{puK4#mxO_x;_^%984vB*V9lD&qk=K^QUs-{g{ANTTKGYPxI~=e zG*Ybnxudu1oi0)`8X`Y3)f-J5XZapu9=7TtC8+cn{nSCW!V%l;g{-6(n0*q{$es{dgX(R+FmI#0b3R7RW?7<$(+wlB zd>E31kR(CMzgvlddM%BABUxgvqa$9<3Z7j0DCqt!V`p#7xBo7<&ni8!ZuHURf}oXt ztjCp6zNZrEP|6_i!HAREt4I!PwI3bpoYe0z>EHe}tEYbYdb;fZR7IE{MBIl{^3A8e z@EFe}V|&;aKV9D&vFOH{=KV>hyc3rAy%cagtK@B~W3TEAH-}iOVnM4}>NTGN`OuqL zrVhINQ9`0nQzb9~j0Y$sMzg}M%fL{Hkk--oQ9 zMi%b7hjja$&TL2fOIPcI0*it#cE5e2%mgOW;SQQg1J}M(vf+*OA4(YS88bUp{rj1& z@D=^^>T~k49IeTSYlZR~zYG$+ zJ$`DF>ea9RuuPDeRq`FOvp<{4fAOv`Ae7>4N9QpHsV7TQP?WE;q9|XHEe(dV7unCA zK|O_(3_=CWVXto^%FpxrDJcNGwg>4Mewq|z=f>(y3meHUC6jTrUR`L*)cEmd-z6*9 zqi;7(iM{Ng<$;O_u`Y`d#Av*Ph8%Ur1(J7BN}dgeeVGaJJ`6(iZyuNkoP($Wpz6Jq&&w z<5Df}#lYWa8F|EV6S>Q;k}Ev~UXq(8y>YDe4aiqz2OUhiqUTq`B*B_;N@s2yvjiE? zdSV|gr=Gj`^eTzJ)fN5``gi8fz3T#mZMBD{pyG`l}?>+-_D!&D_E{ z+F9%Bq3lAm!qeyGU}tQ=K~cwqpMx&w91UbaAq>r{V#_}k{y)0jIx4E~iyIaMDHV`Z zxouWKM*@`inz<45XvaUSd}zSzpXA6#EoWt@ zL5lBVJ12PYg_XZE zIsDkiFGedzuZG!r<9}tdS;!bslB|Hsy#7~!<^WSJPvp4-wEZDR^q;PFyA-iMvW2Z( zFi^HVV>T*$OJ6D3vUSxCUm6QadAKAey>8>Z!F2VuCHHd&ei{|>s{QZw>_p`xM!Pxs zuGp=9xFc2UX!48hlO`|2vT?qd+bCOgWosoXO_%2)_| z47JK;Y`*18+Pkb%U6Qo;kdXwcC}xpcN4z(o9G4}}BHurCADNr`OkL90NZi@kDK1OD z9!Iv1IUnQv=0Id@97Jko*KRFvuL)r#D7871bTeovOBVVl^o3O&bAB`E8=a9MeDERV zp6AvcwwzA{83tK|{04|91j!ews8^)t%e_jLkaB%{T zX%8h6akpm}`l&*p#&;A1je*+N0!JjAQlW#RqM}Z&y``QyJ{s2ium*M+Q2-K|BC96b z#h3x*K;TUDg?kxEt29t7mINT)+E|aY3gR_!6qbsAS)FS3|2ru#T<=~zW_3(99S$Qt z3LM}dk8|p6e9A@jOmV35A>@Ae%WX#S?cLQF6~cn+PmDz7lK2AR%eI^D8=i$5i|#92 zQz~cd1FRW}nR2oHy@8wl8>0M&K2yU-1-DtvQuHbMvLZh7AS+)sNe!_$wDO%3%Rc75 zV$MzWZIY$+6d5owGSOslO;J2?VPpp)Goa_ia6(I=r_YocqMsDHni=V-uyAu-h;HFv zG0xD)hQ@n5qhz)L*?`(@LGzclCqW`l4Inc?jER8oPfWFuSll|9-$u~)jG3m=uDfzx zPo)v?j4U-(vWR$~Z{;jD-jhy^x!ewP5begkMkhiTuhpSyF{6da#TY zRSd*CE6VQ1G<=nXf3|=Qqw&4BVtl=Cg=lKOtcJQ5=z)^tdPFh|uJ7LBdD62G0c^cz z4kUV%Yf;44iYs3lFiYPWxdy$m)Od?ASFJ#%kfHpm$RghQX;nHWE#3NSw!p{l#=d+s z#3Yy7LV)Za$QXW9DZN=^xSAX~;SsjaTA8SYyn4_Lh1D3DMdON=SKNy~k(Uo=$3pmSS6*_ML3B^Ip}#cSA-e zttG@mjuoMNqWR3r%i+=``=q2UXjlIEu>vADxv1FGv!D*@zq$H)RA*=?FXI$}*z~ho zohDun?{p(BuX@ju43)&hlAP1Okz6b`pask&*x%t$$KoRTKY0#;uW9e8L8-2z?|leG z-de<>rX2pJu~A+*&l0B{nK|gpUAAgVn~v^~Jh!3{F!hUE;RVzciN?kN+oOMxO6V_a zKyJz9HQ=dEi&{wZGcX9}yi9a(PQd3G^v4+Lpmq5SJ0_J^)$qjH5D6x0;UeGT; z?0w8NIYLeT(ZiKZil~g%;0L(~!v;#Qq zO9@C!62CgvF~iFQ0)&k^^(Gq8C|jOM&it%$CNRrfGghX=;{DZ}({NHkyWr_UQ^H%t z`<|W@2(d;0dVT&6XFyA3^AEHYE>dbcC@`TsED7`pt}4Viui+anMTSao4r?w+hS+H*tl|QXP^AhiC|Rgu(U1i7e3|Ns z5eyogkxB&O0&Hl1Fr3HgJ}$98QR81m5|yG%ZxE}|bDRD0S)Zpm(!ABWBfCKzPf}r# zr6OrYh*GXMA6jdd9$@R9(*P3{$4HoUwn42FgT+7PKP@7QE+j(zia$!{zXWM@KdH1pY5t z=%v%BS-SA1Xnrve+TdAcYryL0q?@FY7*W^f=vtCYeoz^QzOwak5bMu7K#2X~(L%{@ z1L<_iwi=-73bc>y+_2t@`l?RFIre(mR}gS~c6@;iyI{Ef{C@l{mT-Z_e}Hxciu`3y z<8mdA;KzEGQ~x`GiUATdY5?(?MH1Zrx-9(#Z^R9!GlfkY*~D|y5N+GP3FB@yu+3vx zo;&Qx^bPvI`ds(d2hw|tvaNgB7Zs?BF(^PWq146MnIkR%L3i!M%J~1n zTE;TCA^d00sY%G0$ZfqRwsv8MR{1ZXi7iR3OrnbTh(MM2%>#zGvrl?+{ihM@HK(82 z0Hdl%d7IIySigFyuyH-j)RX?f+ewx%V|dy7LE-MardA#OrumpXT?{~nBDbSBe&dQV z8PP@cawSvDlL5nkkp-SlM&>RpEZk&M>ERw;HwbHot#`n1lwxgdY`zX}bG(V3Ka`S} zmmiQaec5ug(S6enqw+z##e`+_5{q#2K;N)RmmcqMBWWwvkZlIF3^=qLXdCLM5#0deArpOyVO z$n$_-0PSImi`lz!loy7dUGw}LhHQBj))XtlOPVp=hufIEcMJH_Dr=|IWgyfEhlz(g zYKMsdHeZkYNf60GuSHWIP>f%OdXp3GJstJj^n$^sh+BxF5Ns?F?|cny>WDDn$!a{# z^1Mf)$>MP`w=qf0{|3M!w{X>H5x{?_H+}T8#wXz=N)P++sw46x%M+9AS0+zCCzK%f z;rWM_VN4S6o=={O7X?8Rp*(s}jlJJ$d&^vqCnegz4pj?CKT0f8e)yiA*$j~(&inm0 zW#Et;DKKiP%l}kM6Rrge9C)umnM>zGMh~p!){>q3t&zHZ<;HLxEzut)hr-_0v#3Ny zkJe;Ql_N< zX{f#!VLDAKAjUzNwGbbB+>8QHOU$Z-a_E0S1kulEJ=8|l(P4nUK&l$|6=2ae+IeAoesO|9!BZ5GrRuA{VxK zRYYKLyN^_x#Y8C7=2I%38w#Ezi|^1yfTF?hs9t#-;HOrI!+*P`JwemVXjye7 zn|h6CJJQ)Y)zl=FMMocz{7aN5mA>5yE`b zbUc?$yAkV|qh4!ot*J7L8Ja+oXIZIxifL3YHGOeXA!&ac1h^*T5qeX7wVCe{b1g&eVy|N5{M$60>mN0qzR_* zM?icBd!~ntM7Me7`ScIbPm|D6V`RYMm%sC;1KvROpTMczDWTPMMMs{Uh}$LsI_(4$ zk$jA@dMu>}D29XpF+iFlwV4?&?3lH)vDuNV02kniRMIgjsds2GPxKuO{&fn;e*n7q z6XgP(e=AZ4y6d2h*s0nA)`4)&0$RR{r7tAWO_LDa@f-yPki3XH-?nL5vryduNy1PhZ@b{-_kwlTffLG<`u7bO4w z<^}?S!EEyV&^B}05P1N?F2g?i3;qHQHP%l*IeJRg1fn0v#pUYrAu*n-t`@ILUQ~(- z6mk<#SxFMTrXG$J4Fy<}>J`w+y*gg_Yb(4-37qZL zeHciIjk)=j$~pd%>uX&#qrV&c3)xVHo@gSnE3@ctfn30X|HkF9|J%_6kdAH~R3yX3 zB)d-lS@|uq!C68xTavD>Q{)Df920V**%L^{x|BGmsB$dShExD5{&13yU(}qpB$aQj zEF)UyFc`k9U9}oYK-pZfI2l!Cf4O!TDCQ;{SLhgc^I(=Q55_dMH& z*9{+8i->?CF8w{gX%*Czntx6VxxYKwfjt!3z=vf}NjcT`4$Mh3Uf{%*%|EQESlZj3 z^qjzo0oundpxW308tU+{-F$P+FhU!W z59=g%mMAA0pQJV#@6H#|V*KW6Q|H5M#nAGySf8!3>CusJyJm-qAxh8rbV(%aPZE$a z*0>sXKm5W7Kr?atQgC+9-=H2=2Ce&(|6IG}WoLaticZg&gSH3aqry`QQ%Va65O{TXu9>e{hpnj=F+%q`Dp z#uQXQlX&IK-^TcSBe00}AIdxCG*3;02H1o%43w|$z&h81cas>l?&h|1zBjAvNoteb z+xE>M!bMeZdWQbuR8+7zE4mYytx(uLHfAdG0t6U|_8XT&9U;x~Dx&#IWXRf|w{Aww zXU*&&WeSC#+R+Z|T_hE30zYm;_WNIF+`GNKJs#ojxgjV58F);@jMHtekM~Q3P=?nl z`r1GgY#`Xkvt3wxXGIuQTp6K{LKN28Qs~hq!g=z3$yNwL`JG!yy)d?k)FeQARkTBk&Apv|5Q?BLZO~=#gz+r=} zz1L3jx}YM#htMRKVFR6$f%+jak2iYgjhCE;r!v0L8Q$;@mhj7>0&5YU>KC^YIp4Q7 z+dP75ufgbmyNFeMe{q5Qvk{9vaooOyAknuoqcoiSp%Qw4(!WK1e$%3~s7bg^wv z@x)poggC|(~s6{v6QJL2W<3l6Oe^O3WKaMys_hP&f(N6QXWo9t3Ol~MjpE#W zSe^+xH>kYrc0%joNd&E>i>w*38uf^9TrJ-qu{Ct%BF)aZM=YUspAAQzCAOKuNkH(| zO0l`Uyz*UXmhVcWUrd1>NBu(V1G!d4U#s#me>7s{%=kdIx4&BGe-h%ghNDN?uchKuE0~<-gtf#=KAd!Z^>pyfG zj$x<0if=!G_l6ZSgu<608|-_QIUvcpmi7#m&Og}VZXRs_IAvV|AvKHg>AigUt3sNO zI0o#!bMR`Q6l!#xLRvI?$kSXFyF&F*S2O2MES;|ki^JM!vg7+x#G>X@d<_>kbNAqV zjWF38VP~M6|#nst>hgQa5Uo%PPTH09D`*-nhd`uXYXN1#+uJ;O|R_@V#j zjktu^lrCTG-^Ai0hO_bXommE9_ztKN#m(E!42T~ulED|cqFVXU5!cThWJK{efM&{`lR5BKo-XfzNUozpEd1={p^n^+9|q| zQ3K)@CNme%co5!{B9qDoL@Qdri5^65ng37C`c0yR4vS%bG(#*O+*6$L0{VS`C!z;l zqF41Twsws25we1^DI-2)8t)Zjl5{OkewA8H$Q?(lb+p4kp84{{eINlzn_sVXv-e70 zc{pt$s&LzF=3Rzdr3Wg_l-;=pxH@=Llz@jXROO56V+e#qbys!BU0bwGzQob zo7=fKdw<7Fg#C zpo8&ThxlA|{py<;t|4bCj30Ava$JD4tem=fB~-R7jhD8Tg@N#k2YK7UaJp5ey@5?k z-;TJ&)>`m+|0h*VLM~#(?*JEw?ao)zmhB=L^N;m(1BMN_DND+pn<^qo^Xq(1KgobM z5yjlofg#3Dh@{uXVc=MjoxH*A;0osZrey-rTP4hwB)kEA;pma2#jmM*k~^nA%(u%t zT?Y`eoM1mx@c0jx`)$_WE5&5xW`AC=R*WAxw2t_Rd)7auIt* z>CgG<>hRpQ_A;Y242O!wB=x3sEx&Y9f`4%R^?P!>fTG&vhY`j4FDsVLkWQZTlt4F# z_#ka!KqoSRtIceXgGtw~&jUNYt!iI2n49;isJk1}0ya+ZfjP9317#x}?0mJ?j=rnx z&m$xEumJys`g&)GdFY5r0yo?qgn;DCLsh*73R5DG6s#X|4Hpahi5f%YM zslTBt!q+ncig?{Um%U%u0b5=(29QdZc)qKfNnF!n4SuBI+JtLQVEPfMTtR7I|K?uJ_sM`F{e4cA*h%VIT7>;qSYoa= zKZ$A#1jg#aO#42xPy$Y?b`E*c^T&Gt7mz&9G%nguj3@`g3TMra431h^(=Wdc-Vh|H zt=v?|wx_TE#WBkS5b`j<=~N^;J3FcYHa-w?qPGLGuTz3F&#M>vGl2fta*qW~mNY#} zGGg2~5s4nM|8M^JuO>r6`-%=WXIHn6pl`6XXC*b5jjVb3?31=~q}60yt)(voZM1w7 z&`Fl$QS;>hfype8= zz`FRWHh$Xwx2_NG{51oLLd$PTt>C^FFb{UGn@Z}u@Wj6$sWlL!%_GF*Jq6Mg-{}iK zMg|nowsN6q#sQq!?6mW~^h5_wF;V>Pll^~CHtqiYekEO9!5n7};p_5;vilEttq+0s zblsTS(T^|scAW>}n-8j&gNr*gwIaypx1mb0BxB?FPXV6K{QS3=%<8ExS|hWwVKA6$ zyQG+;B(kim?9C#cC53uvd%VE1$n`_-N4ZsuCL1Cak^C}wXn;{ZMi+WjaLLev zj{RsH6#%@qS{$t`-4|ol=N9&QJutLnc6RoU+WTTAu>GTiZq>Ixx{+u19QVf=HNptE zTL_mvjY|uHCU%eMCtwE3fZNDbCT5)En=*z7I{EX zB-xyqKfakuP$+fJ*>&3RvA3@x{Jf!-iGEb=Gyv)W*nQ8fMymhWeNO*R?CS3dC1<|` z6jjIOlBbML2K)rtWuX3lWbFD32u{_L!s&p~Mv{Q|2H??Fz&0#mg51p{asCddp+)cl zWZgQ((fBP8_5GLU38w4`CuF27u4Mu~N})7=4}A1W6o4N5$QjoPYnex~icm`cWdBpI zJKAGK96Ffc+0guKGN9LW2*_~$9kK)XT2w%?%+st57pM==i~&9bHcer?95&i>;Gfym zQxh8tz?IoAIy1NX@7d(LNNaeeRW3}-rg8ezjR-(AjtsDcgsCUF*wl)`x)r^j`kN>* zu$8+I!o)tT5CV@bLI)Q0?*iI-$;3`Rm~1xMsKm`TU)7@>Q1@Wt zngTq;skzPeiGL+Nv3`fB&(&h_j9iyn1FfJG^GloErRmn~6ApZu)nzlT17uh2fjftX zQ9qBc@$hQT!uw5vBk)#MMMMHD(tTc%HJ=7*Hd-nbtyJ@ZTEQPjf&Cuief6yI_-21i z;k-}7pk1TcGXndeT$-$_r?0Pe{oSvMf6@t^oVcy9Bz5mhD1g+zhX(#{GU7~{6)IP?_DBj^!=eez=N>&k5MS;`lX1}zdx=*@o z--n2>@>hb*&7_zM@D$Bf<8;WL5Ijgcxq1IRIj2R=%T(!ZnQ{ zFoovmd%kCFN_fSi0~GmjWOK6-P{+OoIAbow_l_X{Q3SbCJ^uEK1FXoG=Lij`U{ zj>nfbj;NN(%qnc`u!Vh;kfjnE+S6;sT1A8}PRKE4Lr8r)%3wvs`K@EQ+fcfy#v?U>DpLCqS4t#oWjMsgm7uGDdo=9(2gAO*caq})kE2q`<9ZWH3Vis zW`CGSGnrIXjz70MeGMq0ddttTOaJHzsVxi~ussSDt*F$$j2JLKLb;Z`fP?Da`V05& z98<@;uTxLhypZd(u2!^iuUh!bdIpEkaoUnYgzrWb4d-EpyxRvAC&lx2BtLpeN?6Cx2Z=Jw*C1=bvxWE_0gKK@vWSg7(#eN z#46NtbZpG{Yrd+HmzQVRd2!9EYaN?!oBPJLWDU2l_Xa9rpJRPaB`ymz|13TD*~P_$ z@mNmSy@OYzb+egf@8tAkW?9)tt-%fiXk%)#Tt5Bj6A5Rs#>*;mv&%h zEAb>|slNh;vS8e(K2+N+hPMU0pO~6_d4}^b5qWjzOj8g^nWo1T=4!lI9k1D7LBh>t zE0IxiT)Q!;Hf3aYugk>0`+P!miquW`H42bc3w&ED1Jr&33~svfaPxZ15tp7BfBv8P zI72FW+e0e6*RBX(3doysZ6PCk(^c?}$4+?)e4MPoSs7}DP9hJqPaB1-EM?p;U~%Tj zr-gG_PrsQT7QE*X*Sv??`(l*Wa+v=R;gf_MM4^`bYKWuAGeJm$xEU z--JzbE~D*zp5`&>&Vb}37QC8oeR?)@J->N@pj)i#7vl<(A(6cs)bJRg2MKt&hm&I0 zoXdWcPeniCY5(m#*@NbF|GfoB6FUHM*Ezht;55V?trUAG-5y2p>O(d8KGf^9t&8B~ zYL9MZ*Di+9rO$~GUta&KwSpI>)DP-uvUkX{wc%g=KeVU=J7un*Hfe?v=L-Jh$rCty zX&OQ6f5)pHq@j4@s!ew(ewSDL@O}rai2wl`+Z(TS7#+V1LJ12GH;knZ})dLOVKSKE`wC{ zCoAXE#nOy-w;Y)B70m~lpC+-no#twc+(6*JN3)}U30TP4e`3JK2;4gLqkKm z*p((5X^#<m&jZzsW62bzUS@tX<`i$!?@Uuc#7biIr9?Sn@vjuRP;d8O&@yLyj3 zPCfJ1279H#3+J?MGOQB`L$_UhTO6Ty>w%l^;jil5PMa#NE`N8~D6o&RJ1>?`kNQPF zeN)xMLxgwCoY~e!xtID)40x9Y_x0=96_A9)*YxclbTU8_7n%pZC#XpQExE%7V^DV( zxcc`W%(n=L=CoLuq>-t@KY;|U$^(RFG=DFchk>Ct&(+ZDr2Q%%gxY!i^cLYZY(IpR zVB}M+@7Ct#E^WpAM>)9(4{T!^JobcfQ8cjZaxNx_mHEvJd98LQ&be$mErmu|8vMoq0pl=A(dmNUGS#!ICIUO zPdNE)dyL3vam`{$;vzrPoFBH6ec`I1%cq4=t2>Qy3?Tq~`L7~T6j=0Pu5PV%$xZIu zhYWM$NuOtHj1Vrsqlu$NO_&v|=N4T`v76@=Wo4ft%B-xcex#-z#E}!BPb&(FtX_u= zvonf_DCm6p$PE?WZImJLen-Fy$|MWQzIS5iF=r@6@)cbR1Css5@BABESHh z*jrwER~G!T#TQEuG8Hk#oE<*1(opir2v4(??IQ)7pwNv?niL;X+%8VTt^JITmvlW3 zPU&{`?VD}Obj`i-OhNayczPV0o=w;Lr2`s>ADcv3Z{sF@lIvu(y&x`?P)_GPK#b@Y zv|Vmad+-|a;2+NVJWMKNc_6b6y^Gu(-+@OH7oXo*K#xHqYH_8L=P^QOv1bp=zw^iU zn)H{wZeOgiA-&^62W#kY1gV!k=&1|H&+fa$zYUB$Ov{&pnn!<|c4b7m`!nM={bjr8@_egPEHcF%*PwZ8-jX}dBh{G7W@ml(jL+qL?R zG@51IQRnMv%;V7LnjzJ^jxRTyT4tia==kmQd{(2G;OD1%Y> zX&%koZJbFLOoiQt51-M#U8M|ZR=^Gt6-_K8JKYMHyv0HS5>Q*OXWD3c0>Rz=rHJ!r zZsNYndAd(B>}{zIntThpEnju9Ty(%eu7<9R^>oR2A)las#cqj!T?qO9^XCa3k}b;t z2V;=Lv30oDLZL|mW5=mU9lxYZ#>W9ytON{W@={&y#??XVcMJ6UXkG>`@B9EAK72*;!7XuB-1(jf5kn) z1-H=moJqI13#8jMpL)I#*m6(Xj?rG^bE+On0Te{pQ@R+DsH>;Oa6V_G#q71VzKhZB zj1eEqfqeUJf(+%UY9{{Uwg~9mi)p=&bZPDZxHQsYu;yE`oEYJ2)l#^GM;q;%wdJ#` zO^tky_dSSF2%tA&Eg1neF1B3q9xmvc@e|!Ab`mZZw4>FQ^_ySwCmMWM-{v!AHsBBB z9C|>>W>VOFpFwS=E6N*|>;3l?^Jn1k;r?VVGF-}@gH?E zKZr_b+NEqrhQ@Qo1{?$_9^|}eDA-vWWpJKa?cIB-^tQ{-NE_1h{YWe6V_~&AVFLef zSGUB-wRGjL^ND6-VpzpzC*(m7k;I2jw_a|tD0@@FbJq$1WtHp!2ldc>nYBWF*WONN z7-*Op=GtsF=iHFD6LQS@i46XE6_%~vMF@UC*IF48Pe7iPCiP0iXHxawpSU;}S5(ee zYO>T&c&X=f2(1+F%-!IvAhxfT?c@vT2?WS3-lmav+O{iC$jm5-?`%pDmeMg181u|)dqMR6A&NjpRbS4HxiJoWUg2{7a#uLDM0l&9i*s_98_P7!xb;0&87BfN zIN?ZGwfQHSXsoUI3NJA#V6TDeD?b9ZQC-@10zi+8Vt^sG`2;!w1(gmOB9yTM9 zgXKFV*%;p+(mJpy(AR--zi1o*+Nil4DK5<_j?!e(d$t(iTc2f=_V;43#RUaf6||`z z8z#FO-PdB&;|q98x_0yFwv^$%J}E$ZfUaPd=UOcRHom_81-BAjK78Col~l}J@#&t9 zEH0MI`aEW*A!OrEok-tt7mDd4X^|EZ6S)G$5eAi_?Iv?$l5e_}LIu0>_gu*WQg*PO zF`S!AlyI$$?B$_o%tR2W6maN7Ae^^$e!Xq`1o+}zkDq7{mLECMJH4=A&Zhto2A{kR zd4hoTc=>;!B&TGm(K@5-ke$;FDEdM+#+P8nTSo)!jr=CbUtE%(?dHprWdULO6#&+5 zvJsVfzQ|cZ^WxJyVcuA{c5+@{6HFXiP5l_7zr}3eZO<7>iRJ*O!ZEX^M9fg}We!I$ z)5^OPZu{R}M_w&Q*!_qbS{CUFfa7LxY&Zik?UlU zW>DqHrROu~!Vm8%JZGS4zn5+*N%O`_d*_?VGHlEig1ypvrNjzz7ur7v^W^ht0$(yJ zdclrz`7njDOjh=g_lA4gF|#p-L~6u}H1)fmr>9&7=(U(RxU>gHbr=z`G+j~hTeN|o zESfpKhfzGcgEdR#iDpJ_1E<}ieA}t=-}W#rx?+(rmVHvj#&z~=Pf?Hru}|{Iy-uA7 zR6Jbj^2GV|>{Sr=Uvw{vKIMNovOmk=y4Cixk_vR|yJ;_ERj?T73W*nH@gv`-V#7s2 zzyRoP?XU12PhR{};c)tOgEHYvJLF}4Rs`|!6hXh?Tr zEd~KLR%Q%({KsSW1rs$*H}uOxP*oibxXHLL@i0%27+xjTAQ&?yh#$T^!E6V>sdtBHzcyR1bK9%1g1rD&{J^wI%62|^~N0?nC(UfTfz40&vMt-`5;sq6>O`FDTa%nS`p-GZM76tPx* z!bd6+0|>@-gI2|f^2psClnKQDaX0vpyX24D&Hss{RkA(SAdt@*Ivv7VyIRueASz4-&m!d8ImnJxmY+M@>oBS^pZV5+{__|sDQU9x^3N47uO6>j4bj(gvu03z@*X%qd0%P_mf?>_2-+HnFo+k$=eEW%L0YwIE z#&qO{ZxOLf1cQi(!Sp*WuK4fYzk3(3SEGnTlBJ5+a2kpjia6Y(LiR26 z_0#VkDA>y%CN+VO6cGJ!TRikYz;==&fY8B_+nvYyIjqy(yWds&a}t~W;E+hZ}LVsW1pR%%8N-8P_X6C5xad9rflv%>fH93tcCi*V} zwiO=(1&*uXjB)&l?}O&q9&!9V`MYZ2=LFmKyy*}Yez-DLC?wX8nXc(oVU2S!ETnY4T2~eZ;NoHb#?wEV<;|WH8 zX#D;JSOPL@El44TwEhQan8;Y^?PK^;QBf7biUA%S9lg1ZJEv@x?TcsI%2jrM=s2fK z7YT&l@%bp-U-N++>G-&sWE~#X?p}1C12HYe`IEpWfmqSc#1v2>LBN2oERURBTTYU3 zlJ|sjNxqRqnql#U9>!F{j$f#%YfBx@SLK>K*yrGj`B-gZe)Rp#v+R>m(tLopw^-P~ zno`fyPq~w=IFvo!ogpes%)MY%x25SY39V6r2sc@d(QH4v_~O4pID#%PTL(eb67DEq zF5GF2A>(`V9et5j5p<&Q$j-jyGpjJUr6#3Y$%5(LE0jgE+lsu#&%Yx!>1cfHOe38cW_W!Ow;LSW?}8 zPs@LOREsspPg_S15uTR4`xL;W;c^|kc`-0O{7F(xGTU0X;Fz8tesbQ6Z)SZVykNUS z-{a_xxDI;xaphwVqFZ+xKj6u2QTWApa$#B!({Q4S#=eO1m0S5bq|=`BS8A6ME8x3T!%{) ziS^EXISzEB7BLJrM_Dldev?Z3aH;BroXBJ#2KjSFkmiO@)6qSig(;#!Hp!3$9D1@t zNAv!r$dxGnP0u$~B_)~VhuR;$1{DdillwP|^{!cQ-Nf0#H`?$~ZW!?WAxAP%_YHjR=!k>3)ow!^im@v-Iflhm5EK(n8Dj-^_c^n{?-sSTRo!6PQKg`E-66 z(gE6hu8K6j%L1Wxfu?tMw8QxindBE_Ukwf$wtl~!sQO4(54>#!bx6Cvwpcg6*@!5$ z8cx^UOPcl9&aKIT``r5g5&pu|F-ThhRH9cZoQzwX1kE&w;eQ{Y1Rh@du6&~aZA})L zR;t^J5q`VS8rDrF@8D3rF(3|GABJSTAgUSJp?aB%;In_*LXv2pUXn%AuQ67Z8MrGj zIkmAtU4NGq=IEX_K+E)Tg<9p`4A8{rE{ z7ZlHMDnHQ=SXkHzL{yI$rY#Fl z;enqxJ$7z@J3=Iar=&J0jH$1697QhhM}N;Wc;5Co+Dr_kvePrIj-;|~r z{L6C#8|aRn;hxTIyCl2V;F39$WJ%L?UyR&8Z~rr^#>D@;JWqe*){LUsr-55`V&M|74cHVEk}D>8BW|uwEk{b2Kb*ADxE2RQWxL)4^m#3O?xCL@Rn6 zc97;g92kRx;oVIpH0My2zWl30)4W^Xw`sCjvv!D;eqx_G*v9`=i$#8O{wHI~h<2t> zd$j;pb15(8Cl!h0)5lAEiaTs*R#y4im7B3>yUgP|FrAXzr~>;)c2f^xHk9a1&(2Pq zD)Qa1{#!C11p{wrS_x51%7g3&$W4kQ66nHrN&fA%cCw@r~lO2;~hE} zRD2tf?hd07lKQ>$g^PxUc7!eiBv0l8%iWsKb^2W*fbk$G zGAY#9#g`z@3epfq7(TR6DUNc}?40wivihBOWHKZ{iK`;#7Zy~0A!}I;&yiTG5e8yf zLaUb-yAo^*zs{^bBT+M?QvWD4P||6feo|Lf7N^p=xx4J>Pj?#nXP|8Bwxna8UCs zrr>Ynacerg9OT;EzWLM18hp$0SV?gMB(3bwn@t`O%TiT*lFo+Mrq|npp!SFQW>l~g zLTre&47UyBV`rPQZ@wO%Q)3Vv-0( z2>@WN61tNp@9vCPm?ji8DA~MJ6B9K?E`DNKDkTIxNYDWIZ$D`>rpQC;QK1!&{1oXp zQ{$5RJX3gd^=Q#9&n_AI%zE-Q0bU~DF*dFCq?a?z#0aUQZFEmyY_K+-CBSCPtN9u( zN389cCmNrTjl{_I_wD?iHy`(ZddrA;8r)Wq*^4m9ok-3Y)x$jZQKSg|&6VIdvki<` zP*jpNafJ(1P-csfpeP-`e9<&()*zaTW%^VS{}$PQ1seb_1Yx{jC}7vB^fNNM{^uNj zeiR6rzXH+v$`pB_FKUx>`(E&dv0;g0_$PS^A(0qClx${2jHMBLCDF#d`bU%Q?0kIC zAKyJL+w_^j??TURVMm$z$Rf+uVhQae19i8qUN{-vcFnL*v(;|~HQQmg$8#yipO(Pv z+Jtyw2|!i$;La-LS4t9~nNryk^L@nw0?_P+whr;lE&YLiufg%hVe(;}x zRb*fF(&=T##rQrd?&PETOYAqL?>WkNN_mJSa!gp~sUA3Vy|Vh)X@Jx~xMANa*~{0S z@Y^=Q7&+Bi3cVBdv=pPf_I)|_IY)Tq#z0;|&&AW}4-X^CZ*3UxHIFdH^ zE`235+y5ian8uLhc2i?-GCRc+3VDwKPW!RbPnvNs+E2-LzS>K~LfNALxz?`oVQflU zC);YHppVRaz6F6rA7R3bx9Y#VhAj;CvwqS~_|`!tWTKQ#2Lq>bTxuP%4`xB+h8Z0) z+Mn~D182RhzUa=|Zh8;;^W|k63!etZwvW>X^q1`peKOsJBLpZo5tk?z$Kr{4?k{C@ z4TX8Gh|#(x76NS8=5~&fuuK<6Tlukol0sDQ)B(=~Kks8a6(SIg{vWhS@$nJb{Ph>w z1e)vlEKW@K{FnB^v>PmoDB)P1{fJ%A65pNocDNj9qSNnUwRh1+bZq4?H0!IGn&NxT z6@_oArCMB6@R@w<%;8GS+c!Q{k`upKlhYa3RPt^k7M1Le#+sI>Gq(eYbiq|czpuWP zfsl5Ptf7BQlku!TqQUL9X3;^I$N8a~H3By?mt;gECB9d?zYvvy>KX$?VHn42-cd7n zJ(@IED8l2XfEcU-?js%{a!p2(wYIMySSU`f!@Z>yk-Zc-D}J55jUc{4srg_*0ONSfWQ zQ*>?J^itH2;DcihwpeE>O~H)YQ6+b;Va@E!3)w2K z5S7r^mr$49-IK3&(B58+bv7(FYkS7v)GFe$d!>*^OEwOE+CsPH-c?arYciP8A>!GQ zUlmkV6W;Q8<~(7hDx#a=?Chf7k+$<=qF0QN4`a|8+sdsqg~W1IX9ZahlHUC` zb*fvFIJF0i-NO40h8Kx_QC$QFYm}{{7gLK9EX1_VcdB~(Ul*D?z0o}pmopTy{On2o zcukj!-Q^VPQfG2t&S9iqN5kS(>`HUr;o9s`r%@su)zaCgWI;-~{Ia~|+E0v79qP4* zA<8cpIV^WFi_c_3EteijU3oAtebv}kKdNjX_02&iqC02#je?3ryQuGj}hRE0V zdNRpVd!o)?_ZW}%DPR&}@%bFtmridNUHu(Ls-6qx$+n~z?ox6FiAPnhJQhgOfn<91 zw4CMY-z#|h$}p}o!j%dZK!C^WP8UiV9x3p>E%4a4`lPON)w0P2G2T=pm;d%Dx0#rD z5T9gRD3rFWDtskKA`7(w6uMjd6sR@nGLlXEvlJ1irC|TH6pX!R^}#kaHZ}4r{y!pH z_=#2bwD}!p3R=YB<_24QpK>S(ygGFboYBKYkyVOyRjMT1d@ex1PS6_Ix8|W z3(PuUl?@MkdD&aD;>y(-6gLXQWLSbYcQ->;Vwn6Wkpyu1_p)75aNky&-rYO8OG@wAon|hQWmf&HWm*_ki2tHZilc`do&qBuqOLd*G(V=> zRvM6UWaxTroYt!Bn-$f=)#|`dDu(^!t-v?51DCflI&cyU`Irl0`{9L$#=hsdw{He* z^)32M?L8H=>%fcD%qHVnRA<v8TOCu&CMej_{ubOARgL*BCm+WslDoT+fRmPrxF?#u9?Ae*$ zr)P}{UGA1uGqf?@$E%;|O9r>ng{2U8+I^c0LVHhLwikKfeNnY*cNkU%-4~~veP+@; zCb#+bm_(hYU&v?rWMH#$8(*)uXy;w<+lOCeC*CmZta>>tbadm~&!VXS#PL8@*7Cwc zpX3fRP=QajD91mQ0Xvmi`&5PdPbiiZ6^cFk6^g}$UG5rCJrI1dDaT^6ufhF@KCf9` z`E$jjHv8TV*U{ICOzpDIADIYB?I(JYPMKA9KIX;?ubq5$5^Hgi+mc*V!1%IKk;8&;h44ayWT+5VNex$$>XDq+`B4zzHB>7dm3hY`=s|WIJR|!5!;~^u7@DKz3MS% zcrqs*)P-YNl^FVn>O)k*gF*7BL2Wi=Ga|e;^5j=41R{T7+ zeiM5uXbXVI8&95XMr*5wyV=*$17{=ZgpMKQq>H=<=eqxA=jY!UrH@3AGX>qkQJdy5 zqVe5yE_uH1J4+H{mtOR6_1s#hbdEZfc-o#3>g}(DTFx_DPE&EH2^ENZ4t^#t3a3P2 zNh$8^m_JyO5rriae+ODIr?NHKHPE6IFWNbtl82MAvT?a=jINh8J70@><67&gCv)%i zRmmhQ$zzd0ZHy=J`*s%DgEQZXV}|PXT;CL$|Dfhn`A8qm?sR|en9~ooR=Q^{HPq*& z*Nt)0yYN|N!`xMZ7fdRRv$syTqEg8g+B5~V`$e639C|=~SW%Z2oY3c|EV=6`I+5#= zt>|eu`a*e0iJtdqoyy9$Fzb88bp=TR*Q*|FRnd@>b5#o+B{9dUFEDstJ-;hhH>HDP zzL4zO$Uz`U6;#%=7w8H#kwwLOr)`T|Dp6{U%9HNupQwETDrzr+irTY5W=1xAfR(5i zFvnNIkdv&d;`*jj-^IdmxH#6MYWL_RQEB@^r8G8!#8vi+C8tyAVhT1kC1;_6p9-1HP&B9ZpB-|T$C$_DQ?WmqP!y+`T?R7pP0?|g zh(wUEP_EZ3;JzX`d1&}88UIW=Za(WHhse*1eh@jbt{@M$TB^kP-~|peG8w%BnF$NB zd$VtMo0D_=`Vt`au=_c|hN86J=Y5&)ZhkJFA-;F6-n)DE`9FCD8&1Z9 zLJUg(-Gg8GEsJ!Iy60xlR+&R!DHBq9oPr6{l;& z^2yHc**>23fQ$H&L)k}+>02nDv=P)I&_F2;7=fLs%XMV!A=_$iq{kPX!T5oFzi6J{G+3@C{X_!XE1 zw-D1^=*R__pZnr2bfb;ni(dV%ipy_Jeuo96$)5pDo}~WC<%bW|6T!j%7?kRDc!?OO z`Sl0srlwYCKZDDwJ6O27OS`1uh(L)Y6h06!r(=AM+FI!!WEB4dT`5pNq3?l_jLu^g z?jX)6n6TS+5Y#quL7D~pBsB`WikD=a;*$bVH%`dRDvhrI5iTSQ{A-(2myz^q@MUC6 zy9WRFGzZB6x2QbeTeKATah59~(G?7+2@`m~c>X#6q$YWH-7% z#?H>kv=>LYi%RxT0!al2RPom`REh%GK!O)NVo=;-J4C%YLQIYTJQDTNS-N-;xcxYZ z%^Z?iGBa;Qz1b=%XY~bAI57MjO7jKyyP1f7@gU&4F)b#enxIfEjmR#eXO|RANA>Ez+AbPFq&v43fcotu3c4vdf zih$JmCOU~$a6llC6wE6Yzej^g8%u=fETlzLy@jE1u&u%UB~@QVGN(yno*(6hoO&QN zFuc(NZj^EA4s(q|n-xFs^ss-S=t`8r6Ww%dow>j7#(0`MK1R#cC zn>{3@`FnJ2><|My*ifg=Ui^H6lIYvQ0+srq^mm^hjo<6U<a z*W@S2M8Iw{nubn6XgGTPs$i+BoDQeKfqPlfO>(1UE-Npi9wk@2E6E+Cty7&ExB{r@ zbINh96z0)PWjv0JVcAkpl2st}VL#C%4t$wFnD&+dD-fW6$W}{)=batp zE(ZtJ?hexNR)k~

|)1r>;4t)Evze4rP4%PnbujCEfO5HeSo>Qwvp}yNRqx4?li`yx~)# zp(K@Ua@)#}ggx7s{y;xtcfX?fpUFuL%Vq-ekP2ky3WVfGMu4JZT|-04eqtfMa&d8S zLPEkfgpuaM-^s<_`OwA9TLiGr$hTJmle)CufMmQBm6Kkc*{2;fhSTMui0!$$Y%MN* zwdc=yyNZx063{q^(a*-X>z6l%OQoBAEQM)C1Sd;jtPqTHTwb|2-@{=1vu{&2+D&$q z7xQTVY%I`;)5*xms42;E+0Qm;`UY@nd3VDM*G3@A^!j>wB^BtL4f{vAU9FA^KbloT z_*SC{yHUBty^Cw$TtRCBnKcV^)Vf5e0{+k|K`n`>&i^# z=>PMrb~kT{TOA;RBdX*WlMdu0wf23GDu#wintTck(JxizXm)SM;RLpko^Dilc)8cQ zUk~MHFqBBw_qr8{Ro*>aJ%QIiMm1l%c?5rPh>+X$yW(NxbG@qvdoiMq52|2D9{-w* z&G5xLA77`7%=Gkk6;}sBMV|`0ZCPD43r)-$qW+2^hk9IIxja}ql+7ES`}tZ}P*}cKm_UUT=IY`*k8JTEq4OTp48$!7gINlI;m+i+Z+Rx z#d|t&kHR9H6WIQzGW&n+xY|`*-assqaBY z${-(pmL^@lC|e*Onh7M{p^?1zxDF8VEPMsIbdBXo>l4)7j1YBuefn-Dh14`fAv2_+ zE3CPe9riR^ZBf^9b=W;gH3TVvm@^IZHS@qlkZTXsdYX-1hy#64zyy-1Z%Tw!uDwRF zO!kSSj{c~pKFG_U;)s%%;)wEcHFM7=re6xHJvk1f=7SZp^klmmmWo9+RFfZcB@&8S z#%>jgn9Iwmn@JabT+*VJC)USMv-S_ADUQj6LYc*pLWgCSwR``W?oCVTD{Ilk8SS9*fjp4k zk26pE16y8lg@ZE7#>5Z%_pTI_s`8?9vZ|#P5t4o%U(TPtYCB7>ttrilSfL1 zSh$N5UL|mR^nCNuQ#TC2kv0RA{x20*q5XI0eKt5I7k7{9Z&?b=6R0IcaH%e zrqxaIaloGXg=(ZC15|kvn^g@GMG9l2%BuIT$R`P7!X479_jRx_9xZ*4eScVehW43O zeYTW)OjtE701xspu8o_vKT$wmvenrGljZY%XVnwpoomW;lS^?s&V6IGjMTYzHd)L2 zL<;2?wd!6SHOuR+rZ{@=4g`Ywkr%)e-8;hGFmnUwZg4@mh~W=S6%9T20lVj8`UBWN z%Cm!LI3%3&wPzl5L2UXMI#qI&0IgDocXx3dcGYvMT}b4M@)O>xk`t;xBeIbod-la-xX0I;Qw`UJh>{d7vEqxwn zd-6}Tvx8mq8b#BLkCEWxMDg5M8u1bVSM3)~b_BY_n$*`^sG-LVw^-2Juou*xGxzi$ zDSF2&yx$hPtkUNz^?leI~e(gaqTY_lQQ z)K-L1RRYP?eEwIy0tGN}-P;GhUj6^coctIRkgH?!Hn}TK)HCUANFJgt_v_F=FUcswODPj6-W$N~+b$M&mFme3uj2v=(aM=!l>})vW4ib#4 zh&eEgAW_0bS407?PY%-g!GH#77uU8@`5O?D3_(O*lx0i`!(2Fb8?GNfmmXx*C4K*P zUN?4!^5EH-^?FPx4>1OGi3MGGxws7J`g%Xr&{&dlqt0u3=KU5bkI42yV*C~AU zX-gS)BucCUfGCka$0#wmhH~A;G>cgOh&2_uHdD#Uv!0(LIRTT9sJ0->uIG#A-s_08 zyG4d2TsjkN&XQ10+z3{VXvDqJNIyO8f)3hBm9$jq;;xwv4IRwm4Et9q+(Qb#gm)4O zgqH>pqL&){2g@BCxN@nhtFxhMu4v5vXE1`;KwNZQf*MPE1tG1VfWIT|goIn4^6EI! ziSLw)Yx221UlyISwpfHT9*rj*`+79B_+o}%sp~;-@A%IzB8Bj)eeetdRyjk1pHPdc6D%%NSNVikZg29_Z4rlEJKB}I%02u^u1P(|lMP5uZ$S0| z$DoR(q)NVv-xR;oiFk2YEr%2oJ{j>}rER4bc6&adc?_2eOd4*r-(WW3xKGZtNFnKO zSCM=BU9RzW_(_+6MI8GFNa5yIt}AB@Ef@nKFgVf}7*4C>%!FZRO0b@f6!=G3Q? zLmXGoG%SuhquC5T+s=lXNsmT9pC2w@S5+p9y-j`iPJinlrVB)_FwaQ^&c8ki;G1<& zUR!kcPaJhuNI|R)tMzdgkLGMyQ4THr@MB-tYGuHbMq-sC>CXFRL{5Xnjr~bsXv4}L zeutMoNAsbZ1HER~{-7VQCEf&ZFTZ;;7ubju77!UJSt!zwj_nqiFFb5VnG$SL0VTN! zX(rW0MCVl2gaNGc_Jgi^39_&>jyX5sj&}&z6XH$s-Q5mTf3R)E$lByvd>~=Vrb=Kh z>$r>4fc^QWmgcMUQvagjdbhUQ*vB*>l{Kx=uVYR%6cDMnwj|Ms!|m3OTaIZQMe^s= zG!y(rp2OYhtuZ?*gJDo2aQgQnlqzIvL{F^51sa)ZomGSUw?;-8$SiBfO_5Y3>xZg` zr?U2`b8Bv9zpe%;Q2dzdlfxh z#oV9LChU3br0mhLFL2vbs}zB*1{@@<2T&~ZVE`|@o`jJ7I|<+m)h(wkmnJJmRl*iu zL!793$OItz{RoqAsEXt-6~j2*&V(PfD9q(eKNkoF(nY^OF5c(SL1HP1y00fiT;~iQwfvpV+A4oP>zPREv@HUOPzPDcv3<5$>c^QPoaY6P?GiV%woNk zO6E&y1mm4L978?kjG9-J`_K^zaN9H~;<+$dFcDuTSF8ZJ_YXV{xfZzYxRY&sKVj_F z*ximYxYcc7+L!|=X*X8}{f^|>;1rW1&I$>lUM3>ZTWL!=6bHZL;GdEMVpIgLqw##y z=aCcF z4|}dW%F^P>X)nvitD`Rx0Aau-+_D8V$;@|V&UGOrwj1B4C12>hrT@Zm-wCHHLlO!>g^=1G%f7~t z0~6aeV%GEbdbQtBQCEEac0We6%w1$qfLSy}v%Ly{1Wt*p%9vy0=o+V%n!1A50P8oD+Rsz#<@IJ8n6Ov3rS zcC!Cw7cGzk>U6mx%%&jAmcn0bh+BiL1fT;hcHsO*hcVsLq>gK4vuedMGmvG0>fEyx zW|Q{s#B^g{P%ya3Uq#r3)QPQ^3RDRD`F$w2x3OUd-rP?Omp`m#tAAL{ft!Wg{3H$j zZHVC39fsqCTce@FR3pCRdbY#&9lII38M-rDyFpRC)WPM`PiN20E{rQ?%I$Nk2jUA-?w^5VTnGheIYi|RkM}e)wOm0&05!W-u@`=xIr0JdX{aJQ zR-0-Jra5F_lL}UTa|Jt6% zKrlmj>sJp1Ae(>WZG*N!LvkhK#?V(7P?va7rDz)_%XQXwXTkUcH`a!jQ$1wx4gDUK z7%YesG+cf?3*TfY_y1sk?Bm$9sv>(LZlEf)S+6}p+VVoK7>Rk;8Bw`$)RBD2Z=~Ub zC~EbSi0eY!%V21(M}lI+hL;_dB6Nn`@SMnh$}Zz*?TCN$SzCR0#$LCei+|2QbBrt% zw_SgQoEVi?zgOg&I;g8HY#4S)9DTVq6p9GDpU;{1rxyUiVJGe(1LwSA)I>N$+pZ+H zCn2ZEYi1wqY^UVs-w{_i+o$7Ci4G8#zXRv>G+puHp zwxjKSY7VMEAY0C4W)a?JUY8=Krz;iQ)cJWjIR5#A;Atyv^TH4slA2p(s0u>UiFqqK zalibh6THV6+s~)2tv~;$vM5&aG(#)vCb%~^9?#92g-eU)$<8ARFFS_I=FvvM#tqK0(L_5({QeT!>(W(Jp!%%h7Z;!cJbFY zg@x2=2(Me;+`O)V1Nj)!Ms&nH8m^bZmH!b&BP8iLkl~@ibo40#PngQe={xXZY0rFL zQk|h34`fwm25L|K`2;=4Dv$Zz0Q8sDu1Bh~mlug`kTfcsAR�UEa*d*@1!EKNsii_}MDwC2zg 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 RZR`L5002ovPDHLkV1mRB23P<9 diff --git a/pipelines/README.md b/pipelines/README.md index 1453995d..ec328036 100644 --- a/pipelines/README.md +++ b/pipelines/README.md @@ -13,106 +13,103 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> - # Pipelines +# ML Pipelines -## Introduction +There are two ML pipelines defined in this repository: a training pipeline (located in [pipelines/src/pipelines/training/pipeline.py](/pipelines/src/pipelines/training/pipeline.py)) and a batch prediction pipeline (located in [pipelines/src/pipelines/prediction/pipeline.py](/pipelines/src/pipelines/prediction/pipeline.py)). -This repository provides templates and reference implementations of [Vertex AI Pipelines](https://cloud.google.com/vertex-ai/docs/pipelines/) for production-grade training and batch prediction pipelines on GCP for: -- [TensorFlow](https://www.tensorflow.org/api_docs) -- [XGBoost](https://xgboost.readthedocs.io/en/stable/) (using the [Scikit-Learn wrapper interface for XGBoost](https://xgboost.readthedocs.io/en/latest/python/python_api.html#module-xgboost.sklearn)) +## Training pipeline -Further useful documentation: -- [KubeFlow Pipelines SDK](https://www.kubeflow.org/docs/components/pipelines/sdk/sdk-overview/) -- [Google Cloud Pipeline Components - Vertex Training Wrapper](https://github.com/kubeflow/pipelines/blob/master/components/google-cloud/google_cloud_pipeline_components/experimental/custom_job/utils.py) -- [Vertex AI](https://cloud.google.com/vertex-ai) +A screenshot of the completed ML pipeline is shown below. -The sections below provide a general description of the ML pipelines (training and prediction) for both the TensorFlow template and XGBoost template. These two templates are similar in most ways and a complete overview of their key differences are given in their own README files: -- [TensorFlow pipelines README](src/pipelines/tensorflow/README.md) -- [XGBoost pipelines README](src/pipelines/xgboost/README.md) +![Screenshot of the training pipeline in Vertex Pipelines](/docs/images/training_pipeline.png) -## Training Pipeline -### Prerequisites for training pipeline +In the next sections we will walk through the different pipeline steps. -Optional: an existing champion model +### Ingestion / preprocessing step -### Components in training pipeline +The first pipeline step runs a SQL script in BigQuery to extract data from the source table and load it into tables according to a train/test/validation split. -The training pipeline can be broken down into the following sequence of components at a high level: +The SQL query for this can be found in [pipelines/src/pipelines/training/queries/preprocessing.sql](/pipelines/src/pipelines/training/queries/preprocessing.sql). -| Step No | Step Name | Description | Input(s) | Output(s) | -|:-------:| :----------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | -| 1 | Generate Queries | Generate base preprocessing & train-test-validation split queries for Google BigQuery. This component only needs a `.sql` file and all parametrized values in that `.sql` file (`source_dataset`, `source_table`, `filter_column`, and so on) | | | -| 2 | BQ Query to Table | This component takes the generated query from the previous component & runs it on Google BigQuery to create the required table (preprocessed data/ train-test-validation data). | Output from **Generate Queries** | New Google BigQuery table created | -| 3 | Extract BQ to Dataset | Creates CSV file/s in Google Cloud Storage from the Google BigQuery tables created in the previous component | BigQuery table created from **BQ Query to Table** | BigQuery table converted to Google Cloud Storage objects as CSV/JSONL files and corresponding file directory and path | -| 4 | Vertex Training | Run a Vertex Training job with train-validation data using a training component wrapped in a ContainerOp from `google-cloud-pipeline-components` |

| Trained challenger model object/s or binaries stored in Google Cloud Storage | -| 5 | Challenger Model Predictions | Use the trained model to get challenger predictions for evaluation purposes |
  • *Test data* - Google Cloud Storage CSV files for `test_data` from **Extract BQ to Dataset**
  • *Trained Model* - Trained challenger model binaries stored in Google Cloud Storage from **Vertex Training**
| Challenger predictions on test data stored as CSV files in Google Cloud Storage | -| 6 | Calculate Evaluation Metrics | Use predictions from previous component to compute user-defined evaluation metrics. This component uses TFMA. |

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 z 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 b4{b5B2XsKKn9uMrfFI2(S5>nwTlxPF=Bl$L literal 0 HcmV?d00001 diff --git a/docs/images/tf_model_architecture.png b/docs/images/tf_model_architecture.png deleted file mode 100644 index 84cba2213d4b49b2a2bb3b1e5f81e93c30b2b1b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 248682 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%0AH6a!fy5{EAHSWNmJlE`m$7MkhbiyGQ{CeS!N-H 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%0Dyj_Z}Q=+RN#HyBy(-1yxxb|EnWBYB9JR+Wb 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