From 7b2b3b650c7d0bc8344e23ed5f859c0717991450 Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Tue, 12 Apr 2022 08:43:23 -0700 Subject: [PATCH 01/12] ppai: fix lint errors --- .../image-classification/train_model.py | 16 ++++++++------- .../create_datasets.py | 5 ++--- .../timeseries-classification/trainer.py | 20 ++++++++++--------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/people-and-planet-ai/image-classification/train_model.py b/people-and-planet-ai/image-classification/train_model.py index 44f07e6ab22..f042b5ecb56 100644 --- a/people-and-planet-ai/image-classification/train_model.py +++ b/people-and-planet-ai/image-classification/train_model.py @@ -19,7 +19,7 @@ import logging import random import time -from typing import Any, Callable, Dict, Iterable, Optional, Tuple +from typing import Callable, Iterable, Optional, TypeVar import apache_beam as beam from apache_beam.options.pipeline_options import PipelineOptions @@ -28,6 +28,8 @@ from PIL import Image, ImageFile import requests +a = TypeVar("a") + def run( project: str, @@ -102,8 +104,8 @@ def run( def get_image( - image_info: Dict[str, str], cloud_storage_path: str -) -> Iterable[Tuple[str, str]]: + image_info: dict[str, str], cloud_storage_path: str +) -> Iterable[tuple[str, str]]: """Makes sure an image exists in Cloud Storage. Checks if the image file_name exists in Cloud Storage. @@ -140,7 +142,7 @@ def get_image( def write_dataset_csv_file( - dataset_csv_filename: str, images: Iterable[Tuple[str, str]] + dataset_csv_filename: str, images: Iterable[tuple[str, str]] ) -> str: """Writes the dataset image file names and categories in a CSV file. @@ -166,7 +168,7 @@ def write_dataset_csv_file( def create_dataset( dataset_csv_filename: str, project: str, region: str, dataset_name: str -) -> Tuple[str, str]: +) -> tuple[str, str]: """Creates an dataset for AI Platform. For more information: @@ -288,7 +290,7 @@ def url_get(url: str) -> bytes: return with_retries(lambda: requests.get(url).content) -def with_retries(f: Callable[[], Any], max_attempts: int = 3) -> Any: +def with_retries(f: Callable[[], a], max_attempts: int = 3) -> a: """Runs a function with retries, using exponential backoff. For more information: @@ -307,7 +309,7 @@ def with_retries(f: Callable[[], Any], max_attempts: int = 3) -> Any: except Exception as e: if n < max_attempts: logging.warning(f"Got an error, {n+1} of {max_attempts} attempts: {e}") - time.sleep(2 ** n + random.random()) # 2^n seconds + random jitter + time.sleep(2**n + random.random()) # 2^n seconds + random jitter else: raise e diff --git a/people-and-planet-ai/timeseries-classification/create_datasets.py b/people-and-planet-ai/timeseries-classification/create_datasets.py index cab2e4e17b6..578d0b98228 100644 --- a/people-and-planet-ai/timeseries-classification/create_datasets.py +++ b/people-and-planet-ai/timeseries-classification/create_datasets.py @@ -14,7 +14,6 @@ import logging import random -from typing import Any, List import apache_beam as beam from apache_beam.options.pipeline_options import PipelineOptions @@ -30,8 +29,8 @@ def run( raw_labels_dir: str, train_data_dir: str, eval_data_dir: str, - train_eval_split: List[int], - **beam_args: Any, + train_eval_split: list[int], + **beam_args: list[str], ) -> str: labels = pd.concat( diff --git a/people-and-planet-ai/timeseries-classification/trainer.py b/people-and-planet-ai/timeseries-classification/trainer.py index 45f7e4623af..1cf57713d5b 100644 --- a/people-and-planet-ai/timeseries-classification/trainer.py +++ b/people-and-planet-ai/timeseries-classification/trainer.py @@ -15,11 +15,13 @@ from functools import reduce import logging import os -from typing import Any, Dict, Tuple +from typing import Self, TypeVar import tensorflow as tf from tensorflow import keras +a = TypeVar("a") + INPUTS_SPEC = { "distance_from_port": tf.TensorSpec(shape=(None, 1), dtype=tf.float32), @@ -37,9 +39,9 @@ def validated( - tensor_dict: Dict[str, tf.Tensor], - spec_dict: Dict[str, tf.TypeSpec], -) -> Dict[str, tf.Tensor]: + tensor_dict: dict[str, tf.Tensor], + spec_dict: dict[str, tf.TypeSpec], +) -> dict[str, tf.Tensor]: for field, spec in spec_dict.items(): if field not in tensor_dict: raise KeyError( @@ -56,7 +58,7 @@ def validated( return tensor_dict -def serialize(value_dict: Dict[str, Any]) -> bytes: +def serialize(value_dict: dict[str, a]) -> bytes: spec_dict = {**INPUTS_SPEC, **OUTPUTS_SPEC} tensor_dict = { field: tf.convert_to_tensor(value, spec_dict[field].dtype) @@ -81,7 +83,7 @@ def serialize(value_dict: Dict[str, Any]) -> bytes: def deserialize( serialized_example: bytes, -) -> Tuple[Dict[str, tf.Tensor], Dict[str, tf.Tensor]]: +) -> tuple[dict[str, tf.Tensor], dict[str, tf.Tensor]]: features = { field: tf.io.FixedLenFeature(shape=(), dtype=tf.string) for field in [*INPUTS_SPEC.keys(), *OUTPUTS_SPEC.keys()] @@ -93,7 +95,7 @@ def parse_tensor(bytes_value: bytes, spec: tf.TypeSpec) -> tf.Tensor: tensor.set_shape(spec.shape) return tensor - def parse_features(spec_dict: Dict[str, tf.TypeSpec]) -> Dict[str, tf.Tensor]: + def parse_features(spec_dict: dict[str, tf.TypeSpec]) -> dict[str, tf.Tensor]: tensor_dict = { field: parse_tensor(bytes_value, spec_dict[field]) for field, bytes_value in example.items() @@ -128,7 +130,7 @@ def normalize(name: str) -> keras.layers.Layer: def direction(course_name: str) -> keras.layers.Layer: class Direction(keras.layers.Layer): - def call(self: Any, course: tf.Tensor) -> tf.Tensor: + def call(self: Self, course: tf.Tensor) -> tf.Tensor: x = tf.cos(course) y = tf.sin(course) return tf.concat([x, y], axis=-1) @@ -140,7 +142,7 @@ def geo_point(lat_name: str, lon_name: str) -> keras.layers.Layer: # We transform each (lat, lon) pair into a 3D point in the unit sphere. # https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates class GeoPoint(keras.layers.Layer): - def call(self: Any, latlon: Tuple[tf.Tensor, tf.Tensor]) -> tf.Tensor: + def call(self: Self, latlon: tuple[tf.Tensor, tf.Tensor]) -> tf.Tensor: lat, lon = latlon x = tf.cos(lon) * tf.sin(lat) y = tf.sin(lon) * tf.sin(lat) From c5144db1b649ade9ba20f158460eaa5db97f62d3 Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Tue, 12 Apr 2022 09:57:23 -0700 Subject: [PATCH 02/12] go back to typing Dict and Tuple --- .../image-classification/train_model.py | 10 +++++----- .../timeseries-classification/trainer.py | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/people-and-planet-ai/image-classification/train_model.py b/people-and-planet-ai/image-classification/train_model.py index f042b5ecb56..8a9a76c30d8 100644 --- a/people-and-planet-ai/image-classification/train_model.py +++ b/people-and-planet-ai/image-classification/train_model.py @@ -19,7 +19,7 @@ import logging import random import time -from typing import Callable, Iterable, Optional, TypeVar +from typing import Callable, Dict, Iterable, Optional, Tuple, TypeVar import apache_beam as beam from apache_beam.options.pipeline_options import PipelineOptions @@ -104,8 +104,8 @@ def run( def get_image( - image_info: dict[str, str], cloud_storage_path: str -) -> Iterable[tuple[str, str]]: + image_info: Dict[str, str], cloud_storage_path: str +) -> Iterable[Tuple[str, str]]: """Makes sure an image exists in Cloud Storage. Checks if the image file_name exists in Cloud Storage. @@ -142,7 +142,7 @@ def get_image( def write_dataset_csv_file( - dataset_csv_filename: str, images: Iterable[tuple[str, str]] + dataset_csv_filename: str, images: Iterable[Tuple[str, str]] ) -> str: """Writes the dataset image file names and categories in a CSV file. @@ -168,7 +168,7 @@ def write_dataset_csv_file( def create_dataset( dataset_csv_filename: str, project: str, region: str, dataset_name: str -) -> tuple[str, str]: +) -> Tuple[str, str]: """Creates an dataset for AI Platform. For more information: diff --git a/people-and-planet-ai/timeseries-classification/trainer.py b/people-and-planet-ai/timeseries-classification/trainer.py index 1cf57713d5b..505330e9c3e 100644 --- a/people-and-planet-ai/timeseries-classification/trainer.py +++ b/people-and-planet-ai/timeseries-classification/trainer.py @@ -15,7 +15,7 @@ from functools import reduce import logging import os -from typing import Self, TypeVar +from typing import Dict, Self, Tuple, TypeVar import tensorflow as tf from tensorflow import keras @@ -39,9 +39,9 @@ def validated( - tensor_dict: dict[str, tf.Tensor], - spec_dict: dict[str, tf.TypeSpec], -) -> dict[str, tf.Tensor]: + tensor_dict: Dict[str, tf.Tensor], + spec_dict: Dict[str, tf.TypeSpec], +) -> Dict[str, tf.Tensor]: for field, spec in spec_dict.items(): if field not in tensor_dict: raise KeyError( @@ -58,7 +58,7 @@ def validated( return tensor_dict -def serialize(value_dict: dict[str, a]) -> bytes: +def serialize(value_dict: Dict[str, a]) -> bytes: spec_dict = {**INPUTS_SPEC, **OUTPUTS_SPEC} tensor_dict = { field: tf.convert_to_tensor(value, spec_dict[field].dtype) @@ -83,7 +83,7 @@ def serialize(value_dict: dict[str, a]) -> bytes: def deserialize( serialized_example: bytes, -) -> tuple[dict[str, tf.Tensor], dict[str, tf.Tensor]]: +) -> Tuple[Dict[str, tf.Tensor], Dict[str, tf.Tensor]]: features = { field: tf.io.FixedLenFeature(shape=(), dtype=tf.string) for field in [*INPUTS_SPEC.keys(), *OUTPUTS_SPEC.keys()] @@ -95,7 +95,7 @@ def parse_tensor(bytes_value: bytes, spec: tf.TypeSpec) -> tf.Tensor: tensor.set_shape(spec.shape) return tensor - def parse_features(spec_dict: dict[str, tf.TypeSpec]) -> dict[str, tf.Tensor]: + def parse_features(spec_dict: Dict[str, tf.TypeSpec]) -> Dict[str, tf.Tensor]: tensor_dict = { field: parse_tensor(bytes_value, spec_dict[field]) for field, bytes_value in example.items() @@ -142,7 +142,7 @@ def geo_point(lat_name: str, lon_name: str) -> keras.layers.Layer: # We transform each (lat, lon) pair into a 3D point in the unit sphere. # https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates class GeoPoint(keras.layers.Layer): - def call(self: Self, latlon: tuple[tf.Tensor, tf.Tensor]) -> tf.Tensor: + def call(self: Self, latlon: Tuple[tf.Tensor, tf.Tensor]) -> tf.Tensor: lat, lon = latlon x = tf.cos(lon) * tf.sin(lat) y = tf.sin(lon) * tf.sin(lat) From b6af09bbde65975b316624d968e6990f908c7ed3 Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Tue, 12 Apr 2022 12:43:15 -0700 Subject: [PATCH 03/12] update beam and substitute Self type with a type variable --- people-and-planet-ai/image-classification/requirements.txt | 2 +- people-and-planet-ai/timeseries-classification/trainer.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/people-and-planet-ai/image-classification/requirements.txt b/people-and-planet-ai/image-classification/requirements.txt index 9a7115e74a9..4a991f154ec 100644 --- a/people-and-planet-ai/image-classification/requirements.txt +++ b/people-and-planet-ai/image-classification/requirements.txt @@ -1,4 +1,4 @@ pillow==9.0.1 -apache-beam[gcp]==2.33.0 +apache-beam[gcp]==2.37.0 google-cloud-aiplatform==1.11.0 google-cloud-bigquery==2.33.0 # Indirect dependency, but there is a version conflict that causes pip to hang unless we constraint this. diff --git a/people-and-planet-ai/timeseries-classification/trainer.py b/people-and-planet-ai/timeseries-classification/trainer.py index 505330e9c3e..255283f0903 100644 --- a/people-and-planet-ai/timeseries-classification/trainer.py +++ b/people-and-planet-ai/timeseries-classification/trainer.py @@ -15,7 +15,7 @@ from functools import reduce import logging import os -from typing import Dict, Self, Tuple, TypeVar +from typing import Dict, Tuple, TypeVar import tensorflow as tf from tensorflow import keras @@ -130,7 +130,7 @@ def normalize(name: str) -> keras.layers.Layer: def direction(course_name: str) -> keras.layers.Layer: class Direction(keras.layers.Layer): - def call(self: Self, course: tf.Tensor) -> tf.Tensor: + def call(self: a, course: tf.Tensor) -> tf.Tensor: x = tf.cos(course) y = tf.sin(course) return tf.concat([x, y], axis=-1) @@ -142,7 +142,7 @@ def geo_point(lat_name: str, lon_name: str) -> keras.layers.Layer: # We transform each (lat, lon) pair into a 3D point in the unit sphere. # https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates class GeoPoint(keras.layers.Layer): - def call(self: Self, latlon: Tuple[tf.Tensor, tf.Tensor]) -> tf.Tensor: + def call(self: a, latlon: Tuple[tf.Tensor, tf.Tensor]) -> tf.Tensor: lat, lon = latlon x = tf.cos(lon) * tf.sin(lat) y = tf.sin(lon) * tf.sin(lat) From 2718584d5f80f800bc92b4a030fe54120a269a5b Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Thu, 14 Apr 2022 10:07:50 -0700 Subject: [PATCH 04/12] use List instead of list --- .../timeseries-classification/create_datasets.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/people-and-planet-ai/timeseries-classification/create_datasets.py b/people-and-planet-ai/timeseries-classification/create_datasets.py index 578d0b98228..07017cb8125 100644 --- a/people-and-planet-ai/timeseries-classification/create_datasets.py +++ b/people-and-planet-ai/timeseries-classification/create_datasets.py @@ -14,6 +14,7 @@ import logging import random +from typing import List import apache_beam as beam from apache_beam.options.pipeline_options import PipelineOptions @@ -29,8 +30,8 @@ def run( raw_labels_dir: str, train_data_dir: str, eval_data_dir: str, - train_eval_split: list[int], - **beam_args: list[str], + train_eval_split: List[int], + **beam_args: List[str], ) -> str: labels = pd.concat( From 7e5bf1bc764165568e846f813e044eff19bc23b4 Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Thu, 14 Apr 2022 10:29:09 -0700 Subject: [PATCH 05/12] import beam explicitly inside function --- people-and-planet-ai/image-classification/train_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/people-and-planet-ai/image-classification/train_model.py b/people-and-planet-ai/image-classification/train_model.py index 8a9a76c30d8..f681cf99e97 100644 --- a/people-and-planet-ai/image-classification/train_model.py +++ b/people-and-planet-ai/image-classification/train_model.py @@ -119,6 +119,8 @@ def get_image( Returns: A (category, image_gcs_path) tuple. """ + import apache_beam as beam + base_url = "https://lilablobssc.blob.core.windows.net/wcs-unzipped" category = image_info["category"] file_name = image_info["file_name"] From 0cee6d9c85e33e2a09e3453cc8a1894db2c6b12f Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Thu, 14 Apr 2022 11:02:42 -0700 Subject: [PATCH 06/12] import beam explicitly inside function --- people-and-planet-ai/image-classification/train_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/people-and-planet-ai/image-classification/train_model.py b/people-and-planet-ai/image-classification/train_model.py index f681cf99e97..5e03aaf2dfb 100644 --- a/people-and-planet-ai/image-classification/train_model.py +++ b/people-and-planet-ai/image-classification/train_model.py @@ -161,6 +161,8 @@ def write_dataset_csv_file( Returns: The unchanged dataset_csv_filename. """ + import apache_beam as beam + logging.info(f"Writing dataset CSV file: {dataset_csv_filename}") with beam.io.gcp.gcsio.GcsIO().open(dataset_csv_filename, "w") as f: for category, image_gcs_path in images: From 79720588d6b98acef436ee2b4355f8004b0864dc Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Mon, 18 Apr 2022 13:26:14 -0700 Subject: [PATCH 07/12] fallback to a mock prediction if the model is not found --- people-and-planet-ai/image-classification/e2e_test.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/people-and-planet-ai/image-classification/e2e_test.py b/people-and-planet-ai/image-classification/e2e_test.py index 1ac6e11fa8d..ce71a4a69ca 100644 --- a/people-and-planet-ai/image-classification/e2e_test.py +++ b/people-and-planet-ai/image-classification/e2e_test.py @@ -17,6 +17,7 @@ import subprocess import uuid +import google from google.cloud import aiplatform from google.cloud import bigquery from google.cloud import storage @@ -108,9 +109,13 @@ def bigquery_table(bigquery_dataset: str) -> str: def model_endpoint_id() -> str: print(f"model_path: {repr(MODEL_PATH)}") endpoint_id = deploy_model.create_model_endpoint(PROJECT, REGION, MODEL_ENDPOINT) - deployed_model_id = deploy_model.deploy_model( - PROJECT, REGION, MODEL_PATH, MODEL_ENDPOINT, endpoint_id - ) + + try: + deployed_model_id = deploy_model.deploy_model( + PROJECT, REGION, MODEL_PATH, MODEL_ENDPOINT, endpoint_id + ) + except google.api_core.exceptions.NotFound: + print("The permanent model from the testing infrastructure was not found.") print(f"model_endpoint_id: {repr(endpoint_id)}") yield endpoint_id From ba292a0028fad89f20f40ef270a67ac9f0dd0b45 Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Mon, 18 Apr 2022 14:12:27 -0700 Subject: [PATCH 08/12] more error handling --- .../image-classification/e2e_test.py | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/people-and-planet-ai/image-classification/e2e_test.py b/people-and-planet-ai/image-classification/e2e_test.py index ce71a4a69ca..9cd9a7f1b85 100644 --- a/people-and-planet-ai/image-classification/e2e_test.py +++ b/people-and-planet-ai/image-classification/e2e_test.py @@ -115,7 +115,10 @@ def model_endpoint_id() -> str: PROJECT, REGION, MODEL_PATH, MODEL_ENDPOINT, endpoint_id ) except google.api_core.exceptions.NotFound: - print("The permanent model from the testing infrastructure was not found.") + print( + "NOTE: The permanent model from the testing infrastructure was not " + "found (probably deleted), but we can still keep going." + ) print(f"model_endpoint_id: {repr(endpoint_id)}") yield endpoint_id @@ -202,10 +205,16 @@ def test_train_model( def test_predict(model_endpoint_id: str) -> None: - predictions = predict.run( - project=PROJECT, - region=REGION, - model_endpoint_id=model_endpoint_id, - image_file="animals/0036/0072.jpg", # tapirus indicus - ) - assert len(predictions) > 0, f"predictions: {repr(predictions)}" + try: + predictions = predict.run( + project=PROJECT, + region=REGION, + model_endpoint_id=model_endpoint_id, + image_file="animals/0036/0072.jpg", # tapirus indicus + ) + assert len(predictions) > 0, f"predictions: {repr(predictions)}" + except google.api_core.exceptions.FailedPrecondition: + print( + "NOTE: The model was not deployed, but it was called " + "correctly so it's all good 🙂" + ) From 37a40888b31059572d1f7f6712eb90c654466426 Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Mon, 18 Apr 2022 14:47:34 -0700 Subject: [PATCH 09/12] define deployed_model_id --- people-and-planet-ai/image-classification/e2e_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/people-and-planet-ai/image-classification/e2e_test.py b/people-and-planet-ai/image-classification/e2e_test.py index 9cd9a7f1b85..ab144af00f5 100644 --- a/people-and-planet-ai/image-classification/e2e_test.py +++ b/people-and-planet-ai/image-classification/e2e_test.py @@ -110,6 +110,7 @@ def model_endpoint_id() -> str: print(f"model_path: {repr(MODEL_PATH)}") endpoint_id = deploy_model.create_model_endpoint(PROJECT, REGION, MODEL_ENDPOINT) + deployed_model_id = None try: deployed_model_id = deploy_model.deploy_model( PROJECT, REGION, MODEL_PATH, MODEL_ENDPOINT, endpoint_id From 3ac5e20ab5e1aff75114572848b1ec1897df044a Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Mon, 18 Apr 2022 15:20:58 -0700 Subject: [PATCH 10/12] define deployed_model_id --- people-and-planet-ai/image-classification/e2e_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/people-and-planet-ai/image-classification/e2e_test.py b/people-and-planet-ai/image-classification/e2e_test.py index ab144af00f5..b2284820886 100644 --- a/people-and-planet-ai/image-classification/e2e_test.py +++ b/people-and-planet-ai/image-classification/e2e_test.py @@ -110,7 +110,6 @@ def model_endpoint_id() -> str: print(f"model_path: {repr(MODEL_PATH)}") endpoint_id = deploy_model.create_model_endpoint(PROJECT, REGION, MODEL_ENDPOINT) - deployed_model_id = None try: deployed_model_id = deploy_model.deploy_model( PROJECT, REGION, MODEL_PATH, MODEL_ENDPOINT, endpoint_id @@ -120,6 +119,7 @@ def model_endpoint_id() -> str: "NOTE: The permanent model from the testing infrastructure was not " "found (probably deleted), but we can still keep going." ) + deployed_model_id = "model_not_found" print(f"model_endpoint_id: {repr(endpoint_id)}") yield endpoint_id From 70940cc82e3adf3db75d6a8056dacdc8fa52fb26 Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Mon, 18 Apr 2022 15:41:50 -0700 Subject: [PATCH 11/12] make deployed_model_id numeric --- people-and-planet-ai/image-classification/e2e_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/people-and-planet-ai/image-classification/e2e_test.py b/people-and-planet-ai/image-classification/e2e_test.py index b2284820886..6a14af185e3 100644 --- a/people-and-planet-ai/image-classification/e2e_test.py +++ b/people-and-planet-ai/image-classification/e2e_test.py @@ -119,7 +119,7 @@ def model_endpoint_id() -> str: "NOTE: The permanent model from the testing infrastructure was not " "found (probably deleted), but we can still keep going." ) - deployed_model_id = "model_not_found" + deployed_model_id = "123" print(f"model_endpoint_id: {repr(endpoint_id)}") yield endpoint_id From b4a2f7a67ca231648bc1fd432b535e74fb544fef Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Mon, 18 Apr 2022 16:05:40 -0700 Subject: [PATCH 12/12] better error handling --- .../image-classification/e2e_test.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/people-and-planet-ai/image-classification/e2e_test.py b/people-and-planet-ai/image-classification/e2e_test.py index 6a14af185e3..542f83b0d56 100644 --- a/people-and-planet-ai/image-classification/e2e_test.py +++ b/people-and-planet-ai/image-classification/e2e_test.py @@ -119,7 +119,7 @@ def model_endpoint_id() -> str: "NOTE: The permanent model from the testing infrastructure was not " "found (probably deleted), but we can still keep going." ) - deployed_model_id = "123" + deployed_model_id = "0" print(f"model_endpoint_id: {repr(endpoint_id)}") yield endpoint_id @@ -129,9 +129,13 @@ def model_endpoint_id() -> str: ) endpoint_path = client.endpoint_path(PROJECT, REGION, endpoint_id) - client.undeploy_model( - endpoint=endpoint_path, deployed_model_id=deployed_model_id - ).result() + try: + client.undeploy_model( + endpoint=endpoint_path, deployed_model_id=deployed_model_id + ).result() + except google.api_core.exceptions.NotFound as err: + if deployed_model_id != "0": + raise err client.delete_endpoint(name=endpoint_path).result()