Просмотр исходного кода

Type the GCP provider (pragmatic tier) + reconcile its inconsistencies

Annotate cloudbridge/providers/gcp/ (~415 callables) and add providers.gcp.*
to the pragmatic mypy tier. Conform implementations to the corrected interface
and apply the user's decisions for the new inconsistencies GCP surfaced.

Interface/base widenings (genuinely-optional values that providers expose):
- VMFirewallRule.protocol -> str | None (a rule may cover all protocols).
- Instance.key_pair_id -> str | None (an instance may have no key pair).
- AttachmentInfo.device -> str | None (GCP disks expose no device name);
  BaseAttachmentInfo accepts device: str | None.
- Router.subnets -> Iterable[Subnet] (was list[Subnet]) so a provider may
  return its SubnetSubService; AWS/OpenStack lists remain valid covariant
  returns.

GCP conformance:
- Required getters raise ProviderInternalException when absent
  (Instance.vm_type, Instance.image_id) rather than returning None.
- create_image and all *Service.create error paths raise instead of returning
  None (a create returns the resource or raises).
- Implement GCPInstance.start() (mirrors stop()); it was missing, which had
  forced type: ignore[abstract] at the instantiation sites.
- start/stop/reboot/delete/attach*/detach* -> None; upload* -> BucketObject;
  find() wraps in ClientPagedResultList; DnsRecord.data -> list[str];
  Volume.attachments -> AttachmentInfo | None.
- GCP-specific provider members reached via cast("GCPCloudProvider", ...).

Verified: mypy green (43 files), flake8 clean, GCP modules import under the GCP
SDK env. (No AWS runtime change this round; interface/base edits are
annotation-only.)
Nuwan Goonasekera 9 часов назад
Родитель
Сommit
05774f42b9

+ 3 - 2
cloudbridge/base/resources.py

@@ -468,7 +468,8 @@ class BaseMachineImage(
 
 
 class BaseAttachmentInfo(AttachmentInfo):
 class BaseAttachmentInfo(AttachmentInfo):
 
 
-    def __init__(self, volume: Volume, instance_id: str, device: str) -> None:
+    def __init__(self, volume: Volume, instance_id: str,
+                 device: str | None) -> None:
         self._volume = volume
         self._volume = volume
         self._instance_id = instance_id
         self._instance_id = instance_id
         self._device = device
         self._device = device
@@ -482,7 +483,7 @@ class BaseAttachmentInfo(AttachmentInfo):
         return self._instance_id
         return self._instance_id
 
 
     @property
     @property
-    def device(self) -> str:
+    def device(self) -> str | None:
         return self._device
         return self._device
 
 
 
 

+ 4 - 4
cloudbridge/interfaces/resources.py

@@ -691,7 +691,7 @@ class Instance(ObjectLifeCycleMixin, LabeledCloudResource):
         pass
         pass
 
 
     @abstractproperty
     @abstractproperty
-    def key_pair_id(self) -> str:
+    def key_pair_id(self) -> str | None:
         """
         """
         Get the id of the key pair associated with this instance.
         Get the id of the key pair associated with this instance.
 
 
@@ -1268,7 +1268,7 @@ class Router(LabeledCloudResource):
         pass
         pass
 
 
     @abstractproperty
     @abstractproperty
-    def subnets(self) -> list[Subnet]:
+    def subnets(self) -> Iterable[Subnet]:
         """
         """
         List of subnets attached to this router.
         List of subnets attached to this router.
 
 
@@ -1490,7 +1490,7 @@ class AttachmentInfo(object):
         pass
         pass
 
 
     @abstractproperty
     @abstractproperty
-    def device(self) -> str:
+    def device(self) -> str | None:
         """
         """
         Get the device the volume is mapped as.
         Get the device the volume is mapped as.
 
 
@@ -2082,7 +2082,7 @@ class VMFirewallRule(CloudResource):
         pass
         pass
 
 
     @abstractproperty
     @abstractproperty
-    def protocol(self) -> str:
+    def protocol(self) -> str | None:
         """
         """
         IP protocol used. Either ``tcp`` | ``udp`` | ``icmp``.
         IP protocol used. Either ``tcp`` | ``udp`` | ``icmp``.
 
 

+ 35 - 19
cloudbridge/providers/gcp/helpers.py

@@ -3,6 +3,10 @@ import collections
 import datetime
 import datetime
 import hashlib
 import hashlib
 import re
 import re
+from typing import Any
+from typing import Callable
+from typing import Iterator
+from typing import TYPE_CHECKING
 from urllib.parse import quote
 from urllib.parse import quote
 
 
 from googleapiclient.errors import HttpError
 from googleapiclient.errors import HttpError
@@ -11,12 +15,15 @@ import tenacity
 
 
 from cloudbridge.interfaces.exceptions import ProviderInternalException
 from cloudbridge.interfaces.exceptions import ProviderInternalException
 
 
+if TYPE_CHECKING:
+    from .provider import GCPCloudProvider
 
 
-def gcp_projects(provider):
+
+def gcp_projects(provider: "GCPCloudProvider") -> Any:
     return provider.gcp_compute.projects()
     return provider.gcp_compute.projects()
 
 
 
 
-def iter_all(resource, **kwargs):
+def iter_all(resource: Any, **kwargs: Any) -> Iterator[Any]:
     token = None
     token = None
     while True:
     while True:
         response = resource.list(pageToken=token, **kwargs).execute()
         response = resource.list(pageToken=token, **kwargs).execute()
@@ -27,7 +34,7 @@ def iter_all(resource, **kwargs):
         token = response['nextPageToken']
         token = response['nextPageToken']
 
 
 
 
-def get_common_metadata(provider):
+def get_common_metadata(provider: "GCPCloudProvider") -> Any:
     """
     """
     Get a project's commonInstanceMetadata entry
     Get a project's commonInstanceMetadata entry
     """
     """
@@ -36,7 +43,7 @@ def get_common_metadata(provider):
     return metadata["commonInstanceMetadata"]
     return metadata["commonInstanceMetadata"]
 
 
 
 
-def __if_fingerprint_differs(e):
+def __if_fingerprint_differs(e: BaseException) -> bool:
     # return True if the CloudError exception is due to subnet being in use
     # return True if the CloudError exception is due to subnet being in use
     if isinstance(e, HttpError):
     if isinstance(e, HttpError):
         expected_message = 'Supplied fingerprint does not match current ' \
         expected_message = 'Supplied fingerprint does not match current ' \
@@ -50,7 +57,8 @@ def __if_fingerprint_differs(e):
                 retry=tenacity.retry_if_exception(__if_fingerprint_differs),
                 retry=tenacity.retry_if_exception(__if_fingerprint_differs),
                 wait=tenacity.wait_exponential(max=10),
                 wait=tenacity.wait_exponential(max=10),
                 reraise=True)
                 reraise=True)
-def gcp_metadata_save_op(provider, callback):
+def gcp_metadata_save_op(provider: "GCPCloudProvider",
+                         callback: Callable[[Any], Any]) -> None:
     """
     """
     Carries out a metadata save operation. In GCP, a fingerprint based
     Carries out a metadata save operation. In GCP, a fingerprint based
     locking mechanism is used to prevent lost updates. A new fingerprint
     locking mechanism is used to prevent lost updates. A new fingerprint
@@ -59,7 +67,7 @@ def gcp_metadata_save_op(provider, callback):
     metadata, and saves the metadata using the original fingerprint
     metadata, and saves the metadata using the original fingerprint
     immediately afterwards, ensuring that update conflicts can be detected.
     immediately afterwards, ensuring that update conflicts can be detected.
     """
     """
-    def _save_common_metadata(provider):
+    def _save_common_metadata(provider: "GCPCloudProvider") -> None:
         # get the latest metadata (so we get the latest fingerprint)
         # get the latest metadata (so we get the latest fingerprint)
         metadata = get_common_metadata(provider)
         metadata = get_common_metadata(provider)
         # allow callback to do processing on it
         # allow callback to do processing on it
@@ -73,8 +81,9 @@ def gcp_metadata_save_op(provider, callback):
     _save_common_metadata(provider)
     _save_common_metadata(provider)
 
 
 
 
-def modify_or_add_metadata_item(provider, key, value):
-    def _update_metadata_key(metadata):
+def modify_or_add_metadata_item(provider: "GCPCloudProvider", key: str,
+                                value: str) -> None:
+    def _update_metadata_key(metadata: Any) -> None:
         entries = [item for item in metadata.get('items', [])
         entries = [item for item in metadata.get('items', [])
                    if item['key'] == key]
                    if item['key'] == key]
         if entries:
         if entries:
@@ -92,8 +101,9 @@ def modify_or_add_metadata_item(provider, key, value):
 # This function will raise an HttpError with message containing
 # This function will raise an HttpError with message containing
 # "Metadata has duplicate key" if it's not unique, unlike the previous
 # "Metadata has duplicate key" if it's not unique, unlike the previous
 # method which either adds or updates the value corresponding to that key
 # method which either adds or updates the value corresponding to that key
-def add_metadata_item(provider, key, value):
-    def _add_metadata_key(metadata):
+def add_metadata_item(provider: "GCPCloudProvider", key: str,
+                      value: str) -> None:
+    def _add_metadata_key(metadata: Any) -> None:
         entry = {'key': key, 'value': value}
         entry = {'key': key, 'value': value}
         entries = metadata.get('items', [])
         entries = metadata.get('items', [])
         entries.append(entry)
         entries.append(entry)
@@ -104,7 +114,9 @@ def add_metadata_item(provider, key, value):
     gcp_metadata_save_op(provider, _add_metadata_key)
     gcp_metadata_save_op(provider, _add_metadata_key)
 
 
 
 
-def find_matching_metadata_items(provider, key_regex):
+def find_matching_metadata_items(provider: "GCPCloudProvider",
+                                 key_regex: str | re.Pattern[str]
+                                 ) -> list[Any]:
     metadata = get_common_metadata(provider)
     metadata = get_common_metadata(provider)
     items = metadata.get('items', [])
     items = metadata.get('items', [])
     if not items:
     if not items:
@@ -113,7 +125,7 @@ def find_matching_metadata_items(provider, key_regex):
             if re.search(key_regex, item['key'])]
             if re.search(key_regex, item['key'])]
 
 
 
 
-def get_metadata_item_value(provider, key):
+def get_metadata_item_value(provider: "GCPCloudProvider", key: str) -> Any:
     metadata = get_common_metadata(provider)
     metadata = get_common_metadata(provider)
     entries = [item['value'] for item in metadata.get('items', [])
     entries = [item['value'] for item in metadata.get('items', [])
                if item['key'] == key]
                if item['key'] == key]
@@ -123,8 +135,8 @@ def get_metadata_item_value(provider, key):
         return None
         return None
 
 
 
 
-def remove_metadata_item(provider, key):
-    def _remove_metadata_by_key(metadata):
+def remove_metadata_item(provider: "GCPCloudProvider", key: str) -> bool:
+    def _remove_metadata_by_key(metadata: Any) -> bool | None:
         items = metadata.get('items', [])
         items = metadata.get('items', [])
         # No metadata to delete
         # No metadata to delete
         if not items:
         if not items:
@@ -144,12 +156,13 @@ def remove_metadata_item(provider, key):
 
 
             else:
             else:
                 metadata['items'] = entries
                 metadata['items'] = entries
+                return None
 
 
     gcp_metadata_save_op(provider, _remove_metadata_by_key)
     gcp_metadata_save_op(provider, _remove_metadata_by_key)
     return True
     return True
 
 
 
 
-def __if_label_fingerprint_differs(e):
+def __if_label_fingerprint_differs(e: BaseException) -> bool:
     # return True if the CloudError exception is due to subnet being in use
     # return True if the CloudError exception is due to subnet being in use
     if isinstance(e, HttpError):
     if isinstance(e, HttpError):
         expected_message = 'Labels fingerprint either invalid or ' \
         expected_message = 'Labels fingerprint either invalid or ' \
@@ -164,7 +177,8 @@ def __if_label_fingerprint_differs(e):
                     __if_label_fingerprint_differs),
                     __if_label_fingerprint_differs),
                 wait=tenacity.wait_exponential(max=10),
                 wait=tenacity.wait_exponential(max=10),
                 reraise=True)
                 reraise=True)
-def change_label(resource, key, value, res_att, request):
+def change_label(resource: Any, key: str, value: str, res_att: str,
+                 request: Any) -> None:
     resource.assert_valid_resource_label(value)
     resource.assert_valid_resource_label(value)
     labels = getattr(resource, res_att).get("labels", {})
     labels = getattr(resource, res_att).get("labels", {})
     labels[key] = str(value)
     labels[key] = str(value)
@@ -188,9 +202,11 @@ def change_label(resource, key, value, res_att, request):
 
 
 
 
 # https://cloud.google.com/storage/docs/access-control/signing-urls-manually#python-sample
 # https://cloud.google.com/storage/docs/access-control/signing-urls-manually#python-sample
-def generate_signed_url(credentials, bucket_name, object_name,
-                        subresource=None, expiration=604800, http_method='GET',
-                        query_parameters=None, headers=None):
+def generate_signed_url(credentials: Any, bucket_name: str, object_name: str,
+                        subresource: str | None = None,
+                        expiration: int = 604800, http_method: str = 'GET',
+                        query_parameters: dict[str, Any] | None = None,
+                        headers: dict[str, str] | None = None) -> str:
 
 
     if expiration > 604800:
     if expiration > 604800:
         # max allowed expiration time is 7 days
         # max allowed expiration time is 7 days

+ 52 - 41
cloudbridge/providers/gcp/provider.py

@@ -8,8 +8,13 @@ import os
 import re
 import re
 import time
 import time
 from string import Template
 from string import Template
+from typing import Any
+from typing import Callable
 
 
 import google.auth
 import google.auth
+from google.auth.credentials import with_scopes_if_required
+from google.oauth2.service_account import Credentials
+
 import google_auth_httplib2
 import google_auth_httplib2
 
 
 import googleapiclient
 import googleapiclient
@@ -17,12 +22,13 @@ from googleapiclient import discovery
 
 
 import httplib2
 import httplib2
 
 
-from google.auth.credentials import with_scopes_if_required
-
-from google.oauth2.service_account import Credentials
-
 from cloudbridge.base import BaseCloudProvider
 from cloudbridge.base import BaseCloudProvider
 from cloudbridge.interfaces.exceptions import ProviderConnectionException
 from cloudbridge.interfaces.exceptions import ProviderConnectionException
+from cloudbridge.interfaces.services import ComputeService
+from cloudbridge.interfaces.services import DnsService
+from cloudbridge.interfaces.services import NetworkingService
+from cloudbridge.interfaces.services import SecurityService
+from cloudbridge.interfaces.services import StorageService
 
 
 from .services import GCPComputeService
 from .services import GCPComputeService
 from .services import GCPDnsService
 from .services import GCPDnsService
@@ -37,12 +43,12 @@ CLOUD_SCOPES = ['https://www.googleapis.com/auth/cloud-platform']
 
 
 class GCPResourceUrl(object):
 class GCPResourceUrl(object):
 
 
-    def __init__(self, resource, connection):
+    def __init__(self, resource: str, connection: Any) -> None:
         self._resource = resource
         self._resource = resource
         self._connection = connection
         self._connection = connection
-        self.parameters = {}
+        self.parameters: dict[str, Any] = {}
 
 
-    def get_resource(self):
+    def get_resource(self) -> Any:
         """
         """
         The format of the returned resource is explained in details in
         The format of the returned resource is explained in details in
         https://cloud.google.com/compute/docs/reference/latest/ and
         https://cloud.google.com/compute/docs/reference/latest/ and
@@ -71,7 +77,7 @@ class GCPResourceUrl(object):
 
 
 class GCPResources(object):
 class GCPResources(object):
 
 
-    def __init__(self, connection, **kwargs):
+    def __init__(self, connection: Any, **kwargs: Any) -> None:
         self._connection = connection
         self._connection = connection
         self._parameter_defaults = kwargs
         self._parameter_defaults = kwargs
 
 
@@ -116,7 +122,7 @@ class GCPResources(object):
         self.RESOURCE_REGEX = re.compile(
         self.RESOURCE_REGEX = re.compile(
             r"(https://.*\.googleapis\.com/{0})(.*)".format(
             r"(https://.*\.googleapis\.com/{0})(.*)".format(
                 desc['servicePath']))
                 desc['servicePath']))
-        self._resources = {}
+        self._resources: dict[str, dict[str, Any]] = {}
 
 
         # We will not mutate self._desc; it's OK to use items() in Python 2.x.
         # We will not mutate self._desc; it's OK to use items() in Python 2.x.
         for resource, resource_desc in desc['resources'].items():
         for resource, resource_desc in desc['resources'].items():
@@ -149,7 +155,7 @@ class GCPResources(object):
             self._resources[resource] = {'parameters': parameters,
             self._resources[resource] = {'parameters': parameters,
                                          'pattern': re.compile(pattern)}
                                          'pattern': re.compile(pattern)}
 
 
-    def parse_url(self, url):
+    def parse_url(self, url: str) -> "GCPResourceUrl | None":
         """
         """
         Build a GCPResourceUrl from a resource's URL string. One can then call
         Build a GCPResourceUrl from a resource's URL string. One can then call
         the get() method on the returned object to fetch resource details from
         the get() method on the returned object to fetch resource details from
@@ -180,8 +186,11 @@ class GCPResources(object):
             for index, parameter in enumerate(desc['parameters']):
             for index, parameter in enumerate(desc['parameters']):
                 out.parameters[parameter] = m.group(index + 1)
                 out.parameters[parameter] = m.group(index + 1)
             return out
             return out
+        return None
 
 
-    def get_resource_url_with_default(self, resource, url_or_name, **kwargs):
+    def get_resource_url_with_default(
+            self, resource: str, url_or_name: str,
+            **kwargs: Any) -> "GCPResourceUrl | None":
         """
         """
         Build a GCPResourceUrl from a service's name and resource url or name.
         Build a GCPResourceUrl from a service's name and resource url or name.
         If the url_or_name is a valid GCP resource URL, then we build the
         If the url_or_name is a valid GCP resource URL, then we build the
@@ -210,7 +219,7 @@ class GCPResources(object):
 class GCPCloudProvider(BaseCloudProvider):
 class GCPCloudProvider(BaseCloudProvider):
     PROVIDER_ID = 'gcp'
     PROVIDER_ID = 'gcp'
 
 
-    def __init__(self, config):
+    def __init__(self, config: dict[str, Any]) -> None:
         super(GCPCloudProvider, self).__init__(config)
         super(GCPCloudProvider, self).__init__(config)
 
 
         # Disable warnings about file_cache not being available when using
         # Disable warnings about file_cache not being available when using
@@ -248,12 +257,12 @@ class GCPCloudProvider(BaseCloudProvider):
             self.project_name = os.environ.get('GCP_PROJECT_NAME')
             self.project_name = os.environ.get('GCP_PROJECT_NAME')
 
 
         # service connections, lazily initialized
         # service connections, lazily initialized
-        self._gcp_compute = None
-        self._gcp_storage = None
-        self._gcp_dns = None
-        self._compute_resources_cache = None
-        self._storage_resources_cache = None
-        self._dns_resources_cache = None
+        self._gcp_compute: Any = None
+        self._gcp_storage: Any = None
+        self._gcp_dns: Any = None
+        self._compute_resources_cache: GCPResources | None = None
+        self._storage_resources_cache: GCPResources | None = None
+        self._dns_resources_cache: GCPResources | None = None
 
 
         # Initialize provider services
         # Initialize provider services
         self._compute = GCPComputeService(self)
         self._compute = GCPComputeService(self)
@@ -265,49 +274,49 @@ class GCPCloudProvider(BaseCloudProvider):
     # Override base class implementation because it will cause
     # Override base class implementation because it will cause
     # an infinite loop
     # an infinite loop
     @property
     @property
-    def zone_name(self):
+    def zone_name(self) -> str | None:
         return self._zone_name
         return self._zone_name
 
 
     @property
     @property
-    def compute(self):
+    def compute(self) -> ComputeService:
         return self._compute
         return self._compute
 
 
     @property
     @property
-    def networking(self):
+    def networking(self) -> NetworkingService:
         return self._networking
         return self._networking
 
 
     @property
     @property
-    def security(self):
+    def security(self) -> SecurityService:
         return self._security
         return self._security
 
 
     @property
     @property
-    def storage(self):
+    def storage(self) -> StorageService:
         return self._storage
         return self._storage
 
 
     @property
     @property
-    def dns(self):
+    def dns(self) -> DnsService:
         return self._dns
         return self._dns
 
 
     @property
     @property
-    def gcp_compute(self):
+    def gcp_compute(self) -> Any:
         if not self._gcp_compute:
         if not self._gcp_compute:
             self._gcp_compute = self._connect_gcp_compute()
             self._gcp_compute = self._connect_gcp_compute()
         return self._gcp_compute
         return self._gcp_compute
 
 
     @property
     @property
-    def gcp_storage(self):
+    def gcp_storage(self) -> Any:
         if not self._gcp_storage:
         if not self._gcp_storage:
             self._gcp_storage = self._connect_gcp_storage()
             self._gcp_storage = self._connect_gcp_storage()
         return self._gcp_storage
         return self._gcp_storage
 
 
     @property
     @property
-    def gcp_dns(self):
+    def gcp_dns(self) -> Any:
         if not self._gcp_dns:
         if not self._gcp_dns:
             self._gcp_dns = self._connect_gcp_dns()
             self._gcp_dns = self._connect_gcp_dns()
         return self._gcp_dns
         return self._gcp_dns
 
 
     @property
     @property
-    def _compute_resources(self):
+    def _compute_resources(self) -> GCPResources:
         if not self._compute_resources_cache:
         if not self._compute_resources_cache:
             self._compute_resources_cache = GCPResources(
             self._compute_resources_cache = GCPResources(
                 self.gcp_compute,
                 self.gcp_compute,
@@ -317,13 +326,13 @@ class GCPCloudProvider(BaseCloudProvider):
         return self._compute_resources_cache
         return self._compute_resources_cache
 
 
     @property
     @property
-    def _storage_resources(self):
+    def _storage_resources(self) -> GCPResources:
         if not self._storage_resources_cache:
         if not self._storage_resources_cache:
             self._storage_resources_cache = GCPResources(self.gcp_storage)
             self._storage_resources_cache = GCPResources(self.gcp_storage)
         return self._storage_resources_cache
         return self._storage_resources_cache
 
 
     @property
     @property
-    def _dns_resources(self):
+    def _dns_resources(self) -> GCPResources:
         if not self._dns_resources_cache:
         if not self._dns_resources_cache:
             self._dns_resources_cache = GCPResources(
             self._dns_resources_cache = GCPResources(
                 self.gcp_dns,
                 self.gcp_dns,
@@ -331,7 +340,7 @@ class GCPCloudProvider(BaseCloudProvider):
         return self._dns_resources_cache
         return self._dns_resources_cache
 
 
     @property
     @property
-    def _credentials(self):
+    def _credentials(self) -> Any:
         if not self.credentials_obj:
         if not self.credentials_obj:
             if self.credentials_dict:
             if self.credentials_dict:
                 self.credentials_obj = Credentials.from_service_account_info(
                 self.credentials_obj = Credentials.from_service_account_info(
@@ -341,42 +350,43 @@ class GCPCloudProvider(BaseCloudProvider):
         return self.credentials_obj
         return self.credentials_obj
 
 
     @property
     @property
-    def client_id(self):
+    def client_id(self) -> str:
         return self._credentials.service_account_email
         return self._credentials.service_account_email
 
 
-    def _get_build_request(self):
+    def _get_build_request(self) -> Callable[..., Any]:
         credentials = with_scopes_if_required(
         credentials = with_scopes_if_required(
             self._credentials, list(CLOUD_SCOPES))
             self._credentials, list(CLOUD_SCOPES))
 
 
         # FROM: https://github.com/googleapis/google-api-python-client/blob/
         # FROM: https://github.com/googleapis/google-api-python-client/blob/
         # master/docs/thread_safety.md
         # master/docs/thread_safety.md
         # Create a new Http() object for every request
         # Create a new Http() object for every request
-        def build_request(http, *args, **kwargs):
+        def build_request(http: Any, *args: Any, **kwargs: Any) -> Any:
             new_http = google_auth_httplib2.AuthorizedHttp(
             new_http = google_auth_httplib2.AuthorizedHttp(
                 credentials, http=httplib2.Http())
                 credentials, http=httplib2.Http())
             return googleapiclient.http.HttpRequest(new_http, *args, **kwargs)
             return googleapiclient.http.HttpRequest(new_http, *args, **kwargs)
 
 
         return build_request
         return build_request
 
 
-    def _connect_gcp_storage(self):
+    def _connect_gcp_storage(self) -> Any:
         return discovery.build('storage', 'v1', credentials=self._credentials,
         return discovery.build('storage', 'v1', credentials=self._credentials,
                                cache_discovery=False,
                                cache_discovery=False,
                                requestBuilder=self._get_build_request()
                                requestBuilder=self._get_build_request()
                                )
                                )
 
 
-    def _connect_gcp_compute(self):
+    def _connect_gcp_compute(self) -> Any:
         return discovery.build('compute', 'v1', credentials=self._credentials,
         return discovery.build('compute', 'v1', credentials=self._credentials,
                                cache_discovery=False,
                                cache_discovery=False,
                                requestBuilder=self._get_build_request()
                                requestBuilder=self._get_build_request()
                                )
                                )
 
 
-    def _connect_gcp_dns(self):
+    def _connect_gcp_dns(self) -> Any:
         return discovery.build('dns', 'v1', credentials=self._credentials,
         return discovery.build('dns', 'v1', credentials=self._credentials,
                                cache_discovery=False,
                                cache_discovery=False,
                                requestBuilder=self._get_build_request()
                                requestBuilder=self._get_build_request()
                                )
                                )
 
 
-    def wait_for_operation(self, operation, region=None, zone=None):
+    def wait_for_operation(self, operation: Any, region: str | None = None,
+                           zone: str | None = None) -> Any:
         args = {'project': self.project_name, 'operation': operation['name']}
         args = {'project': self.project_name, 'operation': operation['name']}
         if not region and not zone:
         if not region and not zone:
             operations = self.gcp_compute.globalOperations()
             operations = self.gcp_compute.globalOperations()
@@ -396,11 +406,12 @@ class GCPCloudProvider(BaseCloudProvider):
 
 
             time.sleep(0.5)
             time.sleep(0.5)
 
 
-    def parse_url(self, url):
+    def parse_url(self, url: str) -> "GCPResourceUrl | None":
         out = self._compute_resources.parse_url(url)
         out = self._compute_resources.parse_url(url)
         return out if out else self._storage_resources.parse_url(url)
         return out if out else self._storage_resources.parse_url(url)
 
 
-    def get_resource(self, resource, url_or_name, **kwargs):
+    def get_resource(self, resource: str, url_or_name: str,
+                     **kwargs: Any) -> Any:
         if not url_or_name:
         if not url_or_name:
             return None
             return None
         resource_url = (
         resource_url = (
@@ -424,7 +435,7 @@ class GCPCloudProvider(BaseCloudProvider):
             else:
             else:
                 raise
                 raise
 
 
-    def authenticate(self):
+    def authenticate(self) -> bool:
         try:
         try:
             self.gcp_compute
             self.gcp_compute
             return True
             return True

Разница между файлами не показана из-за своего большого размера
+ 300 - 180
cloudbridge/providers/gcp/resources.py


Разница между файлами не показана из-за своего большого размера
+ 320 - 202
cloudbridge/providers/gcp/services.py


+ 12 - 6
cloudbridge/providers/gcp/subservices.py

@@ -6,6 +6,12 @@ from cloudbridge.base.subservices import BaseFloatingIPSubService
 from cloudbridge.base.subservices import BaseGatewaySubService
 from cloudbridge.base.subservices import BaseGatewaySubService
 from cloudbridge.base.subservices import BaseSubnetSubService
 from cloudbridge.base.subservices import BaseSubnetSubService
 from cloudbridge.base.subservices import BaseVMFirewallRuleSubService
 from cloudbridge.base.subservices import BaseVMFirewallRuleSubService
+from cloudbridge.interfaces.provider import CloudProvider
+from cloudbridge.interfaces.resources import Bucket
+from cloudbridge.interfaces.resources import DnsZone
+from cloudbridge.interfaces.resources import Gateway
+from cloudbridge.interfaces.resources import Network
+from cloudbridge.interfaces.resources import VMFirewall
 
 
 
 
 log = logging.getLogger(__name__)
 log = logging.getLogger(__name__)
@@ -13,34 +19,34 @@ log = logging.getLogger(__name__)
 
 
 class GCPBucketObjectSubService(BaseBucketObjectSubService):
 class GCPBucketObjectSubService(BaseBucketObjectSubService):
 
 
-    def __init__(self, provider, bucket):
+    def __init__(self, provider: CloudProvider, bucket: Bucket) -> None:
         super(GCPBucketObjectSubService, self).__init__(provider, bucket)
         super(GCPBucketObjectSubService, self).__init__(provider, bucket)
 
 
 
 
 class GCPGatewaySubService(BaseGatewaySubService):
 class GCPGatewaySubService(BaseGatewaySubService):
-    def __init__(self, provider, network):
+    def __init__(self, provider: CloudProvider, network: Network) -> None:
         super(GCPGatewaySubService, self).__init__(provider, network)
         super(GCPGatewaySubService, self).__init__(provider, network)
 
 
 
 
 class GCPVMFirewallRuleSubService(BaseVMFirewallRuleSubService):
 class GCPVMFirewallRuleSubService(BaseVMFirewallRuleSubService):
 
 
-    def __init__(self, provider, firewall):
+    def __init__(self, provider: CloudProvider, firewall: VMFirewall) -> None:
         super(GCPVMFirewallRuleSubService, self).__init__(provider, firewall)
         super(GCPVMFirewallRuleSubService, self).__init__(provider, firewall)
 
 
 
 
 class GCPFloatingIPSubService(BaseFloatingIPSubService):
 class GCPFloatingIPSubService(BaseFloatingIPSubService):
 
 
-    def __init__(self, provider, gateway):
+    def __init__(self, provider: CloudProvider, gateway: Gateway) -> None:
         super(GCPFloatingIPSubService, self).__init__(provider, gateway)
         super(GCPFloatingIPSubService, self).__init__(provider, gateway)
 
 
 
 
 class GCPSubnetSubService(BaseSubnetSubService):
 class GCPSubnetSubService(BaseSubnetSubService):
 
 
-    def __init__(self, provider, network):
+    def __init__(self, provider: CloudProvider, network: Network) -> None:
         super(GCPSubnetSubService, self).__init__(provider, network)
         super(GCPSubnetSubService, self).__init__(provider, network)
 
 
 
 
 class GCPDnsRecordSubService(BaseDnsRecordSubService):
 class GCPDnsRecordSubService(BaseDnsRecordSubService):
 
 
-    def __init__(self, provider, dns_zone):
+    def __init__(self, provider: CloudProvider, dns_zone: DnsZone) -> None:
         super(GCPDnsRecordSubService, self).__init__(provider, dns_zone)
         super(GCPDnsRecordSubService, self).__init__(provider, dns_zone)

+ 1 - 0
pyproject.toml

@@ -152,6 +152,7 @@ module = [
     "cloudbridge.providers.aws.*",
     "cloudbridge.providers.aws.*",
     "cloudbridge.providers.mock.*",
     "cloudbridge.providers.mock.*",
     "cloudbridge.providers.openstack.*",
     "cloudbridge.providers.openstack.*",
+    "cloudbridge.providers.gcp.*",
 ]
 ]
 ignore_errors = false
 ignore_errors = false
 warn_return_any = false
 warn_return_any = false

Некоторые файлы не были показаны из-за большого количества измененных файлов