diff --git a/.flake8 b/.flake8 index 29227d4..2e43874 100644 --- a/.flake8 +++ b/.flake8 @@ -16,7 +16,7 @@ # Generated by synthtool. DO NOT EDIT! [flake8] -ignore = E203, E266, E501, W503 +ignore = E203, E231, E266, E501, W503 exclude = # Exclude generated code. **/proto/** diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 4837584..64f82d6 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -4,14 +4,14 @@ # 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 +# 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. - docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:f848855a87932612125b7b7be24ae0a58c41ce32cba8239e0748edd50a64f71e + digest: sha256:bc5eed3804aec2f05fad42aacf973821d9500c174015341f721a984a0825b6fd +# created: 2022-04-21T15:43:16.246106921Z diff --git a/.github/auto-label.yaml b/.github/auto-label.yaml new file mode 100644 index 0000000..41bff0b --- /dev/null +++ b/.github/auto-label.yaml @@ -0,0 +1,15 @@ +# 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. +requestsize: + enabled: true diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile index 4e1b1fb..238b87b 100644 --- a/.kokoro/docker/docs/Dockerfile +++ b/.kokoro/docker/docs/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ubuntu:20.04 +from ubuntu:22.04 ENV DEBIAN_FRONTEND noninteractive @@ -60,8 +60,24 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* \ && rm -f /var/cache/apt/archives/*.deb +###################### Install python 3.8.11 + +# Download python 3.8.11 +RUN wget https://www.python.org/ftp/python/3.8.11/Python-3.8.11.tgz + +# Extract files +RUN tar -xvf Python-3.8.11.tgz + +# Install python 3.8.11 +RUN ./Python-3.8.11/configure --enable-optimizations +RUN make altinstall + +###################### Install pip RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \ - && python3.8 /tmp/get-pip.py \ + && python3 /tmp/get-pip.py \ && rm /tmp/get-pip.py +# Test pip +RUN python3 -m pip + CMD ["python3.8"] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 62eb5a7..46d2371 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - id: end-of-file-fixer - id: check-yaml - repo: https://github.com/psf/black - rev: 19.10b0 + rev: 22.3.0 hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 diff --git a/CHANGELOG.md b/CHANGELOG.md index 44b978d..e034756 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +### [0.1.1](https://github.com/googleapis/python-optimization/compare/v0.1.0...v0.1.1) (2022-04-25) + + +### Documentation + +* add code snippets for async api ([#18](https://github.com/googleapis/python-optimization/issues/18)) ([60b35a0](https://github.com/googleapis/python-optimization/commit/60b35a01b12d2c0034aebe1edcc25487bd0fe567)) +* add get_operation code snippets ([#12](https://github.com/googleapis/python-optimization/issues/12)) ([a95c19f](https://github.com/googleapis/python-optimization/commit/a95c19fef17c86f587febcf054a7f1fa49851cdf)) +* add long timeout code snippet ([#20](https://github.com/googleapis/python-optimization/issues/20)) ([9f57450](https://github.com/googleapis/python-optimization/commit/9f574507010ef637e5a6912a1cb725b782c03cf4)) +* add sync api samples with json request ([#13](https://github.com/googleapis/python-optimization/issues/13)) ([c3d6087](https://github.com/googleapis/python-optimization/commit/c3d60874977628698c7f3d0b137c120971e7c42c)) +* update operation id ([#23](https://github.com/googleapis/python-optimization/issues/23)) ([d9c6c42](https://github.com/googleapis/python-optimization/commit/d9c6c422d6146f65f11fa98370f9b7f7edd166ad)) + ## 0.1.0 (2022-03-24) diff --git a/docs/conf.py b/docs/conf.py index d2148ba..e8e7c6d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -361,7 +361,10 @@ intersphinx_mapping = { "python": ("https://python.readthedocs.org/en/latest/", None), "google-auth": ("https://googleapis.dev/python/google-auth/latest/", None), - "google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None,), + "google.api_core": ( + "https://googleapis.dev/python/google-api-core/latest/", + None, + ), "grpc": ("https://grpc.github.io/grpc/python/", None), "proto-plus": ("https://proto-plus-python.readthedocs.io/en/latest/", None), "protobuf": ("https://googleapis.dev/python/protobuf/latest/", None), diff --git a/google/cloud/optimization_v1/services/fleet_routing/async_client.py b/google/cloud/optimization_v1/services/fleet_routing/async_client.py index e6b71f3..9733d84 100644 --- a/google/cloud/optimization_v1/services/fleet_routing/async_client.py +++ b/google/cloud/optimization_v1/services/fleet_routing/async_client.py @@ -16,7 +16,7 @@ from collections import OrderedDict import functools import re -from typing import Dict, Optional, Sequence, Tuple, Type, Union +from typing import Dict, Mapping, Optional, Sequence, Tuple, Type, Union import pkg_resources from google.api_core.client_options import ClientOptions @@ -246,7 +246,6 @@ async def optimize_tours( ``Vehicle``\ s that minimizes the total cost where cost has many components defined in the ``ShipmentModel``. - .. code-block:: python from google.cloud import optimization_v1 @@ -313,7 +312,12 @@ def sample_optimize_tours(): ) # Send the request. - response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + response = await rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) # Done; return the response. return response @@ -338,7 +342,6 @@ async def batch_optimize_tours( containing ``ShipmentRoute``\ s, which are a set of routes to be performed by vehicles minimizing the overall cost. - .. code-block:: python from google.cloud import optimization_v1 @@ -417,7 +420,12 @@ def sample_batch_optimize_tours(): ) # Send the request. - response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + response = await rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) # Wrap the response in an operation future. response = operation_async.from_gapic( diff --git a/google/cloud/optimization_v1/services/fleet_routing/client.py b/google/cloud/optimization_v1/services/fleet_routing/client.py index cbd9648..689520c 100644 --- a/google/cloud/optimization_v1/services/fleet_routing/client.py +++ b/google/cloud/optimization_v1/services/fleet_routing/client.py @@ -16,7 +16,7 @@ from collections import OrderedDict import os import re -from typing import Dict, Optional, Sequence, Tuple, Type, Union +from typing import Dict, Mapping, Optional, Sequence, Tuple, Type, Union import pkg_resources from google.api_core import client_options as client_options_lib @@ -55,7 +55,10 @@ class FleetRoutingClientMeta(type): _transport_registry["grpc"] = FleetRoutingGrpcTransport _transport_registry["grpc_asyncio"] = FleetRoutingGrpcAsyncIOTransport - def get_transport_class(cls, label: str = None,) -> Type[FleetRoutingTransport]: + def get_transport_class( + cls, + label: str = None, + ) -> Type[FleetRoutingTransport]: """Returns an appropriate transport class. Args: @@ -183,7 +186,9 @@ def transport(self) -> FleetRoutingTransport: return self._transport @staticmethod - def common_billing_account_path(billing_account: str,) -> str: + def common_billing_account_path( + billing_account: str, + ) -> str: """Returns a fully-qualified billing_account string.""" return "billingAccounts/{billing_account}".format( billing_account=billing_account, @@ -196,9 +201,13 @@ def parse_common_billing_account_path(path: str) -> Dict[str, str]: return m.groupdict() if m else {} @staticmethod - def common_folder_path(folder: str,) -> str: + def common_folder_path( + folder: str, + ) -> str: """Returns a fully-qualified folder string.""" - return "folders/{folder}".format(folder=folder,) + return "folders/{folder}".format( + folder=folder, + ) @staticmethod def parse_common_folder_path(path: str) -> Dict[str, str]: @@ -207,9 +216,13 @@ def parse_common_folder_path(path: str) -> Dict[str, str]: return m.groupdict() if m else {} @staticmethod - def common_organization_path(organization: str,) -> str: + def common_organization_path( + organization: str, + ) -> str: """Returns a fully-qualified organization string.""" - return "organizations/{organization}".format(organization=organization,) + return "organizations/{organization}".format( + organization=organization, + ) @staticmethod def parse_common_organization_path(path: str) -> Dict[str, str]: @@ -218,9 +231,13 @@ def parse_common_organization_path(path: str) -> Dict[str, str]: return m.groupdict() if m else {} @staticmethod - def common_project_path(project: str,) -> str: + def common_project_path( + project: str, + ) -> str: """Returns a fully-qualified project string.""" - return "projects/{project}".format(project=project,) + return "projects/{project}".format( + project=project, + ) @staticmethod def parse_common_project_path(path: str) -> Dict[str, str]: @@ -229,10 +246,14 @@ def parse_common_project_path(path: str) -> Dict[str, str]: return m.groupdict() if m else {} @staticmethod - def common_location_path(project: str, location: str,) -> str: + def common_location_path( + project: str, + location: str, + ) -> str: """Returns a fully-qualified location string.""" return "projects/{project}/locations/{location}".format( - project=project, location=location, + project=project, + location=location, ) @staticmethod @@ -431,7 +452,6 @@ def optimize_tours( ``Vehicle``\ s that minimizes the total cost where cost has many components defined in the ``ShipmentModel``. - .. code-block:: python from google.cloud import optimization_v1 @@ -490,7 +510,12 @@ def sample_optimize_tours(): ) # Send the request. - response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) # Done; return the response. return response @@ -515,7 +540,6 @@ def batch_optimize_tours( containing ``ShipmentRoute``\ s, which are a set of routes to be performed by vehicles minimizing the overall cost. - .. code-block:: python from google.cloud import optimization_v1 @@ -586,7 +610,12 @@ def sample_batch_optimize_tours(): ) # Send the request. - response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) # Wrap the response in an operation future. response = operation.from_gapic( diff --git a/google/cloud/optimization_v1/services/fleet_routing/transports/base.py b/google/cloud/optimization_v1/services/fleet_routing/transports/base.py index 9021e88..2373a87 100644 --- a/google/cloud/optimization_v1/services/fleet_routing/transports/base.py +++ b/google/cloud/optimization_v1/services/fleet_routing/transports/base.py @@ -82,6 +82,7 @@ def __init__( always_use_jwt_access (Optional[bool]): Whether self signed JWT should be used for service account credentials. """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. if ":" not in host: host += ":443" @@ -155,9 +156,9 @@ def _prep_wrapped_messages(self, client_info): def close(self): """Closes resources associated with the transport. - .. warning:: - Only call this method if the transport is NOT shared - with other clients - this may cause errors in other clients! + .. warning:: + Only call this method if the transport is NOT shared + with other clients - this may cause errors in other clients! """ raise NotImplementedError() @@ -187,5 +188,9 @@ def batch_optimize_tours( ]: raise NotImplementedError() + @property + def kind(self) -> str: + raise NotImplementedError() + __all__ = ("FleetRoutingTransport",) diff --git a/google/cloud/optimization_v1/services/fleet_routing/transports/grpc.py b/google/cloud/optimization_v1/services/fleet_routing/transports/grpc.py index 02579a5..96e5545 100644 --- a/google/cloud/optimization_v1/services/fleet_routing/transports/grpc.py +++ b/google/cloud/optimization_v1/services/fleet_routing/transports/grpc.py @@ -249,8 +249,7 @@ def create_channel( @property def grpc_channel(self) -> grpc.Channel: - """Return the channel designed to connect to this service. - """ + """Return the channel designed to connect to this service.""" return self._grpc_channel @property @@ -349,5 +348,9 @@ def batch_optimize_tours( def close(self): self.grpc_channel.close() + @property + def kind(self) -> str: + return "grpc" + __all__ = ("FleetRoutingGrpcTransport",) diff --git a/google/cloud/optimization_v1/types/async_model.py b/google/cloud/optimization_v1/types/async_model.py index f99119d..1d44308 100644 --- a/google/cloud/optimization_v1/types/async_model.py +++ b/google/cloud/optimization_v1/types/async_model.py @@ -55,9 +55,16 @@ class InputConfig(proto.Message): """ gcs_source = proto.Field( - proto.MESSAGE, number=1, oneof="source", message="GcsSource", + proto.MESSAGE, + number=1, + oneof="source", + message="GcsSource", + ) + data_format = proto.Field( + proto.ENUM, + number=2, + enum="DataFormat", ) - data_format = proto.Field(proto.ENUM, number=2, enum="DataFormat",) class OutputConfig(proto.Message): @@ -77,9 +84,16 @@ class OutputConfig(proto.Message): """ gcs_destination = proto.Field( - proto.MESSAGE, number=1, oneof="destination", message="GcsDestination", + proto.MESSAGE, + number=1, + oneof="destination", + message="GcsDestination", + ) + data_format = proto.Field( + proto.ENUM, + number=2, + enum="DataFormat", ) - data_format = proto.Field(proto.ENUM, number=2, enum="DataFormat",) class GcsSource(proto.Message): @@ -92,7 +106,10 @@ class GcsSource(proto.Message): location. """ - uri = proto.Field(proto.STRING, number=1,) + uri = proto.Field( + proto.STRING, + number=1, + ) class GcsDestination(proto.Message): @@ -105,7 +122,10 @@ class GcsDestination(proto.Message): location. """ - uri = proto.Field(proto.STRING, number=1,) + uri = proto.Field( + proto.STRING, + number=1, + ) class AsyncModelMetadata(proto.Message): @@ -133,10 +153,25 @@ class State(proto.Enum): CANCELLED = 3 FAILED = 4 - state = proto.Field(proto.ENUM, number=1, enum=State,) - state_message = proto.Field(proto.STRING, number=2,) - create_time = proto.Field(proto.MESSAGE, number=3, message=timestamp_pb2.Timestamp,) - update_time = proto.Field(proto.MESSAGE, number=4, message=timestamp_pb2.Timestamp,) + state = proto.Field( + proto.ENUM, + number=1, + enum=State, + ) + state_message = proto.Field( + proto.STRING, + number=2, + ) + create_time = proto.Field( + proto.MESSAGE, + number=3, + message=timestamp_pb2.Timestamp, + ) + update_time = proto.Field( + proto.MESSAGE, + number=4, + message=timestamp_pb2.Timestamp, + ) __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/optimization_v1/types/fleet_routing.py b/google/cloud/optimization_v1/types/fleet_routing.py index d2e52b7..211c7a5 100644 --- a/google/cloud/optimization_v1/types/fleet_routing.py +++ b/google/cloud/optimization_v1/types/fleet_routing.py @@ -297,30 +297,87 @@ class SearchMode(proto.Enum): RETURN_FAST = 1 CONSUME_ALL_AVAILABLE_TIME = 2 - parent = proto.Field(proto.STRING, number=1,) - timeout = proto.Field(proto.MESSAGE, number=2, message=duration_pb2.Duration,) - model = proto.Field(proto.MESSAGE, number=3, message="ShipmentModel",) - solving_mode = proto.Field(proto.ENUM, number=4, enum=SolvingMode,) - max_validation_errors = proto.Field(proto.INT32, number=5, optional=True,) - search_mode = proto.Field(proto.ENUM, number=6, enum=SearchMode,) + parent = proto.Field( + proto.STRING, + number=1, + ) + timeout = proto.Field( + proto.MESSAGE, + number=2, + message=duration_pb2.Duration, + ) + model = proto.Field( + proto.MESSAGE, + number=3, + message="ShipmentModel", + ) + solving_mode = proto.Field( + proto.ENUM, + number=4, + enum=SolvingMode, + ) + max_validation_errors = proto.Field( + proto.INT32, + number=5, + optional=True, + ) + search_mode = proto.Field( + proto.ENUM, + number=6, + enum=SearchMode, + ) injected_first_solution_routes = proto.RepeatedField( - proto.MESSAGE, number=7, message="ShipmentRoute", + proto.MESSAGE, + number=7, + message="ShipmentRoute", ) injected_solution_constraint = proto.Field( - proto.MESSAGE, number=8, message="InjectedSolutionConstraint", + proto.MESSAGE, + number=8, + message="InjectedSolutionConstraint", ) refresh_details_routes = proto.RepeatedField( - proto.MESSAGE, number=9, message="ShipmentRoute", + proto.MESSAGE, + number=9, + message="ShipmentRoute", + ) + interpret_injected_solutions_using_labels = proto.Field( + proto.BOOL, + number=10, + ) + consider_road_traffic = proto.Field( + proto.BOOL, + number=11, + ) + populate_polylines = proto.Field( + proto.BOOL, + number=12, + ) + populate_transition_polylines = proto.Field( + proto.BOOL, + number=13, + ) + allow_large_deadline_despite_interruption_risk = proto.Field( + proto.BOOL, + number=14, + ) + use_geodesic_distances = proto.Field( + proto.BOOL, + number=15, + ) + geodesic_meters_per_second = proto.Field( + proto.DOUBLE, + number=16, + optional=True, + ) + label = proto.Field( + proto.STRING, + number=17, + ) + populate_travel_step_polylines = proto.Field( + proto.BOOL, + number=20, ) - interpret_injected_solutions_using_labels = proto.Field(proto.BOOL, number=10,) - consider_road_traffic = proto.Field(proto.BOOL, number=11,) - populate_polylines = proto.Field(proto.BOOL, number=12,) - populate_transition_polylines = proto.Field(proto.BOOL, number=13,) - allow_large_deadline_despite_interruption_risk = proto.Field(proto.BOOL, number=14,) - use_geodesic_distances = proto.Field(proto.BOOL, number=15,) - geodesic_meters_per_second = proto.Field(proto.DOUBLE, number=16, optional=True,) - label = proto.Field(proto.STRING, number=17,) - populate_travel_step_polylines = proto.Field(proto.BOOL, number=20,) class OptimizeToursResponse(proto.Message): @@ -380,7 +437,7 @@ class Metrics(proto.Message): The latest end time for a used vehicle, computed as the maximum over all used vehicles of [ShipmentRoute.vehicle_end_time][google.cloud.optimization.v1.ShipmentRoute.vehicle_end_time]. - costs (Sequence[google.cloud.optimization_v1.types.OptimizeToursResponse.Metrics.CostsEntry]): + costs (Mapping[str, float]): Cost of the solution, broken down by cost-related request fields. The keys are proto paths, relative to the input OptimizeToursRequest, e.g. "model.shipments.pickups.cost", @@ -398,29 +455,66 @@ class Metrics(proto.Message): """ aggregated_route_metrics = proto.Field( - proto.MESSAGE, number=1, message="AggregatedMetrics", + proto.MESSAGE, + number=1, + message="AggregatedMetrics", + ) + skipped_mandatory_shipment_count = proto.Field( + proto.INT32, + number=2, + ) + used_vehicle_count = proto.Field( + proto.INT32, + number=3, ) - skipped_mandatory_shipment_count = proto.Field(proto.INT32, number=2,) - used_vehicle_count = proto.Field(proto.INT32, number=3,) earliest_vehicle_start_time = proto.Field( - proto.MESSAGE, number=4, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=4, + message=timestamp_pb2.Timestamp, ) latest_vehicle_end_time = proto.Field( - proto.MESSAGE, number=5, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=5, + message=timestamp_pb2.Timestamp, + ) + costs = proto.MapField( + proto.STRING, + proto.DOUBLE, + number=10, + ) + total_cost = proto.Field( + proto.DOUBLE, + number=6, ) - costs = proto.MapField(proto.STRING, proto.DOUBLE, number=10,) - total_cost = proto.Field(proto.DOUBLE, number=6,) - routes = proto.RepeatedField(proto.MESSAGE, number=1, message="ShipmentRoute",) - request_label = proto.Field(proto.STRING, number=3,) + routes = proto.RepeatedField( + proto.MESSAGE, + number=1, + message="ShipmentRoute", + ) + request_label = proto.Field( + proto.STRING, + number=3, + ) skipped_shipments = proto.RepeatedField( - proto.MESSAGE, number=4, message="SkippedShipment", + proto.MESSAGE, + number=4, + message="SkippedShipment", ) validation_errors = proto.RepeatedField( - proto.MESSAGE, number=5, message="OptimizeToursValidationError", + proto.MESSAGE, + number=5, + message="OptimizeToursValidationError", + ) + metrics = proto.Field( + proto.MESSAGE, + number=6, + message=Metrics, + ) + total_cost = proto.Field( + proto.DOUBLE, + number=2, ) - metrics = proto.Field(proto.MESSAGE, number=6, message=Metrics,) - total_cost = proto.Field(proto.DOUBLE, number=2,) class BatchOptimizeToursRequest(proto.Message): @@ -469,18 +563,33 @@ class AsyncModelConfig(proto.Message): prevents the risk of interruption. """ - display_name = proto.Field(proto.STRING, number=1,) + display_name = proto.Field( + proto.STRING, + number=1, + ) input_config = proto.Field( - proto.MESSAGE, number=2, message=async_model.InputConfig, + proto.MESSAGE, + number=2, + message=async_model.InputConfig, ) output_config = proto.Field( - proto.MESSAGE, number=3, message=async_model.OutputConfig, + proto.MESSAGE, + number=3, + message=async_model.OutputConfig, + ) + enable_checkpoints = proto.Field( + proto.BOOL, + number=4, ) - enable_checkpoints = proto.Field(proto.BOOL, number=4,) - parent = proto.Field(proto.STRING, number=1,) + parent = proto.Field( + proto.STRING, + number=1, + ) model_configs = proto.RepeatedField( - proto.MESSAGE, number=2, message=AsyncModelConfig, + proto.MESSAGE, + number=2, + message=AsyncModelConfig, ) @@ -726,14 +835,24 @@ class Row(proto.Message): """ durations = proto.RepeatedField( - proto.MESSAGE, number=1, message=duration_pb2.Duration, + proto.MESSAGE, + number=1, + message=duration_pb2.Duration, + ) + meters = proto.RepeatedField( + proto.DOUBLE, + number=2, ) - meters = proto.RepeatedField(proto.DOUBLE, number=2,) rows = proto.RepeatedField( - proto.MESSAGE, number=1, message="ShipmentModel.DurationDistanceMatrix.Row", + proto.MESSAGE, + number=1, + message="ShipmentModel.DurationDistanceMatrix.Row", + ) + vehicle_start_tag = proto.Field( + proto.STRING, + number=2, ) - vehicle_start_tag = proto.Field(proto.STRING, number=2,) class PrecedenceRule(proto.Message): r"""A precedence rule between two events (each event is the pickup or @@ -768,12 +887,28 @@ class PrecedenceRule(proto.Message): event. It can be negative. """ - first_index = proto.Field(proto.INT32, number=1, optional=True,) - first_is_delivery = proto.Field(proto.BOOL, number=3,) - second_index = proto.Field(proto.INT32, number=2, optional=True,) - second_is_delivery = proto.Field(proto.BOOL, number=4,) + first_index = proto.Field( + proto.INT32, + number=1, + optional=True, + ) + first_is_delivery = proto.Field( + proto.BOOL, + number=3, + ) + second_index = proto.Field( + proto.INT32, + number=2, + optional=True, + ) + second_is_delivery = proto.Field( + proto.BOOL, + number=4, + ) offset_duration = proto.Field( - proto.MESSAGE, number=5, message=duration_pb2.Duration, + proto.MESSAGE, + number=5, + message=duration_pb2.Duration, ) class BreakRule(proto.Message): @@ -822,13 +957,19 @@ class BreakRequest(proto.Message): """ earliest_start_time = proto.Field( - proto.MESSAGE, number=1, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, ) latest_start_time = proto.Field( - proto.MESSAGE, number=2, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=2, + message=timestamp_pb2.Timestamp, ) min_duration = proto.Field( - proto.MESSAGE, number=3, message=duration_pb2.Duration, + proto.MESSAGE, + number=3, + message=duration_pb2.Duration, ) class FrequencyConstraint(proto.Message): @@ -880,14 +1021,20 @@ class FrequencyConstraint(proto.Message): """ min_break_duration = proto.Field( - proto.MESSAGE, number=1, message=duration_pb2.Duration, + proto.MESSAGE, + number=1, + message=duration_pb2.Duration, ) max_inter_break_duration = proto.Field( - proto.MESSAGE, number=2, message=duration_pb2.Duration, + proto.MESSAGE, + number=2, + message=duration_pb2.Duration, ) break_requests = proto.RepeatedField( - proto.MESSAGE, number=1, message="ShipmentModel.BreakRule.BreakRequest", + proto.MESSAGE, + number=1, + message="ShipmentModel.BreakRule.BreakRequest", ) frequency_constraints = proto.RepeatedField( proto.MESSAGE, @@ -895,34 +1042,73 @@ class FrequencyConstraint(proto.Message): message="ShipmentModel.BreakRule.FrequencyConstraint", ) - shipments = proto.RepeatedField(proto.MESSAGE, number=1, message="Shipment",) - vehicles = proto.RepeatedField(proto.MESSAGE, number=2, message="Vehicle",) - max_active_vehicles = proto.Field(proto.INT32, number=4, optional=True,) + shipments = proto.RepeatedField( + proto.MESSAGE, + number=1, + message="Shipment", + ) + vehicles = proto.RepeatedField( + proto.MESSAGE, + number=2, + message="Vehicle", + ) + max_active_vehicles = proto.Field( + proto.INT32, + number=4, + optional=True, + ) global_start_time = proto.Field( - proto.MESSAGE, number=5, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=5, + message=timestamp_pb2.Timestamp, ) global_end_time = proto.Field( - proto.MESSAGE, number=6, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=6, + message=timestamp_pb2.Timestamp, + ) + global_duration_cost_per_hour = proto.Field( + proto.DOUBLE, + number=7, ) - global_duration_cost_per_hour = proto.Field(proto.DOUBLE, number=7,) duration_distance_matrices = proto.RepeatedField( - proto.MESSAGE, number=8, message=DurationDistanceMatrix, + proto.MESSAGE, + number=8, + message=DurationDistanceMatrix, + ) + duration_distance_matrix_src_tags = proto.RepeatedField( + proto.STRING, + number=9, + ) + duration_distance_matrix_dst_tags = proto.RepeatedField( + proto.STRING, + number=10, ) - duration_distance_matrix_src_tags = proto.RepeatedField(proto.STRING, number=9,) - duration_distance_matrix_dst_tags = proto.RepeatedField(proto.STRING, number=10,) transition_attributes = proto.RepeatedField( - proto.MESSAGE, number=11, message="TransitionAttributes", + proto.MESSAGE, + number=11, + message="TransitionAttributes", ) shipment_type_incompatibilities = proto.RepeatedField( - proto.MESSAGE, number=12, message="ShipmentTypeIncompatibility", + proto.MESSAGE, + number=12, + message="ShipmentTypeIncompatibility", ) shipment_type_requirements = proto.RepeatedField( - proto.MESSAGE, number=13, message="ShipmentTypeRequirement", + proto.MESSAGE, + number=13, + message="ShipmentTypeRequirement", ) precedence_rules = proto.RepeatedField( - proto.MESSAGE, number=14, message=PrecedenceRule, + proto.MESSAGE, + number=14, + message=PrecedenceRule, + ) + break_rules = proto.RepeatedField( + proto.MESSAGE, + number=15, + message=BreakRule, ) - break_rules = proto.RepeatedField(proto.MESSAGE, number=15, message=BreakRule,) class Shipment(proto.Message): @@ -944,7 +1130,7 @@ class Shipment(proto.Message): the shipment. If not specified, the vehicle only needs to visit a location corresponding to the pickups. - load_demands (Sequence[google.cloud.optimization_v1.types.Shipment.LoadDemandsEntry]): + load_demands (Mapping[str, google.cloud.optimization_v1.types.Shipment.Load]): Load demands of the shipment (for example weight, volume, number of pallets etc). The keys in the map should be identifiers describing the type of the corresponding load, @@ -1136,7 +1322,7 @@ class VisitRequest(proto.Message): pickup or delivery of a shipment. This cost must be in the same unit as ``Shipment.penalty_cost`` and must not be negative. - load_demands (Sequence[google.cloud.optimization_v1.types.Shipment.VisitRequest.LoadDemandsEntry]): + load_demands (Mapping[str, google.cloud.optimization_v1.types.Shipment.Load]): Load demands of this visit request. This is just like [Shipment.load_demands][google.cloud.optimization.v1.Shipment.load_demands] field, except that it only applies to this @@ -1162,26 +1348,61 @@ class VisitRequest(proto.Message): """ arrival_location = proto.Field( - proto.MESSAGE, number=1, message=latlng_pb2.LatLng, + proto.MESSAGE, + number=1, + message=latlng_pb2.LatLng, + ) + arrival_waypoint = proto.Field( + proto.MESSAGE, + number=2, + message="Waypoint", ) - arrival_waypoint = proto.Field(proto.MESSAGE, number=2, message="Waypoint",) departure_location = proto.Field( - proto.MESSAGE, number=3, message=latlng_pb2.LatLng, + proto.MESSAGE, + number=3, + message=latlng_pb2.LatLng, + ) + departure_waypoint = proto.Field( + proto.MESSAGE, + number=4, + message="Waypoint", + ) + tags = proto.RepeatedField( + proto.STRING, + number=5, ) - departure_waypoint = proto.Field(proto.MESSAGE, number=4, message="Waypoint",) - tags = proto.RepeatedField(proto.STRING, number=5,) time_windows = proto.RepeatedField( - proto.MESSAGE, number=6, message="TimeWindow", + proto.MESSAGE, + number=6, + message="TimeWindow", + ) + duration = proto.Field( + proto.MESSAGE, + number=7, + message=duration_pb2.Duration, + ) + cost = proto.Field( + proto.DOUBLE, + number=8, ) - duration = proto.Field(proto.MESSAGE, number=7, message=duration_pb2.Duration,) - cost = proto.Field(proto.DOUBLE, number=8,) load_demands = proto.MapField( - proto.STRING, proto.MESSAGE, number=12, message="Shipment.Load", + proto.STRING, + proto.MESSAGE, + number=12, + message="Shipment.Load", + ) + visit_types = proto.RepeatedField( + proto.STRING, + number=10, + ) + label = proto.Field( + proto.STRING, + number=11, ) - visit_types = proto.RepeatedField(proto.STRING, number=10,) - label = proto.Field(proto.STRING, number=11,) demands = proto.RepeatedField( - proto.MESSAGE, number=9, message="CapacityQuantity", + proto.MESSAGE, + number=9, + message="CapacityQuantity", ) class Load(proto.Message): @@ -1199,28 +1420,76 @@ class Load(proto.Message): precision. Must be ≥ 0. """ - amount = proto.Field(proto.INT64, number=2,) + amount = proto.Field( + proto.INT64, + number=2, + ) - pickups = proto.RepeatedField(proto.MESSAGE, number=1, message=VisitRequest,) - deliveries = proto.RepeatedField(proto.MESSAGE, number=2, message=VisitRequest,) - load_demands = proto.MapField(proto.STRING, proto.MESSAGE, number=14, message=Load,) - penalty_cost = proto.Field(proto.DOUBLE, number=4, optional=True,) - allowed_vehicle_indices = proto.RepeatedField(proto.INT32, number=5,) - costs_per_vehicle = proto.RepeatedField(proto.DOUBLE, number=6,) - costs_per_vehicle_indices = proto.RepeatedField(proto.INT32, number=7,) + pickups = proto.RepeatedField( + proto.MESSAGE, + number=1, + message=VisitRequest, + ) + deliveries = proto.RepeatedField( + proto.MESSAGE, + number=2, + message=VisitRequest, + ) + load_demands = proto.MapField( + proto.STRING, + proto.MESSAGE, + number=14, + message=Load, + ) + penalty_cost = proto.Field( + proto.DOUBLE, + number=4, + optional=True, + ) + allowed_vehicle_indices = proto.RepeatedField( + proto.INT32, + number=5, + ) + costs_per_vehicle = proto.RepeatedField( + proto.DOUBLE, + number=6, + ) + costs_per_vehicle_indices = proto.RepeatedField( + proto.INT32, + number=7, + ) pickup_to_delivery_relative_detour_limit = proto.Field( - proto.DOUBLE, number=8, optional=True, + proto.DOUBLE, + number=8, + optional=True, ) pickup_to_delivery_absolute_detour_limit = proto.Field( - proto.MESSAGE, number=9, message=duration_pb2.Duration, + proto.MESSAGE, + number=9, + message=duration_pb2.Duration, ) pickup_to_delivery_time_limit = proto.Field( - proto.MESSAGE, number=10, message=duration_pb2.Duration, + proto.MESSAGE, + number=10, + message=duration_pb2.Duration, + ) + shipment_type = proto.Field( + proto.STRING, + number=11, + ) + label = proto.Field( + proto.STRING, + number=12, + ) + ignore = proto.Field( + proto.BOOL, + number=13, + ) + demands = proto.RepeatedField( + proto.MESSAGE, + number=3, + message="CapacityQuantity", ) - shipment_type = proto.Field(proto.STRING, number=11,) - label = proto.Field(proto.STRING, number=12,) - ignore = proto.Field(proto.BOOL, number=13,) - demands = proto.RepeatedField(proto.MESSAGE, number=3, message="CapacityQuantity",) class ShipmentTypeIncompatibility(proto.Message): @@ -1244,8 +1513,15 @@ class IncompatibilityMode(proto.Enum): NOT_PERFORMED_BY_SAME_VEHICLE = 1 NOT_IN_SAME_VEHICLE_SIMULTANEOUSLY = 2 - types = proto.RepeatedField(proto.STRING, number=1,) - incompatibility_mode = proto.Field(proto.ENUM, number=2, enum=IncompatibilityMode,) + types = proto.RepeatedField( + proto.STRING, + number=1, + ) + incompatibility_mode = proto.Field( + proto.ENUM, + number=2, + enum=IncompatibilityMode, + ) class ShipmentTypeRequirement(proto.Message): @@ -1278,9 +1554,19 @@ class RequirementMode(proto.Enum): IN_SAME_VEHICLE_AT_PICKUP_TIME = 2 IN_SAME_VEHICLE_AT_DELIVERY_TIME = 3 - required_shipment_type_alternatives = proto.RepeatedField(proto.STRING, number=1,) - dependent_shipment_types = proto.RepeatedField(proto.STRING, number=2,) - requirement_mode = proto.Field(proto.ENUM, number=3, enum=RequirementMode,) + required_shipment_type_alternatives = proto.RepeatedField( + proto.STRING, + number=1, + ) + dependent_shipment_types = proto.RepeatedField( + proto.STRING, + number=2, + ) + requirement_mode = proto.Field( + proto.ENUM, + number=3, + enum=RequirementMode, + ) class Vehicle(proto.Message): @@ -1379,7 +1665,7 @@ class Vehicle(proto.Message): This field is a member of `oneof`_ ``_travel_duration_multiple``. unloading_policy (google.cloud.optimization_v1.types.Vehicle.UnloadingPolicy): Unloading policy enforced on the vehicle. - load_limits (Sequence[google.cloud.optimization_v1.types.Vehicle.LoadLimitsEntry]): + load_limits (Mapping[str, google.cloud.optimization_v1.types.Vehicle.LoadLimit]): Capacities of the vehicle (weight, volume, # of pallets for example). The keys in the map are the identifiers of the type of load, consistent with the keys of the @@ -1442,7 +1728,7 @@ class Vehicle(proto.Message): In a given ``OptimizeToursResponse``, the route distance is the sum of all its [transitions.travel_distance_meters][google.cloud.optimization.v1.ShipmentRoute.Transition.travel_distance_meters]. - extra_visit_duration_for_visit_type (Sequence[google.cloud.optimization_v1.types.Vehicle.ExtraVisitDurationForVisitTypeEntry]): + extra_visit_duration_for_visit_type (Mapping[str, google.protobuf.duration_pb2.Duration]): Specifies a map from visit_types strings to durations. The duration is time in addition to [VisitRequest.duration][google.cloud.optimization.v1.Shipment.VisitRequest.duration] @@ -1567,17 +1853,38 @@ class Interval(proto.Message): This field is a member of `oneof`_ ``_max``. """ - min_ = proto.Field(proto.INT64, number=1,) - max_ = proto.Field(proto.INT64, number=2, optional=True,) + min_ = proto.Field( + proto.INT64, + number=1, + ) + max_ = proto.Field( + proto.INT64, + number=2, + optional=True, + ) - max_load = proto.Field(proto.INT64, number=1, optional=True,) - soft_max_load = proto.Field(proto.INT64, number=2,) - cost_per_unit_above_soft_max = proto.Field(proto.DOUBLE, number=3,) + max_load = proto.Field( + proto.INT64, + number=1, + optional=True, + ) + soft_max_load = proto.Field( + proto.INT64, + number=2, + ) + cost_per_unit_above_soft_max = proto.Field( + proto.DOUBLE, + number=3, + ) start_load_interval = proto.Field( - proto.MESSAGE, number=4, message="Vehicle.LoadLimit.Interval", + proto.MESSAGE, + number=4, + message="Vehicle.LoadLimit.Interval", ) end_load_interval = proto.Field( - proto.MESSAGE, number=5, message="Vehicle.LoadLimit.Interval", + proto.MESSAGE, + number=5, + message="Vehicle.LoadLimit.Interval", ) class DurationLimit(proto.Message): @@ -1646,66 +1953,162 @@ class DurationLimit(proto.Message): """ max_duration = proto.Field( - proto.MESSAGE, number=1, message=duration_pb2.Duration, + proto.MESSAGE, + number=1, + message=duration_pb2.Duration, ) soft_max_duration = proto.Field( - proto.MESSAGE, number=2, message=duration_pb2.Duration, + proto.MESSAGE, + number=2, + message=duration_pb2.Duration, ) cost_per_hour_after_soft_max = proto.Field( - proto.DOUBLE, number=3, optional=True, + proto.DOUBLE, + number=3, + optional=True, ) quadratic_soft_max_duration = proto.Field( - proto.MESSAGE, number=4, message=duration_pb2.Duration, + proto.MESSAGE, + number=4, + message=duration_pb2.Duration, ) cost_per_square_hour_after_quadratic_soft_max = proto.Field( - proto.DOUBLE, number=5, optional=True, + proto.DOUBLE, + number=5, + optional=True, ) - travel_mode = proto.Field(proto.ENUM, number=1, enum=TravelMode,) - start_location = proto.Field(proto.MESSAGE, number=3, message=latlng_pb2.LatLng,) - start_waypoint = proto.Field(proto.MESSAGE, number=4, message="Waypoint",) - end_location = proto.Field(proto.MESSAGE, number=5, message=latlng_pb2.LatLng,) - end_waypoint = proto.Field(proto.MESSAGE, number=6, message="Waypoint",) - start_tags = proto.RepeatedField(proto.STRING, number=7,) - end_tags = proto.RepeatedField(proto.STRING, number=8,) + travel_mode = proto.Field( + proto.ENUM, + number=1, + enum=TravelMode, + ) + start_location = proto.Field( + proto.MESSAGE, + number=3, + message=latlng_pb2.LatLng, + ) + start_waypoint = proto.Field( + proto.MESSAGE, + number=4, + message="Waypoint", + ) + end_location = proto.Field( + proto.MESSAGE, + number=5, + message=latlng_pb2.LatLng, + ) + end_waypoint = proto.Field( + proto.MESSAGE, + number=6, + message="Waypoint", + ) + start_tags = proto.RepeatedField( + proto.STRING, + number=7, + ) + end_tags = proto.RepeatedField( + proto.STRING, + number=8, + ) start_time_windows = proto.RepeatedField( - proto.MESSAGE, number=9, message="TimeWindow", + proto.MESSAGE, + number=9, + message="TimeWindow", ) end_time_windows = proto.RepeatedField( - proto.MESSAGE, number=10, message="TimeWindow", + proto.MESSAGE, + number=10, + message="TimeWindow", + ) + travel_duration_multiple = proto.Field( + proto.DOUBLE, + number=11, + optional=True, + ) + unloading_policy = proto.Field( + proto.ENUM, + number=12, + enum=UnloadingPolicy, ) - travel_duration_multiple = proto.Field(proto.DOUBLE, number=11, optional=True,) - unloading_policy = proto.Field(proto.ENUM, number=12, enum=UnloadingPolicy,) load_limits = proto.MapField( - proto.STRING, proto.MESSAGE, number=30, message=LoadLimit, - ) - cost_per_hour = proto.Field(proto.DOUBLE, number=16,) - cost_per_traveled_hour = proto.Field(proto.DOUBLE, number=17,) - cost_per_kilometer = proto.Field(proto.DOUBLE, number=18,) - fixed_cost = proto.Field(proto.DOUBLE, number=19,) - used_if_route_is_empty = proto.Field(proto.BOOL, number=20,) - route_duration_limit = proto.Field(proto.MESSAGE, number=21, message=DurationLimit,) + proto.STRING, + proto.MESSAGE, + number=30, + message=LoadLimit, + ) + cost_per_hour = proto.Field( + proto.DOUBLE, + number=16, + ) + cost_per_traveled_hour = proto.Field( + proto.DOUBLE, + number=17, + ) + cost_per_kilometer = proto.Field( + proto.DOUBLE, + number=18, + ) + fixed_cost = proto.Field( + proto.DOUBLE, + number=19, + ) + used_if_route_is_empty = proto.Field( + proto.BOOL, + number=20, + ) + route_duration_limit = proto.Field( + proto.MESSAGE, + number=21, + message=DurationLimit, + ) travel_duration_limit = proto.Field( - proto.MESSAGE, number=22, message=DurationLimit, + proto.MESSAGE, + number=22, + message=DurationLimit, ) route_distance_limit = proto.Field( - proto.MESSAGE, number=23, message="DistanceLimit", + proto.MESSAGE, + number=23, + message="DistanceLimit", ) extra_visit_duration_for_visit_type = proto.MapField( - proto.STRING, proto.MESSAGE, number=24, message=duration_pb2.Duration, + proto.STRING, + proto.MESSAGE, + number=24, + message=duration_pb2.Duration, + ) + break_rule = proto.Field( + proto.MESSAGE, + number=25, + message="BreakRule", + ) + label = proto.Field( + proto.STRING, + number=27, + ) + ignore = proto.Field( + proto.BOOL, + number=28, + ) + break_rule_indices = proto.RepeatedField( + proto.INT32, + number=29, ) - break_rule = proto.Field(proto.MESSAGE, number=25, message="BreakRule",) - label = proto.Field(proto.STRING, number=27,) - ignore = proto.Field(proto.BOOL, number=28,) - break_rule_indices = proto.RepeatedField(proto.INT32, number=29,) capacities = proto.RepeatedField( - proto.MESSAGE, number=13, message="CapacityQuantity", + proto.MESSAGE, + number=13, + message="CapacityQuantity", ) start_load_intervals = proto.RepeatedField( - proto.MESSAGE, number=14, message="CapacityQuantityInterval", + proto.MESSAGE, + number=14, + message="CapacityQuantityInterval", ) end_load_intervals = proto.RepeatedField( - proto.MESSAGE, number=15, message="CapacityQuantityInterval", + proto.MESSAGE, + number=15, + message="CapacityQuantityInterval", ) @@ -1775,19 +2178,35 @@ class TimeWindow(proto.Message): This field is a member of `oneof`_ ``_cost_per_hour_after_soft_end_time``. """ - start_time = proto.Field(proto.MESSAGE, number=1, message=timestamp_pb2.Timestamp,) - end_time = proto.Field(proto.MESSAGE, number=2, message=timestamp_pb2.Timestamp,) + start_time = proto.Field( + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, + ) + end_time = proto.Field( + proto.MESSAGE, + number=2, + message=timestamp_pb2.Timestamp, + ) soft_start_time = proto.Field( - proto.MESSAGE, number=3, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=3, + message=timestamp_pb2.Timestamp, ) soft_end_time = proto.Field( - proto.MESSAGE, number=4, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=4, + message=timestamp_pb2.Timestamp, ) cost_per_hour_before_soft_start_time = proto.Field( - proto.DOUBLE, number=5, optional=True, + proto.DOUBLE, + number=5, + optional=True, ) cost_per_hour_after_soft_end_time = proto.Field( - proto.DOUBLE, number=6, optional=True, + proto.DOUBLE, + number=6, + optional=True, ) @@ -1802,8 +2221,14 @@ class CapacityQuantity(proto.Message): """ - type_ = proto.Field(proto.STRING, number=1,) - value = proto.Field(proto.INT64, number=2,) + type_ = proto.Field( + proto.STRING, + number=1, + ) + value = proto.Field( + proto.INT64, + number=2, + ) class CapacityQuantityInterval(proto.Message): @@ -1820,9 +2245,20 @@ class CapacityQuantityInterval(proto.Message): This field is a member of `oneof`_ ``_max_value``. """ - type_ = proto.Field(proto.STRING, number=1,) - min_value = proto.Field(proto.INT64, number=2, optional=True,) - max_value = proto.Field(proto.INT64, number=3, optional=True,) + type_ = proto.Field( + proto.STRING, + number=1, + ) + min_value = proto.Field( + proto.INT64, + number=2, + optional=True, + ) + max_value = proto.Field( + proto.INT64, + number=3, + optional=True, + ) class DistanceLimit(proto.Message): @@ -1864,10 +2300,20 @@ class DistanceLimit(proto.Message): This field is a member of `oneof`_ ``_cost_per_kilometer_above_soft_max``. """ - max_meters = proto.Field(proto.INT64, number=1, optional=True,) - soft_max_meters = proto.Field(proto.INT64, number=2, optional=True,) + max_meters = proto.Field( + proto.INT64, + number=1, + optional=True, + ) + soft_max_meters = proto.Field( + proto.INT64, + number=2, + optional=True, + ) cost_per_kilometer_above_soft_max = proto.Field( - proto.DOUBLE, number=3, optional=True, + proto.DOUBLE, + number=3, + optional=True, ) @@ -1926,14 +2372,40 @@ class TransitionAttributes(proto.Message): and *before* starting the destination visit. """ - src_tag = proto.Field(proto.STRING, number=1,) - excluded_src_tag = proto.Field(proto.STRING, number=2,) - dst_tag = proto.Field(proto.STRING, number=3,) - excluded_dst_tag = proto.Field(proto.STRING, number=4,) - cost = proto.Field(proto.DOUBLE, number=5,) - cost_per_kilometer = proto.Field(proto.DOUBLE, number=6,) - distance_limit = proto.Field(proto.MESSAGE, number=7, message="DistanceLimit",) - delay = proto.Field(proto.MESSAGE, number=8, message=duration_pb2.Duration,) + src_tag = proto.Field( + proto.STRING, + number=1, + ) + excluded_src_tag = proto.Field( + proto.STRING, + number=2, + ) + dst_tag = proto.Field( + proto.STRING, + number=3, + ) + excluded_dst_tag = proto.Field( + proto.STRING, + number=4, + ) + cost = proto.Field( + proto.DOUBLE, + number=5, + ) + cost_per_kilometer = proto.Field( + proto.DOUBLE, + number=6, + ) + distance_limit = proto.Field( + proto.MESSAGE, + number=7, + message="DistanceLimit", + ) + delay = proto.Field( + proto.MESSAGE, + number=8, + message=duration_pb2.Duration, + ) class Waypoint(proto.Message): @@ -1971,10 +2443,20 @@ class Waypoint(proto.Message): """ location = proto.Field( - proto.MESSAGE, number=1, oneof="location_type", message="Location", + proto.MESSAGE, + number=1, + oneof="location_type", + message="Location", + ) + place_id = proto.Field( + proto.STRING, + number=2, + oneof="location_type", + ) + side_of_road = proto.Field( + proto.BOOL, + number=3, ) - place_id = proto.Field(proto.STRING, number=2, oneof="location_type",) - side_of_road = proto.Field(proto.BOOL, number=3,) class Location(proto.Message): @@ -1995,8 +2477,16 @@ class Location(proto.Message): This field is a member of `oneof`_ ``_heading``. """ - lat_lng = proto.Field(proto.MESSAGE, number=1, message=latlng_pb2.LatLng,) - heading = proto.Field(proto.INT32, number=2, optional=True,) + lat_lng = proto.Field( + proto.MESSAGE, + number=1, + message=latlng_pb2.LatLng, + ) + heading = proto.Field( + proto.INT32, + number=2, + optional=True, + ) class BreakRule(proto.Message): @@ -2044,13 +2534,19 @@ class BreakRequest(proto.Message): """ earliest_start_time = proto.Field( - proto.MESSAGE, number=1, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, ) latest_start_time = proto.Field( - proto.MESSAGE, number=2, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=2, + message=timestamp_pb2.Timestamp, ) min_duration = proto.Field( - proto.MESSAGE, number=3, message=duration_pb2.Duration, + proto.MESSAGE, + number=3, + message=duration_pb2.Duration, ) class FrequencyConstraint(proto.Message): @@ -2102,15 +2598,25 @@ class FrequencyConstraint(proto.Message): """ min_break_duration = proto.Field( - proto.MESSAGE, number=1, message=duration_pb2.Duration, + proto.MESSAGE, + number=1, + message=duration_pb2.Duration, ) max_inter_break_duration = proto.Field( - proto.MESSAGE, number=2, message=duration_pb2.Duration, + proto.MESSAGE, + number=2, + message=duration_pb2.Duration, ) - break_requests = proto.RepeatedField(proto.MESSAGE, number=1, message=BreakRequest,) + break_requests = proto.RepeatedField( + proto.MESSAGE, + number=1, + message=BreakRequest, + ) frequency_constraints = proto.RepeatedField( - proto.MESSAGE, number=2, message=FrequencyConstraint, + proto.MESSAGE, + number=2, + message=FrequencyConstraint, ) @@ -2268,7 +2774,7 @@ class ShipmentRoute(proto.Message): or [ShipmentRoute.visits][google.cloud.optimization.v1.ShipmentRoute.visits], depending on the context. - route_costs (Sequence[google.cloud.optimization_v1.types.ShipmentRoute.RouteCostsEntry]): + route_costs (Mapping[str, float]): Cost of the route, broken down by cost-related request fields. The keys are proto paths, relative to the input OptimizeToursRequest, e.g. "model.shipments.pickups.cost", @@ -2323,9 +2829,15 @@ class Delay(proto.Message): """ start_time = proto.Field( - proto.MESSAGE, number=1, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, + ) + duration = proto.Field( + proto.MESSAGE, + number=2, + message=duration_pb2.Duration, ) - duration = proto.Field(proto.MESSAGE, number=2, message=duration_pb2.Duration,) class Visit(proto.Message): r"""A visit performed during a route. This visit corresponds to a pickup @@ -2345,7 +2857,7 @@ class Visit(proto.Message): Time at which the visit starts. Note that the vehicle may arrive earlier than this at the visit location. Times are consistent with the ``ShipmentModel``. - load_demands (Sequence[google.cloud.optimization_v1.types.ShipmentRoute.Visit.LoadDemandsEntry]): + load_demands (Mapping[str, google.cloud.optimization_v1.types.Shipment.Load]): Total visit load demand as the sum of the shipment and the visit request ``load_demands``. The values are negative if the visit is a delivery. Demands are reported for the same @@ -2397,26 +2909,56 @@ class Visit(proto.Message): Deprecated: Use [Visit.load_demands][] instead. """ - shipment_index = proto.Field(proto.INT32, number=1,) - is_pickup = proto.Field(proto.BOOL, number=2,) - visit_request_index = proto.Field(proto.INT32, number=3,) + shipment_index = proto.Field( + proto.INT32, + number=1, + ) + is_pickup = proto.Field( + proto.BOOL, + number=2, + ) + visit_request_index = proto.Field( + proto.INT32, + number=3, + ) start_time = proto.Field( - proto.MESSAGE, number=4, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=4, + message=timestamp_pb2.Timestamp, ) load_demands = proto.MapField( - proto.STRING, proto.MESSAGE, number=11, message="Shipment.Load", + proto.STRING, + proto.MESSAGE, + number=11, + message="Shipment.Load", + ) + detour = proto.Field( + proto.MESSAGE, + number=6, + message=duration_pb2.Duration, + ) + shipment_label = proto.Field( + proto.STRING, + number=7, + ) + visit_label = proto.Field( + proto.STRING, + number=8, ) - detour = proto.Field(proto.MESSAGE, number=6, message=duration_pb2.Duration,) - shipment_label = proto.Field(proto.STRING, number=7,) - visit_label = proto.Field(proto.STRING, number=8,) arrival_loads = proto.RepeatedField( - proto.MESSAGE, number=9, message="CapacityQuantity", + proto.MESSAGE, + number=9, + message="CapacityQuantity", ) delay_before_start = proto.Field( - proto.MESSAGE, number=10, message="ShipmentRoute.Delay", + proto.MESSAGE, + number=10, + message="ShipmentRoute.Delay", ) demands = proto.RepeatedField( - proto.MESSAGE, number=5, message="CapacityQuantity", + proto.MESSAGE, + number=5, + message="CapacityQuantity", ) class Transition(proto.Message): @@ -2475,7 +3017,7 @@ class Transition(proto.Message): [populate_transition_polylines] [google.cloud.optimization.v1.OptimizeToursRequest.populate_transition_polylines] is set to true. - vehicle_loads (Sequence[google.cloud.optimization_v1.types.ShipmentRoute.Transition.VehicleLoadsEntry]): + vehicle_loads (Mapping[str, google.cloud.optimization_v1.types.ShipmentRoute.VehicleLoad]): Vehicle loads during this transition, for each type that either appears in this vehicle's [Vehicle.load_limits][google.cloud.optimization.v1.Vehicle.load_limits], @@ -2493,33 +3035,58 @@ class Transition(proto.Message): """ travel_duration = proto.Field( - proto.MESSAGE, number=1, message=duration_pb2.Duration, + proto.MESSAGE, + number=1, + message=duration_pb2.Duration, + ) + travel_distance_meters = proto.Field( + proto.DOUBLE, + number=2, + ) + traffic_info_unavailable = proto.Field( + proto.BOOL, + number=3, ) - travel_distance_meters = proto.Field(proto.DOUBLE, number=2,) - traffic_info_unavailable = proto.Field(proto.BOOL, number=3,) delay_duration = proto.Field( - proto.MESSAGE, number=4, message=duration_pb2.Duration, + proto.MESSAGE, + number=4, + message=duration_pb2.Duration, ) break_duration = proto.Field( - proto.MESSAGE, number=5, message=duration_pb2.Duration, + proto.MESSAGE, + number=5, + message=duration_pb2.Duration, ) wait_duration = proto.Field( - proto.MESSAGE, number=6, message=duration_pb2.Duration, + proto.MESSAGE, + number=6, + message=duration_pb2.Duration, ) total_duration = proto.Field( - proto.MESSAGE, number=7, message=duration_pb2.Duration, + proto.MESSAGE, + number=7, + message=duration_pb2.Duration, ) start_time = proto.Field( - proto.MESSAGE, number=8, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=8, + message=timestamp_pb2.Timestamp, ) route_polyline = proto.Field( - proto.MESSAGE, number=9, message="ShipmentRoute.EncodedPolyline", + proto.MESSAGE, + number=9, + message="ShipmentRoute.EncodedPolyline", ) vehicle_loads = proto.MapField( - proto.STRING, proto.MESSAGE, number=11, message="ShipmentRoute.VehicleLoad", + proto.STRING, + proto.MESSAGE, + number=11, + message="ShipmentRoute.VehicleLoad", ) loads = proto.RepeatedField( - proto.MESSAGE, number=10, message="CapacityQuantity", + proto.MESSAGE, + number=10, + message="CapacityQuantity", ) class VehicleLoad(proto.Message): @@ -2534,7 +3101,10 @@ class VehicleLoad(proto.Message): [Transition.vehicle_loads][google.cloud.optimization.v1.ShipmentRoute.Transition.vehicle_loads]. """ - amount = proto.Field(proto.INT64, number=1,) + amount = proto.Field( + proto.INT64, + number=1, + ) class EncodedPolyline(proto.Message): r"""The encoded representation of a polyline. More information on @@ -2548,7 +3118,10 @@ class EncodedPolyline(proto.Message): polyline. """ - points = proto.Field(proto.STRING, number=1,) + points = proto.Field( + proto.STRING, + number=1, + ) class Break(proto.Message): r"""Data representing the execution of a break. @@ -2561,9 +3134,15 @@ class Break(proto.Message): """ start_time = proto.Field( - proto.MESSAGE, number=1, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, + ) + duration = proto.Field( + proto.MESSAGE, + number=2, + message=duration_pb2.Duration, ) - duration = proto.Field(proto.MESSAGE, number=2, message=duration_pb2.Duration,) class TravelStep(proto.Message): r"""Deprecated: Use [ShipmentRoute.transitions][] instead. Travel @@ -2601,37 +3180,101 @@ class TravelStep(proto.Message): is set to true. """ - duration = proto.Field(proto.MESSAGE, number=1, message=duration_pb2.Duration,) - distance_meters = proto.Field(proto.DOUBLE, number=2,) - traffic_info_unavailable = proto.Field(proto.BOOL, number=3,) + duration = proto.Field( + proto.MESSAGE, + number=1, + message=duration_pb2.Duration, + ) + distance_meters = proto.Field( + proto.DOUBLE, + number=2, + ) + traffic_info_unavailable = proto.Field( + proto.BOOL, + number=3, + ) route_polyline = proto.Field( - proto.MESSAGE, number=4, message="ShipmentRoute.EncodedPolyline", + proto.MESSAGE, + number=4, + message="ShipmentRoute.EncodedPolyline", ) - vehicle_index = proto.Field(proto.INT32, number=1,) - vehicle_label = proto.Field(proto.STRING, number=2,) + vehicle_index = proto.Field( + proto.INT32, + number=1, + ) + vehicle_label = proto.Field( + proto.STRING, + number=2, + ) vehicle_start_time = proto.Field( - proto.MESSAGE, number=5, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=5, + message=timestamp_pb2.Timestamp, ) vehicle_end_time = proto.Field( - proto.MESSAGE, number=6, message=timestamp_pb2.Timestamp, - ) - visits = proto.RepeatedField(proto.MESSAGE, number=7, message=Visit,) - transitions = proto.RepeatedField(proto.MESSAGE, number=8, message=Transition,) - has_traffic_infeasibilities = proto.Field(proto.BOOL, number=9,) - route_polyline = proto.Field(proto.MESSAGE, number=10, message=EncodedPolyline,) - breaks = proto.RepeatedField(proto.MESSAGE, number=11, message=Break,) - metrics = proto.Field(proto.MESSAGE, number=12, message="AggregatedMetrics",) - route_costs = proto.MapField(proto.STRING, proto.DOUBLE, number=17,) - route_total_cost = proto.Field(proto.DOUBLE, number=18,) + proto.MESSAGE, + number=6, + message=timestamp_pb2.Timestamp, + ) + visits = proto.RepeatedField( + proto.MESSAGE, + number=7, + message=Visit, + ) + transitions = proto.RepeatedField( + proto.MESSAGE, + number=8, + message=Transition, + ) + has_traffic_infeasibilities = proto.Field( + proto.BOOL, + number=9, + ) + route_polyline = proto.Field( + proto.MESSAGE, + number=10, + message=EncodedPolyline, + ) + breaks = proto.RepeatedField( + proto.MESSAGE, + number=11, + message=Break, + ) + metrics = proto.Field( + proto.MESSAGE, + number=12, + message="AggregatedMetrics", + ) + route_costs = proto.MapField( + proto.STRING, + proto.DOUBLE, + number=17, + ) + route_total_cost = proto.Field( + proto.DOUBLE, + number=18, + ) end_loads = proto.RepeatedField( - proto.MESSAGE, number=13, message="CapacityQuantity", + proto.MESSAGE, + number=13, + message="CapacityQuantity", + ) + travel_steps = proto.RepeatedField( + proto.MESSAGE, + number=14, + message=TravelStep, ) - travel_steps = proto.RepeatedField(proto.MESSAGE, number=14, message=TravelStep,) vehicle_detour = proto.Field( - proto.MESSAGE, number=15, message=duration_pb2.Duration, + proto.MESSAGE, + number=15, + message=duration_pb2.Duration, + ) + delay_before_vehicle_end = proto.Field( + proto.MESSAGE, + number=16, + message=Delay, ) - delay_before_vehicle_end = proto.Field(proto.MESSAGE, number=16, message=Delay,) class SkippedShipment(proto.Message): @@ -2712,13 +3355,34 @@ class Code(proto.Enum): CANNOT_BE_PERFORMED_WITHIN_VEHICLE_TIME_WINDOWS = 6 VEHICLE_NOT_ALLOWED = 7 - code = proto.Field(proto.ENUM, number=1, enum="SkippedShipment.Reason.Code",) - example_vehicle_index = proto.Field(proto.INT32, number=2, optional=True,) - example_exceeded_capacity_type = proto.Field(proto.STRING, number=3,) + code = proto.Field( + proto.ENUM, + number=1, + enum="SkippedShipment.Reason.Code", + ) + example_vehicle_index = proto.Field( + proto.INT32, + number=2, + optional=True, + ) + example_exceeded_capacity_type = proto.Field( + proto.STRING, + number=3, + ) - index = proto.Field(proto.INT32, number=1,) - label = proto.Field(proto.STRING, number=2,) - reasons = proto.RepeatedField(proto.MESSAGE, number=3, message=Reason,) + index = proto.Field( + proto.INT32, + number=1, + ) + label = proto.Field( + proto.STRING, + number=2, + ) + reasons = proto.RepeatedField( + proto.MESSAGE, + number=3, + message=Reason, + ) class AggregatedMetrics(proto.Message): @@ -2761,14 +3425,14 @@ class AggregatedMetrics(proto.Message): travel_distance_meters (float): Total travel distance for a route or a solution. - max_loads (Sequence[google.cloud.optimization_v1.types.AggregatedMetrics.MaxLoadsEntry]): + max_loads (Mapping[str, google.cloud.optimization_v1.types.ShipmentRoute.VehicleLoad]): Maximum load achieved over the entire route (resp. solution), for each of the quantities on this route (resp. solution), computed as the maximum over all [Transition.vehicle_loads][google.cloud.optimization.v1.ShipmentRoute.Transition.vehicle_loads] (resp. [ShipmentRoute.metrics.max_loads][google.cloud.optimization.v1.AggregatedMetrics.max_loads]. - costs (Sequence[google.cloud.optimization_v1.types.AggregatedMetrics.CostsEntry]): + costs (Mapping[str, float]): Deprecated: Use [ShipmentRoute.route_costs][] and [OptimizeToursResponse.Metrics.costs][] instead. total_cost (float): @@ -2776,29 +3440,59 @@ class AggregatedMetrics(proto.Message): [OptimizeToursResponse.Metrics.total_cost][] instead. """ - performed_shipment_count = proto.Field(proto.INT32, number=1,) + performed_shipment_count = proto.Field( + proto.INT32, + number=1, + ) travel_duration = proto.Field( - proto.MESSAGE, number=2, message=duration_pb2.Duration, + proto.MESSAGE, + number=2, + message=duration_pb2.Duration, + ) + wait_duration = proto.Field( + proto.MESSAGE, + number=3, + message=duration_pb2.Duration, ) - wait_duration = proto.Field(proto.MESSAGE, number=3, message=duration_pb2.Duration,) delay_duration = proto.Field( - proto.MESSAGE, number=4, message=duration_pb2.Duration, + proto.MESSAGE, + number=4, + message=duration_pb2.Duration, ) break_duration = proto.Field( - proto.MESSAGE, number=5, message=duration_pb2.Duration, + proto.MESSAGE, + number=5, + message=duration_pb2.Duration, ) visit_duration = proto.Field( - proto.MESSAGE, number=6, message=duration_pb2.Duration, + proto.MESSAGE, + number=6, + message=duration_pb2.Duration, ) total_duration = proto.Field( - proto.MESSAGE, number=7, message=duration_pb2.Duration, + proto.MESSAGE, + number=7, + message=duration_pb2.Duration, + ) + travel_distance_meters = proto.Field( + proto.DOUBLE, + number=8, ) - travel_distance_meters = proto.Field(proto.DOUBLE, number=8,) max_loads = proto.MapField( - proto.STRING, proto.MESSAGE, number=9, message="ShipmentRoute.VehicleLoad", + proto.STRING, + proto.MESSAGE, + number=9, + message="ShipmentRoute.VehicleLoad", + ) + costs = proto.MapField( + proto.STRING, + proto.DOUBLE, + number=10, + ) + total_cost = proto.Field( + proto.DOUBLE, + number=11, ) - costs = proto.MapField(proto.STRING, proto.DOUBLE, number=10,) - total_cost = proto.Field(proto.DOUBLE, number=11,) class InjectedSolutionConstraint(proto.Message): @@ -2927,23 +3621,39 @@ class Level(proto.Enum): enum="InjectedSolutionConstraint.ConstraintRelaxation.Relaxation.Level", ) threshold_time = proto.Field( - proto.MESSAGE, number=2, message=timestamp_pb2.Timestamp, + proto.MESSAGE, + number=2, + message=timestamp_pb2.Timestamp, + ) + threshold_visit_count = proto.Field( + proto.INT32, + number=3, ) - threshold_visit_count = proto.Field(proto.INT32, number=3,) relaxations = proto.RepeatedField( proto.MESSAGE, number=1, message="InjectedSolutionConstraint.ConstraintRelaxation.Relaxation", ) - vehicle_indices = proto.RepeatedField(proto.INT32, number=2,) + vehicle_indices = proto.RepeatedField( + proto.INT32, + number=2, + ) - routes = proto.RepeatedField(proto.MESSAGE, number=1, message="ShipmentRoute",) + routes = proto.RepeatedField( + proto.MESSAGE, + number=1, + message="ShipmentRoute", + ) skipped_shipments = proto.RepeatedField( - proto.MESSAGE, number=2, message="SkippedShipment", + proto.MESSAGE, + number=2, + message="SkippedShipment", ) constraint_relaxations = proto.RepeatedField( - proto.MESSAGE, number=3, message=ConstraintRelaxation, + proto.MESSAGE, + number=3, + message=ConstraintRelaxation, ) @@ -3347,20 +4057,47 @@ class FieldReference(proto.Message): Recursively nested sub-field, if needed. """ - name = proto.Field(proto.STRING, number=1,) - index = proto.Field(proto.INT32, number=2, oneof="index_or_key",) - key = proto.Field(proto.STRING, number=4, oneof="index_or_key",) + name = proto.Field( + proto.STRING, + number=1, + ) + index = proto.Field( + proto.INT32, + number=2, + oneof="index_or_key", + ) + key = proto.Field( + proto.STRING, + number=4, + oneof="index_or_key", + ) sub_field = proto.Field( proto.MESSAGE, number=3, message="OptimizeToursValidationError.FieldReference", ) - code = proto.Field(proto.INT32, number=1,) - display_name = proto.Field(proto.STRING, number=2,) - fields = proto.RepeatedField(proto.MESSAGE, number=3, message=FieldReference,) - error_message = proto.Field(proto.STRING, number=4,) - offending_values = proto.Field(proto.STRING, number=5,) + code = proto.Field( + proto.INT32, + number=1, + ) + display_name = proto.Field( + proto.STRING, + number=2, + ) + fields = proto.RepeatedField( + proto.MESSAGE, + number=3, + message=FieldReference, + ) + error_message = proto.Field( + proto.STRING, + number=4, + ) + offending_values = proto.Field( + proto.STRING, + number=5, + ) __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/noxfile.py b/noxfile.py index 2a2001c..92da31f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -20,16 +20,41 @@ import os import pathlib import shutil +import warnings import nox - -BLACK_VERSION = "black==19.10b0" -BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] +BLACK_VERSION = "black==22.3.0" +ISORT_VERSION = "isort==5.10.1" +LINT_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] DEFAULT_PYTHON_VERSION = "3.8" -SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] + UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] +UNIT_TEST_STANDARD_DEPENDENCIES = [ + "mock", + "asyncmock", + "pytest", + "pytest-cov", + "pytest-asyncio", +] +UNIT_TEST_EXTERNAL_DEPENDENCIES = [] +UNIT_TEST_LOCAL_DEPENDENCIES = [] +UNIT_TEST_DEPENDENCIES = [] +UNIT_TEST_EXTRAS = [] +UNIT_TEST_EXTRAS_BY_PYTHON = {} + +SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] +SYSTEM_TEST_STANDARD_DEPENDENCIES = [ + "mock", + "pytest", + "google-cloud-testutils", +] +SYSTEM_TEST_EXTERNAL_DEPENDENCIES = [] +SYSTEM_TEST_LOCAL_DEPENDENCIES = [] +SYSTEM_TEST_DEPENDENCIES = [] +SYSTEM_TEST_EXTRAS = [] +SYSTEM_TEST_EXTRAS_BY_PYTHON = {} CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() @@ -57,7 +82,9 @@ def lint(session): """ session.install("flake8", BLACK_VERSION) session.run( - "black", "--check", *BLACK_PATHS, + "black", + "--check", + *LINT_PATHS, ) session.run("flake8", "google", "tests") @@ -67,7 +94,28 @@ def blacken(session): """Run black. Format code to uniform standard.""" session.install(BLACK_VERSION) session.run( - "black", *BLACK_PATHS, + "black", + *LINT_PATHS, + ) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def format(session): + """ + Run isort to sort imports. Then run black + to format code to uniform standard. + """ + session.install(BLACK_VERSION, ISORT_VERSION) + # Use the --fss option to sort imports using strict alphabetical order. + # See https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections + session.run( + "isort", + "--fss", + *LINT_PATHS, + ) + session.run( + "black", + *LINT_PATHS, ) @@ -78,23 +126,41 @@ def lint_setup_py(session): session.run("python", "setup.py", "check", "--restructuredtext", "--strict") +def install_unittest_dependencies(session, *constraints): + standard_deps = UNIT_TEST_STANDARD_DEPENDENCIES + UNIT_TEST_DEPENDENCIES + session.install(*standard_deps, *constraints) + + if UNIT_TEST_EXTERNAL_DEPENDENCIES: + warnings.warn( + "'unit_test_external_dependencies' is deprecated. Instead, please " + "use 'unit_test_dependencies' or 'unit_test_local_dependencies'.", + DeprecationWarning, + ) + session.install(*UNIT_TEST_EXTERNAL_DEPENDENCIES, *constraints) + + if UNIT_TEST_LOCAL_DEPENDENCIES: + session.install(*UNIT_TEST_LOCAL_DEPENDENCIES, *constraints) + + if UNIT_TEST_EXTRAS_BY_PYTHON: + extras = UNIT_TEST_EXTRAS_BY_PYTHON.get(session.python, []) + elif UNIT_TEST_EXTRAS: + extras = UNIT_TEST_EXTRAS + else: + extras = [] + + if extras: + session.install("-e", f".[{','.join(extras)}]", *constraints) + else: + session.install("-e", ".", *constraints) + + def default(session): # Install all test dependencies, then install this package in-place. constraints_path = str( CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" ) - session.install( - "mock", - "asyncmock", - "pytest", - "pytest-cov", - "pytest-asyncio", - "-c", - constraints_path, - ) - - session.install("-e", ".", "-c", constraints_path) + install_unittest_dependencies(session, "-c", constraints_path) # Run py.test against the unit tests. session.run( @@ -118,6 +184,35 @@ def unit(session): default(session) +def install_systemtest_dependencies(session, *constraints): + + # Use pre-release gRPC for system tests. + session.install("--pre", "grpcio") + + session.install(*SYSTEM_TEST_STANDARD_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_EXTERNAL_DEPENDENCIES: + session.install(*SYSTEM_TEST_EXTERNAL_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_LOCAL_DEPENDENCIES: + session.install("-e", *SYSTEM_TEST_LOCAL_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_DEPENDENCIES: + session.install("-e", *SYSTEM_TEST_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_EXTRAS_BY_PYTHON: + extras = SYSTEM_TEST_EXTRAS_BY_PYTHON.get(session.python, []) + elif SYSTEM_TEST_EXTRAS: + extras = SYSTEM_TEST_EXTRAS + else: + extras = [] + + if extras: + session.install("-e", f".[{','.join(extras)}]", *constraints) + else: + session.install("-e", ".", *constraints) + + @nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) def system(session): """Run the system test suite.""" @@ -140,13 +235,7 @@ def system(session): if not system_test_exists and not system_test_folder_exists: session.skip("System tests were not found") - # Use pre-release gRPC for system tests. - session.install("--pre", "grpcio") - - # Install all test dependencies, then install this package into the - # virtualenv's dist-packages. - session.install("mock", "pytest", "google-cloud-testutils", "-c", constraints_path) - session.install("-e", ".", "-c", constraints_path) + install_systemtest_dependencies(session, "-c", constraints_path) # Run py.test against the system tests. if system_test_exists: diff --git a/owlbot.py b/owlbot.py index 60d092a..468a49f 100644 --- a/owlbot.py +++ b/owlbot.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from pathlib import Path + import synthtool as s import synthtool.gcp as gcp from synthtool.languages import python @@ -67,4 +69,6 @@ # Run blacken session # ---------------------------------------------------------------------------- -s.shell.run(["nox", "-s", "blacken"], hide_output=False) +# run blacken session for all directories which have a noxfile +for noxfile in Path(".").glob("**/noxfile.py"): + s.shell.run(["nox", "-s", "blacken"], cwd=noxfile.parent, hide_output=False) diff --git a/samples/generated_samples/snippet_metadata_optimization_v1.json b/samples/generated_samples/snippet_metadata_optimization_v1.json index 413f2a8..c35e77d 100644 --- a/samples/generated_samples/snippet_metadata_optimization_v1.json +++ b/samples/generated_samples/snippet_metadata_optimization_v1.json @@ -1,16 +1,57 @@ { + "clientLibrary": { + "apis": [ + { + "id": "google.cloud.optimization.v1", + "version": "v1" + } + ], + "language": "PYTHON", + "name": "google-cloud-optimization" + }, "snippets": [ { + "canonical": true, "clientMethod": { "async": true, + "client": { + "fullName": "google.cloud.optimization_v1.FleetRoutingAsyncClient", + "shortName": "FleetRoutingAsyncClient" + }, + "fullName": "google.cloud.optimization_v1.FleetRoutingAsyncClient.batch_optimize_tours", "method": { + "fullName": "google.cloud.optimization.v1.FleetRouting.BatchOptimizeTours", "service": { + "fullName": "google.cloud.optimization.v1.FleetRouting", "shortName": "FleetRouting" }, "shortName": "BatchOptimizeTours" - } + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.optimization_v1.types.BatchOptimizeToursRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.api_core.operation_async.AsyncOperation", + "shortName": "batch_optimize_tours" }, + "description": "Sample for BatchOptimizeTours", "file": "cloudoptimization_v1_generated_fleet_routing_batch_optimize_tours_async.py", + "language": "PYTHON", + "origin": "API_DEFINITION", "regionTag": "cloudoptimization_v1_generated_FleetRouting_BatchOptimizeTours_async", "segments": [ { @@ -43,18 +84,50 @@ "start": 51, "type": "RESPONSE_HANDLING" } - ] + ], + "title": "cloudoptimization_v1_generated_fleet_routing_batch_optimize_tours_async.py" }, { + "canonical": true, "clientMethod": { + "client": { + "fullName": "google.cloud.optimization_v1.FleetRoutingClient", + "shortName": "FleetRoutingClient" + }, + "fullName": "google.cloud.optimization_v1.FleetRoutingClient.batch_optimize_tours", "method": { + "fullName": "google.cloud.optimization.v1.FleetRouting.BatchOptimizeTours", "service": { + "fullName": "google.cloud.optimization.v1.FleetRouting", "shortName": "FleetRouting" }, "shortName": "BatchOptimizeTours" - } + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.optimization_v1.types.BatchOptimizeToursRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.api_core.operation.Operation", + "shortName": "batch_optimize_tours" }, + "description": "Sample for BatchOptimizeTours", "file": "cloudoptimization_v1_generated_fleet_routing_batch_optimize_tours_sync.py", + "language": "PYTHON", + "origin": "API_DEFINITION", "regionTag": "cloudoptimization_v1_generated_FleetRouting_BatchOptimizeTours_sync", "segments": [ { @@ -87,19 +160,51 @@ "start": 51, "type": "RESPONSE_HANDLING" } - ] + ], + "title": "cloudoptimization_v1_generated_fleet_routing_batch_optimize_tours_sync.py" }, { + "canonical": true, "clientMethod": { "async": true, + "client": { + "fullName": "google.cloud.optimization_v1.FleetRoutingAsyncClient", + "shortName": "FleetRoutingAsyncClient" + }, + "fullName": "google.cloud.optimization_v1.FleetRoutingAsyncClient.optimize_tours", "method": { + "fullName": "google.cloud.optimization.v1.FleetRouting.OptimizeTours", "service": { + "fullName": "google.cloud.optimization.v1.FleetRouting", "shortName": "FleetRouting" }, "shortName": "OptimizeTours" - } + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.optimization_v1.types.OptimizeToursRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.cloud.optimization_v1.types.OptimizeToursResponse", + "shortName": "optimize_tours" }, + "description": "Sample for OptimizeTours", "file": "cloudoptimization_v1_generated_fleet_routing_optimize_tours_async.py", + "language": "PYTHON", + "origin": "API_DEFINITION", "regionTag": "cloudoptimization_v1_generated_FleetRouting_OptimizeTours_async", "segments": [ { @@ -132,18 +237,50 @@ "start": 42, "type": "RESPONSE_HANDLING" } - ] + ], + "title": "cloudoptimization_v1_generated_fleet_routing_optimize_tours_async.py" }, { + "canonical": true, "clientMethod": { + "client": { + "fullName": "google.cloud.optimization_v1.FleetRoutingClient", + "shortName": "FleetRoutingClient" + }, + "fullName": "google.cloud.optimization_v1.FleetRoutingClient.optimize_tours", "method": { + "fullName": "google.cloud.optimization.v1.FleetRouting.OptimizeTours", "service": { + "fullName": "google.cloud.optimization.v1.FleetRouting", "shortName": "FleetRouting" }, "shortName": "OptimizeTours" - } + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.optimization_v1.types.OptimizeToursRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.cloud.optimization_v1.types.OptimizeToursResponse", + "shortName": "optimize_tours" }, + "description": "Sample for OptimizeTours", "file": "cloudoptimization_v1_generated_fleet_routing_optimize_tours_sync.py", + "language": "PYTHON", + "origin": "API_DEFINITION", "regionTag": "cloudoptimization_v1_generated_FleetRouting_OptimizeTours_sync", "segments": [ { @@ -176,7 +313,8 @@ "start": 42, "type": "RESPONSE_HANDLING" } - ] + ], + "title": "cloudoptimization_v1_generated_fleet_routing_optimize_tours_sync.py" } ] } diff --git a/samples/snippets/async_api.py b/samples/snippets/async_api.py new file mode 100644 index 0000000..8ab95b9 --- /dev/null +++ b/samples/snippets/async_api.py @@ -0,0 +1,59 @@ +# 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. + +# [START cloudoptimization_async_api] + +from google.api_core.exceptions import GoogleAPICallError +from google.cloud import optimization_v1 + +# TODO(developer): Uncomment these variables before running the sample. +# project_id= 'YOUR_PROJECT_ID' +# request_file_name = 'YOUR_REQUEST_FILE_NAME' +# request_model_gcs_path = 'gs://YOUR_PROJECT/YOUR_BUCKET/YOUR_REQUEST_MODEL_PATH' +# model_solution_gcs_path = 'gs://YOUR_PROJECT/YOUR_BUCKET/YOUR_SOLUCTION_PATH' + + +def call_async_api( + project_id: str, request_model_gcs_path: str, model_solution_gcs_path_prefix: str +) -> None: + """Call the async api for fleet routing.""" + # Use the default credentials for the environment to authenticate the client. + fleet_routing_client = optimization_v1.FleetRoutingClient() + request_file_name = "resources/async_request.json" + + with open(request_file_name, "r") as f: + fleet_routing_request = optimization_v1.BatchOptimizeToursRequest.from_json( + f.read() + ) + fleet_routing_request.parent = f"projects/{project_id}" + for idx, mc in enumerate(fleet_routing_request.model_configs): + mc.input_config.gcs_source.uri = request_model_gcs_path + model_solution_gcs_path = f"{model_solution_gcs_path_prefix}_{idx}" + mc.output_config.gcs_destination.uri = model_solution_gcs_path + + # The timeout argument for the gRPC call is independent from the `timeout` + # field in the request's OptimizeToursRequest message(s). + operation = fleet_routing_client.batch_optimize_tours(fleet_routing_request) + print(operation.operation.name) + + try: + # Block to wait for the job to finish. + result = operation.result() + print(result) + # Do you stuff. + except GoogleAPICallError: + print(operation.operation.error) + + +# [END cloudoptimization_async_api] diff --git a/samples/snippets/async_api_test.py b/samples/snippets/async_api_test.py new file mode 100644 index 0000000..a400d2c --- /dev/null +++ b/samples/snippets/async_api_test.py @@ -0,0 +1,48 @@ +# 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 uuid + +import google.auth +from google.cloud import storage +import pytest +from samples.snippets import async_api + + +# TODO(developer): Replace the variables in the file before use. +# A sample request model can be found at resources/async_request_model.json. +TEST_UUID = uuid.uuid4() +BUCKET = f"optimization-ai-{TEST_UUID}" +OUTPUT_PREFIX = f"code_snippets_test_output_{TEST_UUID}" +INPUT_URI = "gs://cloud-samples-data/optimization-ai/async_request_model.json" +BATCH_OUTPUT_URI_PREFIX = "gs://{}/{}/".format(BUCKET, OUTPUT_PREFIX) + + +@pytest.fixture(autouse=True) +def setup_teardown() -> None: + """Create a temporary bucket to store optimization output.""" + storage_client = storage.Client() + bucket = storage_client.create_bucket(BUCKET) + + yield + + bucket.delete(force=True) + + +def test_call_async_api(capsys: pytest.LogCaptureFixture) -> None: + _, project_id = google.auth.default() + async_api.call_async_api(project_id, INPUT_URI, BATCH_OUTPUT_URI_PREFIX) + out, _ = capsys.readouterr() + + assert "operations" in out diff --git a/samples/snippets/get_operation.py b/samples/snippets/get_operation.py new file mode 100644 index 0000000..7f20b96 --- /dev/null +++ b/samples/snippets/get_operation.py @@ -0,0 +1,34 @@ +# 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. + +# [START cloudoptimization_get_operation] +from google.cloud import optimization_v1 + + +def get_operation(operation_full_id: str) -> None: + """Get operation details and status.""" + # TODO(developer): Uncomment and set the following variables + # operation_full_id = \ + # "projects/[projectId]/operations/[operationId]" + + client = optimization_v1.FleetRoutingClient() + # Get the latest state of a long-running operation. + response = client.transport.operations_client.get_operation(operation_full_id) + + print("Name: {}".format(response.name)) + print("Operation details:") + print(response) + + +# [END cloudoptimization_get_operation] diff --git a/samples/snippets/get_operation_test.py b/samples/snippets/get_operation_test.py new file mode 100644 index 0000000..e942e44 --- /dev/null +++ b/samples/snippets/get_operation_test.py @@ -0,0 +1,41 @@ +# 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.auth +from google.cloud import optimization_v1 +import pytest + +import get_operation + + +@pytest.fixture(scope="function") +def operation_id() -> str: + fleet_routing_client = optimization_v1.FleetRoutingClient() + + _, project_id = google.auth.default() + fleet_routing_request = {"parent": f"projects/{project_id}"} + + # Make the request + operation = fleet_routing_client.batch_optimize_tours(fleet_routing_request) + + yield operation.operation.name + + +def test_get_operation_status( + capsys: pytest.LogCaptureFixture, operation_id: str +) -> None: + get_operation.get_operation(operation_id) + out, _ = capsys.readouterr() + assert "Operation details" in out diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py new file mode 100644 index 0000000..a40410b --- /dev/null +++ b/samples/snippets/noxfile.py @@ -0,0 +1,311 @@ +# Copyright 2019 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 __future__ import print_function + +import glob +import os +from pathlib import Path +import sys +from typing import Callable, Dict, List, Optional + +import nox + + +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING +# DO NOT EDIT THIS FILE EVER! +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING + +BLACK_VERSION = "black==22.3.0" +ISORT_VERSION = "isort==5.10.1" + +# Copy `noxfile_config.py` to your directory and modify it instead. + +# `TEST_CONFIG` dict is a configuration hook that allows users to +# modify the test configurations. The values here should be in sync +# with `noxfile_config.py`. Users will copy `noxfile_config.py` into +# their directory and modify it. + +TEST_CONFIG = { + # You can opt out from the test for specific Python versions. + "ignored_versions": [], + # Old samples are opted out of enforcing Python type hints + # All new samples should feature them + "enforce_type_hints": False, + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + "gcloud_project_env": "GOOGLE_CLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + "envs": {}, +} + + +try: + # Ensure we can import noxfile_config in the project's directory. + sys.path.append(".") + from noxfile_config import TEST_CONFIG_OVERRIDE +except ImportError as e: + print("No user noxfile_config found: detail: {}".format(e)) + TEST_CONFIG_OVERRIDE = {} + +# Update the TEST_CONFIG with the user supplied values. +TEST_CONFIG.update(TEST_CONFIG_OVERRIDE) + + +def get_pytest_env_vars() -> Dict[str, str]: + """Returns a dict for pytest invocation.""" + ret = {} + + # Override the GCLOUD_PROJECT and the alias. + env_key = TEST_CONFIG["gcloud_project_env"] + # This should error out if not set. + ret["GOOGLE_CLOUD_PROJECT"] = os.environ[env_key] + + # Apply user supplied envs. + ret.update(TEST_CONFIG["envs"]) + return ret + + +# DO NOT EDIT - automatically generated. +# All versions used to test samples. +ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] + +# Any default versions that should be ignored. +IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] + +TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) + +INSTALL_LIBRARY_FROM_SOURCE = os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False) in ( + "True", + "true", +) + +# Error if a python version is missing +nox.options.error_on_missing_interpreters = True + +# +# Style Checks +# + + +def _determine_local_import_names(start_dir: str) -> List[str]: + """Determines all import names that should be considered "local". + + This is used when running the linter to insure that import order is + properly checked. + """ + file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)] + return [ + basename + for basename, extension in file_ext_pairs + if extension == ".py" + or os.path.isdir(os.path.join(start_dir, basename)) + and basename not in ("__pycache__") + ] + + +# Linting with flake8. +# +# We ignore the following rules: +# E203: whitespace before ‘:’ +# E266: too many leading ‘#’ for block comment +# E501: line too long +# I202: Additional newline in a section of imports +# +# We also need to specify the rules which are ignored by default: +# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121'] +FLAKE8_COMMON_ARGS = [ + "--show-source", + "--builtin=gettext", + "--max-complexity=20", + "--import-order-style=google", + "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", + "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", + "--max-line-length=88", +] + + +@nox.session +def lint(session: nox.sessions.Session) -> None: + if not TEST_CONFIG["enforce_type_hints"]: + session.install("flake8", "flake8-import-order") + else: + session.install("flake8", "flake8-import-order", "flake8-annotations") + + local_names = _determine_local_import_names(".") + args = FLAKE8_COMMON_ARGS + [ + "--application-import-names", + ",".join(local_names), + ".", + ] + session.run("flake8", *args) + + +# +# Black +# + + +@nox.session +def blacken(session: nox.sessions.Session) -> None: + """Run black. Format code to uniform standard.""" + session.install(BLACK_VERSION) + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + + session.run("black", *python_files) + + +# +# format = isort + black +# + + +@nox.session +def format(session: nox.sessions.Session) -> None: + """ + Run isort to sort imports. Then run black + to format code to uniform standard. + """ + session.install(BLACK_VERSION, ISORT_VERSION) + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + + # Use the --fss option to sort imports using strict alphabetical order. + # See https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections + session.run("isort", "--fss", *python_files) + session.run("black", *python_files) + + +# +# Sample Tests +# + + +PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] + + +def _session_tests( + session: nox.sessions.Session, post_install: Callable = None +) -> None: + # check for presence of tests + test_list = glob.glob("*_test.py") + glob.glob("test_*.py") + test_list.extend(glob.glob("tests")) + + if len(test_list) == 0: + print("No tests found, skipping directory.") + return + + if TEST_CONFIG["pip_version_override"]: + pip_version = TEST_CONFIG["pip_version_override"] + session.install(f"pip=={pip_version}") + """Runs py.test for a particular project.""" + concurrent_args = [] + if os.path.exists("requirements.txt"): + if os.path.exists("constraints.txt"): + session.install("-r", "requirements.txt", "-c", "constraints.txt") + else: + session.install("-r", "requirements.txt") + with open("requirements.txt") as rfile: + packages = rfile.read() + + if os.path.exists("requirements-test.txt"): + if os.path.exists("constraints-test.txt"): + session.install("-r", "requirements-test.txt", "-c", "constraints-test.txt") + else: + session.install("-r", "requirements-test.txt") + with open("requirements-test.txt") as rtfile: + packages += rtfile.read() + + if INSTALL_LIBRARY_FROM_SOURCE: + session.install("-e", _get_repo_root()) + + if post_install: + post_install(session) + + if "pytest-parallel" in packages: + concurrent_args.extend(["--workers", "auto", "--tests-per-worker", "auto"]) + elif "pytest-xdist" in packages: + concurrent_args.extend(["-n", "auto"]) + + session.run( + "pytest", + *(PYTEST_COMMON_ARGS + session.posargs + concurrent_args), + # Pytest will return 5 when no tests are collected. This can happen + # on travis where slow and flaky tests are excluded. + # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html + success_codes=[0, 5], + env=get_pytest_env_vars(), + ) + + +@nox.session(python=ALL_VERSIONS) +def py(session: nox.sessions.Session) -> None: + """Runs py.test for a sample using the specified version of Python.""" + if session.python in TESTED_VERSIONS: + _session_tests(session) + else: + session.skip( + "SKIPPED: {} tests are disabled for this sample.".format(session.python) + ) + + +# +# Readmegen +# + + +def _get_repo_root() -> Optional[str]: + """Returns the root folder of the project.""" + # Get root of this repository. Assume we don't have directories nested deeper than 10 items. + p = Path(os.getcwd()) + for i in range(10): + if p is None: + break + if Path(p / ".git").exists(): + return str(p) + # .git is not available in repos cloned via Cloud Build + # setup.py is always in the library's root, so use that instead + # https://github.com/googleapis/synthtool/issues/792 + if Path(p / "setup.py").exists(): + return str(p) + p = p.parent + raise Exception("Unable to detect repository root.") + + +GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")]) + + +@nox.session +@nox.parametrize("path", GENERATED_READMES) +def readmegen(session: nox.sessions.Session, path: str) -> None: + """(Re-)generates the readme for a sample.""" + session.install("jinja2", "pyyaml") + dir_ = os.path.dirname(path) + + if os.path.exists(os.path.join(dir_, "requirements.txt")): + session.install("-r", os.path.join(dir_, "requirements.txt")) + + in_file = os.path.join(dir_, "README.rst.in") + session.run( + "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file + ) diff --git a/samples/snippets/noxfile_config.py b/samples/snippets/noxfile_config.py new file mode 100644 index 0000000..545546d --- /dev/null +++ b/samples/snippets/noxfile_config.py @@ -0,0 +1,42 @@ +# Copyright 2021 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. + +# Default TEST_CONFIG_OVERRIDE for python repos. + +# You can copy this file into your directory, then it will be imported from +# the noxfile.py. + +# The source of truth: +# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/noxfile_config.py + +TEST_CONFIG_OVERRIDE = { + # You can opt out from the test for specific Python versions. + "ignored_versions": ["2.7", "3.6"], + # Old samples are opted out of enforcing Python type hints + # All new samples should feature them + "enforce_type_hints": True, + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + "gcloud_project_env": "GOOGLE_CLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + "envs": {}, +} diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt new file mode 100644 index 0000000..9b2511d --- /dev/null +++ b/samples/snippets/requirements-test.txt @@ -0,0 +1,2 @@ +pytest==7.1.2 + diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt new file mode 100644 index 0000000..14af1a0 --- /dev/null +++ b/samples/snippets/requirements.txt @@ -0,0 +1,2 @@ +google-cloud-optimization==0.1.0 +google-cloud-storage==2.3.0 diff --git a/samples/snippets/resources/async_request.json b/samples/snippets/resources/async_request.json new file mode 100644 index 0000000..10ae1b0 --- /dev/null +++ b/samples/snippets/resources/async_request.json @@ -0,0 +1,33 @@ +{ + "parent": "projects/${YOUR_GCP_PROJECT_ID}", + "model_configs":[ + { + "input_config":{ + "gcs_source":{ + "uri":"${REQUEST_MODEL_GCS_PATH}" + }, + "data_format":"JSON" + }, + "output_config":{ + "gcs_destination":{ + "uri":"${MODEL_SOLUTION_GCS_PATH}" + }, + "data_format":"JSON" + } + }, + { + "input_config":{ + "gcs_source":{ + "uri":"${REQUEST_MODEL_GCS_PATH}" + }, + "data_format":"JSON" + }, + "output_config":{ + "gcs_destination":{ + "uri":"${MODEL_SOLUTION_GCS_PATH}" + }, + "data_format":"JSON" + } + } + ] + } \ No newline at end of file diff --git a/samples/snippets/resources/async_request_model.json b/samples/snippets/resources/async_request_model.json new file mode 100644 index 0000000..75c4b11 --- /dev/null +++ b/samples/snippets/resources/async_request_model.json @@ -0,0 +1,114 @@ +{ + "parent":"${YOUR_GCP_PROJECT_ID}", + "allowLargeDeadlineDespiteInterruptionRisk":true, + "model":{ + "shipments":[ + { + "deliveries":[ + { + "arrivalLocation":{ + "latitude":48.880941999999997, + "longitude":2.3238660000000002 + }, + "duration":"250s", + "timeWindows":[ + { + "endTime":"1970-01-01T01:06:40Z", + "startTime":"1970-01-01T00:50:00Z" + } + ] + } + ], + "loadDemands": { + "weight": { + "amount": "10" + } + }, + "pickups":[ + { + "arrivalLocation":{ + "latitude":48.874507000000001, + "longitude":2.3036099999999999 + }, + "duration":"150s", + "timeWindows":[ + { + "endTime":"1970-01-01T00:33:20Z", + "startTime":"1970-01-01T00:16:40Z" + } + ] + } + ] + }, + { + "deliveries":[ + { + "arrivalLocation":{ + "latitude":48.880940000000002, + "longitude":2.3238439999999998 + }, + "duration":"251s", + "timeWindows":[ + { + "endTime":"1970-01-01T01:06:41Z", + "startTime":"1970-01-01T00:50:01Z" + } + ] + } + ], + "loadDemands": { + "weight": { + "amount": "20" + } + }, + "pickups":[ + { + "arrivalLocation":{ + "latitude":48.880943000000002, + "longitude":2.3238669999999999 + }, + "duration":"151s", + "timeWindows":[ + { + "endTime":"1970-01-01T00:33:21Z", + "startTime":"1970-01-01T00:16:41Z" + } + ] + } + ] + } + ], + "vehicles":[ + { + "loadLimits": { + "weight": { + "maxLoad": 50 + } + }, + "endLocation":{ + "latitude":48.863109999999999, + "longitude":2.341205 + }, + "startLocation":{ + "latitude":48.863101999999998, + "longitude":2.3412039999999998 + } + }, + { + "loadLimits": { + "weight": { + "maxLoad": 60 + } + }, + "endLocation":{ + "latitude":48.863120000000002, + "longitude":2.341215 + }, + "startLocation":{ + "latitude":48.863112000000001, + "longitude":2.3412139999999999 + } + } + ] + } + } \ No newline at end of file diff --git a/samples/snippets/resources/sync_request.json b/samples/snippets/resources/sync_request.json new file mode 100644 index 0000000..cbdf747 --- /dev/null +++ b/samples/snippets/resources/sync_request.json @@ -0,0 +1,114 @@ +{ + "parent": "projects/${YOUR_GCP_PROJECT_ID}", + "timeout": "15s", + "model": { + "shipments": [ + { + "deliveries": [ + { + "arrivalLocation": { + "latitude": 48.880942, + "longitude": 2.323866 + }, + "duration": "250s", + "timeWindows": [ + { + "endTime": "1970-01-01T01:06:40Z", + "startTime": "1970-01-01T00:50:00Z" + } + ] + } + ], + "loadDemands": { + "weight": { + "amount": "10" + } + }, + "pickups": [ + { + "arrivalLocation": { + "latitude": 48.874507, + "longitude": 2.30361 + }, + "duration": "150s", + "timeWindows": [ + { + "endTime": "1970-01-01T00:33:20Z", + "startTime": "1970-01-01T00:16:40Z" + } + ] + } + ] + }, + { + "deliveries": [ + { + "arrivalLocation": { + "latitude": 48.88094, + "longitude": 2.323844 + }, + "duration": "251s", + "timeWindows": [ + { + "endTime": "1970-01-01T01:06:41Z", + "startTime": "1970-01-01T00:50:01Z" + } + ] + } + ], + "loadDemands": { + "weight": { + "amount": "20" + } + }, + "pickups": [ + { + "arrivalLocation": { + "latitude": 48.880943, + "longitude": 2.323867 + }, + "duration": "151s", + "timeWindows": [ + { + "endTime": "1970-01-01T00:33:21Z", + "startTime": "1970-01-01T00:16:41Z" + } + ] + } + ] + } + ], + "vehicles": [ + { + "loadLimits": { + "weight": { + "maxLoad": 50 + } + }, + "endLocation": { + "latitude": 48.86311, + "longitude": 2.341205 + }, + "startLocation": { + "latitude": 48.863102, + "longitude": 2.341204 + } + }, + { + "loadLimits": { + "weight": { + "maxLoad": 60 + } + }, + "endLocation": { + "latitude": 48.86312, + "longitude": 2.341215 + }, + "startLocation": { + "latitude": 48.863112, + "longitude": 2.341214 + } + } + ] + } +} \ No newline at end of file diff --git a/samples/snippets/sync_api.py b/samples/snippets/sync_api.py new file mode 100644 index 0000000..187afd2 --- /dev/null +++ b/samples/snippets/sync_api.py @@ -0,0 +1,47 @@ +# 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. + +# [START cloudoptimization_sync_api] + +from google.cloud import optimization_v1 + +# TODO(developer): Uncomment these variables before running the sample. +# project_id= 'YOUR_PROJECT_ID' + + +def call_sync_api(project_id: str) -> None: + """Call the sync api for fleet routing.""" + # Use the default credentials for the environment. + # Change the file name to your request file. + request_file_name = "resources/sync_request.json" + fleet_routing_client = optimization_v1.FleetRoutingClient() + + with open(request_file_name, "r") as f: + # The request must include the `parent` field with the value set to + # 'projects/{YOUR_GCP_PROJECT_ID}'. + fleet_routing_request = optimization_v1.OptimizeToursRequest.from_json(f.read()) + fleet_routing_request.parent = f"projects/{project_id}" + # Send the request and print the response. + # Fleet Routing will return a response by the earliest of the `timeout` + # field in the request payload and the gRPC timeout specified below. + fleet_routing_response = fleet_routing_client.optimize_tours( + fleet_routing_request, timeout=100 + ) + print(fleet_routing_response) + # If you want to format the response to JSON, you can do the following: + # from google.protobuf.json_format import MessageToJson + # json_obj = MessageToJson(fleet_routing_response._pb) + + +# [END cloudoptimization_sync_api] diff --git a/samples/snippets/sync_api_test.py b/samples/snippets/sync_api_test.py new file mode 100644 index 0000000..a5354e1 --- /dev/null +++ b/samples/snippets/sync_api_test.py @@ -0,0 +1,28 @@ +# 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.auth +import pytest + +from samples.snippets import sync_api + + +def test_call_sync_api(capsys: pytest.LogCaptureFixture) -> None: + _, project_id = google.auth.default() + sync_api.call_sync_api(project_id) + out, _ = capsys.readouterr() + + expected_strings = ["routes", "visits", "transitions", "metrics"] + for expected_string in expected_strings: + assert expected_string in out diff --git a/samples/snippets/sync_api_with_long_timeout.py b/samples/snippets/sync_api_with_long_timeout.py new file mode 100644 index 0000000..ba658ff --- /dev/null +++ b/samples/snippets/sync_api_with_long_timeout.py @@ -0,0 +1,51 @@ +# 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. + +# [START cloudoptimization_long_timeout] + +from google.cloud import optimization_v1 +from google.cloud.optimization_v1.services import fleet_routing +from google.cloud.optimization_v1.services.fleet_routing import transports +from google.cloud.optimization_v1.services.fleet_routing.transports import ( + grpc as fleet_routing_grpc, +) + +# TODO(developer): Uncomment these variables before running the sample. +# project_id= 'YOUR_PROJECT_ID' + + +def long_timeout(request_file_name: str, project_id: str) -> None: + with open(request_file_name, "r") as f: + fleet_routing_request = optimization_v1.OptimizeToursRequest.from_json(f.read()) + fleet_routing_request.parent = f"projects/{project_id}" + + # Create a channel to provide a connection to the Fleet Routing servers with + # custom behavior. The `grpc.keepalive_time_ms` channel argument modifies + # the channel behavior in order to send keep-alive pings every 5 minutes. + channel = fleet_routing_grpc.FleetRoutingGrpcTransport.create_channel( + options=[ + ("grpc.keepalive_time_ms", 500), + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + # Keep-alive pings are sent on the transport. Create the transport using the + # custom channel The transport is essentially a wrapper to the channel. + transport = transports.FleetRoutingGrpcTransport(channel=channel) + client = fleet_routing.client.FleetRoutingClient(transport=transport) + fleet_routing_response = client.optimize_tours(fleet_routing_request) + print(fleet_routing_response) + + +# [END cloudoptimization_long_timeout] diff --git a/samples/snippets/sync_api_with_long_timeout_test.py b/samples/snippets/sync_api_with_long_timeout_test.py new file mode 100644 index 0000000..197edd3 --- /dev/null +++ b/samples/snippets/sync_api_with_long_timeout_test.py @@ -0,0 +1,29 @@ +# 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.auth +import pytest + +from samples.snippets import sync_api_with_long_timeout + + +def test_long_timeout(capsys: pytest.LogCaptureFixture) -> None: + request_file_name = "resources/sync_request.json" + _, project_id = google.auth.default() + sync_api_with_long_timeout.long_timeout(request_file_name, project_id) + out, _ = capsys.readouterr() + + expected_strings = ["routes", "visits", "transitions", "metrics"] + for expected_string in expected_strings: + assert expected_string in out diff --git a/setup.py b/setup.py index cc9c420..0b2eca2 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ name = "google-cloud-optimization" description = "Cloud Optimization API client library" -version = "0.1.0" +version = "0.1.1" release_status = "Development Status :: 4 - Beta" url = "https://github.com/googleapis/python-optimization" dependencies = [ diff --git a/tests/unit/gapic/optimization_v1/test_fleet_routing.py b/tests/unit/gapic/optimization_v1/test_fleet_routing.py index 0d3d4db..cc06eb2 100644 --- a/tests/unit/gapic/optimization_v1/test_fleet_routing.py +++ b/tests/unit/gapic/optimization_v1/test_fleet_routing.py @@ -91,7 +91,10 @@ def test__get_default_mtls_endpoint(): @pytest.mark.parametrize( "client_class,transport_name", - [(FleetRoutingClient, "grpc"), (FleetRoutingAsyncClient, "grpc_asyncio"),], + [ + (FleetRoutingClient, "grpc"), + (FleetRoutingAsyncClient, "grpc_asyncio"), + ], ) def test_fleet_routing_client_from_service_account_info(client_class, transport_name): creds = ga_credentials.AnonymousCredentials() @@ -134,7 +137,10 @@ def test_fleet_routing_client_service_account_always_use_jwt( @pytest.mark.parametrize( "client_class,transport_name", - [(FleetRoutingClient, "grpc"), (FleetRoutingAsyncClient, "grpc_asyncio"),], + [ + (FleetRoutingClient, "grpc"), + (FleetRoutingAsyncClient, "grpc_asyncio"), + ], ) def test_fleet_routing_client_from_service_account_file(client_class, transport_name): creds = ga_credentials.AnonymousCredentials() @@ -494,7 +500,9 @@ def test_fleet_routing_client_client_options_scopes( client_class, transport_class, transport_name ): # Check the case scopes are provided. - options = client_options.ClientOptions(scopes=["1", "2"],) + options = client_options.ClientOptions( + scopes=["1", "2"], + ) with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None client = client_class(client_options=options, transport=transport_name) @@ -632,10 +640,17 @@ def test_fleet_routing_client_create_channel_credentials_file( ) -@pytest.mark.parametrize("request_type", [fleet_routing.OptimizeToursRequest, dict,]) +@pytest.mark.parametrize( + "request_type", + [ + fleet_routing.OptimizeToursRequest, + dict, + ], +) def test_optimize_tours(request_type, transport: str = "grpc"): client = FleetRoutingClient( - credentials=ga_credentials.AnonymousCredentials(), transport=transport, + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, @@ -646,7 +661,8 @@ def test_optimize_tours(request_type, transport: str = "grpc"): with mock.patch.object(type(client.transport.optimize_tours), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = fleet_routing.OptimizeToursResponse( - request_label="request_label_value", total_cost=0.10840000000000001, + request_label="request_label_value", + total_cost=0.10840000000000001, ) response = client.optimize_tours(request) @@ -665,7 +681,8 @@ def test_optimize_tours_empty_call(): # This test is a coverage failsafe to make sure that totally empty calls, # i.e. request == None and no flattened fields passed, work. client = FleetRoutingClient( - credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", ) # Mock the actual call within the gRPC stub, and fake the request. @@ -681,7 +698,8 @@ async def test_optimize_tours_async( transport: str = "grpc_asyncio", request_type=fleet_routing.OptimizeToursRequest ): client = FleetRoutingAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), transport=transport, + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, @@ -693,7 +711,8 @@ async def test_optimize_tours_async( # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( fleet_routing.OptimizeToursResponse( - request_label="request_label_value", total_cost=0.10840000000000001, + request_label="request_label_value", + total_cost=0.10840000000000001, ) ) response = await client.optimize_tours(request) @@ -715,7 +734,9 @@ async def test_optimize_tours_async_from_dict(): def test_optimize_tours_field_headers(): - client = FleetRoutingClient(credentials=ga_credentials.AnonymousCredentials(),) + client = FleetRoutingClient( + credentials=ga_credentials.AnonymousCredentials(), + ) # Any value that is part of the HTTP/1.1 URI should be sent as # a field header. Set these to a non-empty value. @@ -735,12 +756,17 @@ def test_optimize_tours_field_headers(): # Establish that the field header was sent. _, _, kw = call.mock_calls[0] - assert ("x-goog-request-params", "parent=parent/value",) in kw["metadata"] + assert ( + "x-goog-request-params", + "parent=parent/value", + ) in kw["metadata"] @pytest.mark.asyncio async def test_optimize_tours_field_headers_async(): - client = FleetRoutingAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + client = FleetRoutingAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) # Any value that is part of the HTTP/1.1 URI should be sent as # a field header. Set these to a non-empty value. @@ -762,15 +788,23 @@ async def test_optimize_tours_field_headers_async(): # Establish that the field header was sent. _, _, kw = call.mock_calls[0] - assert ("x-goog-request-params", "parent=parent/value",) in kw["metadata"] + assert ( + "x-goog-request-params", + "parent=parent/value", + ) in kw["metadata"] @pytest.mark.parametrize( - "request_type", [fleet_routing.BatchOptimizeToursRequest, dict,] + "request_type", + [ + fleet_routing.BatchOptimizeToursRequest, + dict, + ], ) def test_batch_optimize_tours(request_type, transport: str = "grpc"): client = FleetRoutingClient( - credentials=ga_credentials.AnonymousCredentials(), transport=transport, + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, @@ -798,7 +832,8 @@ def test_batch_optimize_tours_empty_call(): # This test is a coverage failsafe to make sure that totally empty calls, # i.e. request == None and no flattened fields passed, work. client = FleetRoutingClient( - credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", ) # Mock the actual call within the gRPC stub, and fake the request. @@ -817,7 +852,8 @@ async def test_batch_optimize_tours_async( request_type=fleet_routing.BatchOptimizeToursRequest, ): client = FleetRoutingAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), transport=transport, + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, @@ -849,7 +885,9 @@ async def test_batch_optimize_tours_async_from_dict(): def test_batch_optimize_tours_field_headers(): - client = FleetRoutingClient(credentials=ga_credentials.AnonymousCredentials(),) + client = FleetRoutingClient( + credentials=ga_credentials.AnonymousCredentials(), + ) # Any value that is part of the HTTP/1.1 URI should be sent as # a field header. Set these to a non-empty value. @@ -871,12 +909,17 @@ def test_batch_optimize_tours_field_headers(): # Establish that the field header was sent. _, _, kw = call.mock_calls[0] - assert ("x-goog-request-params", "parent=parent/value",) in kw["metadata"] + assert ( + "x-goog-request-params", + "parent=parent/value", + ) in kw["metadata"] @pytest.mark.asyncio async def test_batch_optimize_tours_field_headers_async(): - client = FleetRoutingAsyncClient(credentials=ga_credentials.AnonymousCredentials(),) + client = FleetRoutingAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) # Any value that is part of the HTTP/1.1 URI should be sent as # a field header. Set these to a non-empty value. @@ -900,7 +943,10 @@ async def test_batch_optimize_tours_field_headers_async(): # Establish that the field header was sent. _, _, kw = call.mock_calls[0] - assert ("x-goog-request-params", "parent=parent/value",) in kw["metadata"] + assert ( + "x-goog-request-params", + "parent=parent/value", + ) in kw["metadata"] def test_credentials_transport_error(): @@ -910,7 +956,8 @@ def test_credentials_transport_error(): ) with pytest.raises(ValueError): client = FleetRoutingClient( - credentials=ga_credentials.AnonymousCredentials(), transport=transport, + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, ) # It is an error to provide a credentials file and a transport instance. @@ -930,7 +977,10 @@ def test_credentials_transport_error(): options = client_options.ClientOptions() options.api_key = "api_key" with pytest.raises(ValueError): - client = FleetRoutingClient(client_options=options, transport=transport,) + client = FleetRoutingClient( + client_options=options, + transport=transport, + ) # It is an error to provide an api_key and a credential. options = mock.Mock() @@ -946,7 +996,8 @@ def test_credentials_transport_error(): ) with pytest.raises(ValueError): client = FleetRoutingClient( - client_options={"scopes": ["1", "2"]}, transport=transport, + client_options={"scopes": ["1", "2"]}, + transport=transport, ) @@ -989,10 +1040,28 @@ def test_transport_adc(transport_class): adc.assert_called_once() +@pytest.mark.parametrize( + "transport_name", + [ + "grpc", + ], +) +def test_transport_kind(transport_name): + transport = FleetRoutingClient.get_transport_class(transport_name)( + credentials=ga_credentials.AnonymousCredentials(), + ) + assert transport.kind == transport_name + + def test_transport_grpc_default(): # A client should use the gRPC transport by default. - client = FleetRoutingClient(credentials=ga_credentials.AnonymousCredentials(),) - assert isinstance(client.transport, transports.FleetRoutingGrpcTransport,) + client = FleetRoutingClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + assert isinstance( + client.transport, + transports.FleetRoutingGrpcTransport, + ) def test_fleet_routing_base_transport_error(): @@ -1032,6 +1101,14 @@ def test_fleet_routing_base_transport(): with pytest.raises(NotImplementedError): transport.operations_client + # Catch all for all remaining methods and properties + remainder = [ + "kind", + ] + for r in remainder: + with pytest.raises(NotImplementedError): + getattr(transport, r)() + def test_fleet_routing_base_transport_with_credentials_file(): # Instantiate the base transport with a credentials file @@ -1043,7 +1120,8 @@ def test_fleet_routing_base_transport_with_credentials_file(): Transport.return_value = None load_creds.return_value = (ga_credentials.AnonymousCredentials(), None) transport = transports.FleetRoutingTransport( - credentials_file="credentials.json", quota_project_id="octopus", + credentials_file="credentials.json", + quota_project_id="octopus", ) load_creds.assert_called_once_with( "credentials.json", @@ -1173,7 +1251,13 @@ def test_fleet_routing_grpc_transport_client_cert_source_for_mtls(transport_clas ) -@pytest.mark.parametrize("transport_name", ["grpc", "grpc_asyncio",]) +@pytest.mark.parametrize( + "transport_name", + [ + "grpc", + "grpc_asyncio", + ], +) def test_fleet_routing_host_no_port(transport_name): client = FleetRoutingClient( credentials=ga_credentials.AnonymousCredentials(), @@ -1185,7 +1269,13 @@ def test_fleet_routing_host_no_port(transport_name): assert client.transport._host == ("cloudoptimization.googleapis.com:443") -@pytest.mark.parametrize("transport_name", ["grpc", "grpc_asyncio",]) +@pytest.mark.parametrize( + "transport_name", + [ + "grpc", + "grpc_asyncio", + ], +) def test_fleet_routing_host_with_port(transport_name): client = FleetRoutingClient( credentials=ga_credentials.AnonymousCredentials(), @@ -1202,7 +1292,8 @@ def test_fleet_routing_grpc_transport_channel(): # Check that channel is used if provided. transport = transports.FleetRoutingGrpcTransport( - host="squid.clam.whelk", channel=channel, + host="squid.clam.whelk", + channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" @@ -1214,7 +1305,8 @@ def test_fleet_routing_grpc_asyncio_transport_channel(): # Check that channel is used if provided. transport = transports.FleetRoutingGrpcAsyncIOTransport( - host="squid.clam.whelk", channel=channel, + host="squid.clam.whelk", + channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" @@ -1315,12 +1407,16 @@ def test_fleet_routing_transport_channel_mtls_with_adc(transport_class): def test_fleet_routing_grpc_lro_client(): client = FleetRoutingClient( - credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", ) transport = client.transport # Ensure that we have a api-core operations client. - assert isinstance(transport.operations_client, operations_v1.OperationsClient,) + assert isinstance( + transport.operations_client, + operations_v1.OperationsClient, + ) # Ensure that subsequent calls to the property send the exact same object. assert transport.operations_client is transport.operations_client @@ -1328,12 +1424,16 @@ def test_fleet_routing_grpc_lro_client(): def test_fleet_routing_grpc_lro_async_client(): client = FleetRoutingAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), transport="grpc_asyncio", + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", ) transport = client.transport # Ensure that we have a api-core operations client. - assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) + assert isinstance( + transport.operations_client, + operations_v1.OperationsAsyncClient, + ) # Ensure that subsequent calls to the property send the exact same object. assert transport.operations_client is transport.operations_client @@ -1361,7 +1461,9 @@ def test_parse_common_billing_account_path(): def test_common_folder_path(): folder = "whelk" - expected = "folders/{folder}".format(folder=folder,) + expected = "folders/{folder}".format( + folder=folder, + ) actual = FleetRoutingClient.common_folder_path(folder) assert expected == actual @@ -1379,7 +1481,9 @@ def test_parse_common_folder_path(): def test_common_organization_path(): organization = "oyster" - expected = "organizations/{organization}".format(organization=organization,) + expected = "organizations/{organization}".format( + organization=organization, + ) actual = FleetRoutingClient.common_organization_path(organization) assert expected == actual @@ -1397,7 +1501,9 @@ def test_parse_common_organization_path(): def test_common_project_path(): project = "cuttlefish" - expected = "projects/{project}".format(project=project,) + expected = "projects/{project}".format( + project=project, + ) actual = FleetRoutingClient.common_project_path(project) assert expected == actual @@ -1417,7 +1523,8 @@ def test_common_location_path(): project = "winkle" location = "nautilus" expected = "projects/{project}/locations/{location}".format( - project=project, location=location, + project=project, + location=location, ) actual = FleetRoutingClient.common_location_path(project, location) assert expected == actual @@ -1442,7 +1549,8 @@ def test_client_with_default_client_info(): transports.FleetRoutingTransport, "_prep_wrapped_messages" ) as prep: client = FleetRoutingClient( - credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + credentials=ga_credentials.AnonymousCredentials(), + client_info=client_info, ) prep.assert_called_once_with(client_info) @@ -1451,7 +1559,8 @@ def test_client_with_default_client_info(): ) as prep: transport_class = FleetRoutingClient.get_transport_class() transport = transport_class( - credentials=ga_credentials.AnonymousCredentials(), client_info=client_info, + credentials=ga_credentials.AnonymousCredentials(), + client_info=client_info, ) prep.assert_called_once_with(client_info) @@ -1459,7 +1568,8 @@ def test_client_with_default_client_info(): @pytest.mark.asyncio async def test_transport_close_async(): client = FleetRoutingAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), transport="grpc_asyncio", + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", ) with mock.patch.object( type(getattr(client.transport, "grpc_channel")), "close"