Skip to content

Commit

Permalink
Fix type hints and discovered bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
plamut committed Nov 9, 2021
1 parent 12c2272 commit 859a65d
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 84 deletions.
4 changes: 2 additions & 2 deletions google/cloud/bigquery/_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ def __init__(self, client, client_info=None, api_endpoint=None):
self._client_info.gapic_version = __version__
self._client_info.client_library_version = __version__

API_VERSION = "v2"
API_VERSION = "v2" # type: ignore
"""The version of the API, used in building the API call's URL."""

API_URL_TEMPLATE = "{api_base_url}/bigquery/{api_version}{path}"
API_URL_TEMPLATE = "{api_base_url}/bigquery/{api_version}{path}" # type: ignore
"""A template for the URL of a particular API call."""
4 changes: 2 additions & 2 deletions google/cloud/bigquery/_pandas_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
else:
import numpy

import pyarrow
import pyarrow.parquet
import pyarrow # type: ignore
import pyarrow.parquet # type: ignore

try:
# _BaseGeometry is used to detect shapely objevys in `bq_to_arrow_array`
Expand Down
2 changes: 1 addition & 1 deletion google/cloud/bigquery/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class Client(ClientWithProject):
to acquire default credentials.
"""

SCOPE = (
SCOPE = ( # type: ignore
"https://www.googleapis.com/auth/bigquery",
"https://www.googleapis.com/auth/cloud-platform",
)
Expand Down
99 changes: 61 additions & 38 deletions google/cloud/bigquery/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@

import copy
import datetime
import typing
from typing import Any, Dict, Optional, Sequence, Union

import google.cloud._helpers # type: ignore
from google.cloud.bigquery import _helpers
from google.cloud.bigquery import standard_sql
from google.cloud.bigquery.encryption_configuration import EncryptionConfiguration


Expand Down Expand Up @@ -70,55 +70,63 @@ def reference(self) -> Optional["ModelReference"]:
Read-only.
"""
resource = self._properties.get("modelReference")
if resource is not None:
if resource is None:
return None
else:
return ModelReference.from_api_repr(resource)

@property
def project(self) -> str:
def project(self) -> Optional[str]:
"""Project bound to the model."""
return self.reference.project
ref = self.reference
return ref.project if ref is not None else None

@property
def dataset_id(self) -> str:
def dataset_id(self) -> Optional[str]:
"""ID of dataset containing the model."""
return self.reference.dataset_id
ref = self.reference
return ref.dataset_id if ref is not None else None

@property
def model_id(self) -> str:
def model_id(self) -> Optional[str]:
"""The model ID."""
return self.reference.model_id
ref = self.reference
return ref.model_id if ref is not None else None

@property
def path(self) -> str:
def path(self) -> Optional[str]:
"""URL path for the model's APIs."""
return self.reference.path
ref = self.reference
return ref.path if ref is not None else None

@property
def location(self) -> str:
def location(self) -> Optional[str]:
"""The geographic location where the model resides.
This value is inherited from the dataset.
Read-only.
"""
return self._properties.get("location")
return typing.cast(Optional[str], self._properties.get("location"))

@property
def etag(self) -> str:
def etag(self) -> Optional[str]:
"""ETag for the model resource (:data:`None` until set from the server).
Read-only.
"""
return self._properties.get("etag")
return typing.cast(Optional[str], self._properties.get("etag"))

@property
def created(self) -> Optional[datetime.datetime]:
"""Datetime at which the model was created (:data:`None` until set from the server).
Read-only.
"""
value = self._properties.get("creationTime")
if value is not None:
value = typing.cast(Optional[float], self._properties.get("creationTime"))
if value is None:
return None
else:
# value will be in milliseconds.
return google.cloud._helpers._datetime_from_microseconds(
1000.0 * float(value)
Expand All @@ -130,8 +138,10 @@ def modified(self) -> Optional[datetime.datetime]:
Read-only.
"""
value = value = self._properties.get("lastModifiedTime")
if value is not None:
value = typing.cast(Optional[float], self._properties.get("lastModifiedTime"))
if value is None:
return None
else:
# value will be in milliseconds.
return google.cloud._helpers._datetime_from_microseconds(
1000.0 * float(value)
Expand All @@ -143,7 +153,9 @@ def model_type(self) -> str:
Read-only.
"""
return self._properties.get("modelType", "MODEL_TYPE_UNSPECIFIED")
return typing.cast(
str, self._properties.get("modelType", "MODEL_TYPE_UNSPECIFIED")
)

@property
def training_runs(self) -> Sequence[Dict[str, Any]]:
Expand All @@ -154,25 +166,31 @@ def training_runs(self) -> Sequence[Dict[str, Any]]:
Read-only.
"""
return self._properties.get("trainingRuns", [])
return typing.cast(
Sequence[Dict[str, Any]], self._properties.get("trainingRuns", [])
)

@property
def feature_columns(self) -> Sequence[standard_sql.StandardSqlField]:
def feature_columns(self) -> Sequence[Dict[str, Any]]:
"""Input feature columns that were used to train this model.
Read-only.
"""
return self._properties.get("featureColumns", [])
return typing.cast(
Sequence[Dict[str, Any]], self._properties.get("featureColumns", [])
)

@property
def label_columns(self) -> Sequence[standard_sql.StandardSqlField]:
def label_columns(self) -> Sequence[Dict[str, Any]]:
"""Label columns that were used to train this model.
The output of the model will have a ``predicted_`` prefix to these columns.
Read-only.
"""
return self._properties.get("labelColumns", [])
return typing.cast(
Sequence[Dict[str, Any]], self._properties.get("labelColumns", [])
)

@property
def best_trial_id(self) -> Optional[int]:
Expand All @@ -183,7 +201,7 @@ def best_trial_id(self) -> Optional[int]:
Read-only.
"""
value = self._properties.get("bestTrialId")
value = typing.cast(Optional[int], self._properties.get("bestTrialId"))
if value is not None:
value = int(value)
return value
Expand All @@ -195,36 +213,43 @@ def expires(self) -> Optional[datetime.datetime]:
If not present, the model will persist indefinitely. Expired models will be
deleted and their storage reclaimed.
"""
value = self._properties.get("expirationTime")
if value is not None:
value = typing.cast(Optional[float], self._properties.get("expirationTime"))
if value is None:
return None
else:
# value will be in milliseconds.
return google.cloud._helpers._datetime_from_microseconds(
1000.0 * float(value)
)

@expires.setter
def expires(self, value: Optional[datetime.datetime]):
if value is not None:
value = str(google.cloud._helpers._millis_from_datetime(value))
self._properties["expirationTime"] = value
if value is None:
value_to_store: Optional[str] = None
else:
value_to_store = str(google.cloud._helpers._millis_from_datetime(value))
# TODO: Consider using typing.TypedDict when only Python 3.8+ is supported.
self._properties["expirationTime"] = value_to_store # type: ignore

@property
def description(self) -> Optional[str]:
"""Description of the model (defaults to :data:`None`)."""
return self._properties.get("description")
return typing.cast(Optional[str], self._properties.get("description"))

@description.setter
def description(self, value: Optional[str]):
self._properties["description"] = value
# TODO: Consider using typing.TypedDict when only Python 3.8+ is supported.
self._properties["description"] = value # type: ignore

@property
def friendly_name(self) -> Optional[str]:
"""Title of the table (defaults to :data:`None`)."""
return self._properties.get("friendlyName")
return typing.cast(Optional[str], self._properties.get("friendlyName"))

@friendly_name.setter
def friendly_name(self, value: Optional[str]):
self._properties["friendlyName"] = value
# TODO: Consider using typing.TypedDict when only Python 3.8+ is supported.
self._properties["friendlyName"] = value # type: ignore

@property
def labels(self) -> Dict[str, str]:
Expand Down Expand Up @@ -256,13 +281,11 @@ def encryption_configuration(self) -> Optional[EncryptionConfiguration]:
prop = self._properties.get("encryptionConfiguration")
if prop:
prop = EncryptionConfiguration.from_api_repr(prop)
return prop
return typing.cast(Optional[EncryptionConfiguration], prop)

@encryption_configuration.setter
def encryption_configuration(self, value: Optional[EncryptionConfiguration]):
api_repr = value
if value:
api_repr = value.to_api_repr()
api_repr = value.to_api_repr() if value else value
self._properties["encryptionConfiguration"] = api_repr

@classmethod
Expand Down
5 changes: 1 addition & 4 deletions google/cloud/bigquery/routine/routine.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,7 @@ def return_type(self):

@return_type.setter
def return_type(self, value: StandardSqlDataType):
if value:
resource = value.to_api_repr()
else:
resource = None
resource = None if not value else value.to_api_repr()
self._properties[self._PROPERTY_TO_API_FIELD["return_type"]] = resource

@property
Expand Down
16 changes: 0 additions & 16 deletions google/cloud/bigquery/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,22 +66,6 @@ class _DefaultSentinel(enum.Enum):
_DEFAULT_VALUE = _DefaultSentinel.DEFAULT_VALUE


class _DefaultSentinel(enum.Enum):
"""Object used as 'sentinel' indicating default value should be used.
Uses enum so that pytype/mypy knows that this is the only possible value.
https://stackoverflow.com/a/60605919/101923
Literal[_DEFAULT_VALUE] is an alternative, but only added in Python 3.8.
https://docs.python.org/3/library/typing.html#typing.Literal
"""

DEFAULT_VALUE = object()


_DEFAULT_VALUE = _DefaultSentinel.DEFAULT_VALUE


class SchemaField(object):
"""Describe a single field within a table schema.
Expand Down
Loading

0 comments on commit 859a65d

Please sign in to comment.