فهرست منبع

Tighten interface types and enable full mypy strict

Address imprecise annotations surfaced in review:

- factory: type the provider registry precisely. provider_list is now
  defaultdict[str, dict[str, type[CloudProvider]]] and list_providers /
  get_provider_class return type[CloudProvider] instead of Any. The
  issubclass() guard in register_provider_class narrows the registered
  class, so the previous cast() in get_provider_class is now redundant and
  removed. (register_provider_class keeps `cls: type` on purpose -- it is a
  filter that receives arbitrary classes and decides whether to register.)
- provider: the vestigial ContainerProvider/DeploymentProvider stubs no
  longer use Any; create_container/delete_container -> None and
  deploy(target: Instance) -> None (Instance is the only concrete type the
  docstring names; there is no Container class).
- Configuration now subclasses dict[str, Any] instead of bare dict.

Enable `strict = true` (still scoped to the interface layer + factory;
base/providers stay exempt via ignore_errors), with two documented
opt-outs that don't fit this codebase: implicit_reexport (interfaces/
__init__.py re-exports without __all__) and disallow_untyped_decorators
(cross-class property setters + the deprecation library's decorators).
Note: even strict does not forbid explicit Any, so the remaining Any uses
(**kwargs, dict[str, Any] for to_json/extra_data/config) are intentional.
Nuwan Goonasekera 1 روز پیش
والد
کامیت
f61531719a
4فایلهای تغییر یافته به همراه19 افزوده شده و 16 حذف شده
  1. 4 4
      cloudbridge/factory.py
  2. 4 3
      cloudbridge/interfaces/provider.py
  3. 1 1
      cloudbridge/interfaces/resources.py
  4. 10 8
      pyproject.toml

+ 4 - 4
cloudbridge/factory.py

@@ -4,7 +4,6 @@ import logging
 import pkgutil
 from collections import defaultdict
 from typing import Any
-from typing import cast
 
 from cloudbridge import providers
 from cloudbridge.interfaces import CloudProvider
@@ -29,7 +28,8 @@ class CloudProviderFactory(object):
     """
 
     def __init__(self) -> None:
-        self.provider_list: defaultdict[str, dict[str, Any]] = defaultdict(dict)
+        self.provider_list: defaultdict[str, dict[str, type[CloudProvider]]] \
+            = defaultdict(dict)
         log.debug("Providers List: %s", self.provider_list)
 
     def register_provider_class(self, cls: type) -> None:
@@ -90,7 +90,7 @@ class CloudProviderFactory(object):
             log.debug("Registering the provider: %s", cls)
             self.register_provider_class(cls)
 
-    def list_providers(self) -> dict[str, dict[str, Any]]:
+    def list_providers(self) -> dict[str, dict[str, type[CloudProvider]]]:
         """
         Get a list of available providers.
 
@@ -153,7 +153,7 @@ class CloudProviderFactory(object):
         impl = self.list_providers().get(name)
         if impl:
             log.debug("Returning provider class for %s", name)
-            return cast("type[CloudProvider]", impl["class"])
+            return impl["class"]
         else:
             log.debug("Provider with the name: %s not found", name)
             return None

+ 4 - 3
cloudbridge/interfaces/provider.py

@@ -13,6 +13,7 @@ if TYPE_CHECKING:
     from pyeventsystem.middleware import MiddlewareManager
 
     from cloudbridge.interfaces.resources import Configuration
+    from cloudbridge.interfaces.resources import Instance
     from cloudbridge.interfaces.resources import PlacementZone
     from cloudbridge.interfaces.services import ComputeService
     from cloudbridge.interfaces.services import DnsService
@@ -327,11 +328,11 @@ class ContainerProvider(object):
     __metaclass__ = ABCMeta
 
     @abstractmethod
-    def create_container(self) -> Any:
+    def create_container(self) -> None:
         pass
 
     @abstractmethod
-    def delete_container(self) -> Any:
+    def delete_container(self) -> None:
         pass
 
 
@@ -343,7 +344,7 @@ class DeploymentProvider(object):
     __metaclass__ = ABCMeta
 
     @abstractmethod
-    def deploy(self, target: Any) -> Any:
+    def deploy(self, target: Instance) -> None:
         """
         Deploys on given target, where target is an Instance or Container
         """

+ 1 - 1
cloudbridge/interfaces/resources.py

@@ -165,7 +165,7 @@ class LabeledCloudResource(CloudResource):
         pass
 
 
-class Configuration(dict):
+class Configuration(dict[str, Any]):
     """
     Represents a CloudBridge configuration object
     """

+ 10 - 8
pyproject.toml

@@ -124,14 +124,16 @@ files = ["cloudbridge"]
 ignore_missing_imports = true   # untyped cloud SDKs (boto3, azure-*, ...) -> Any
 namespace_packages = true
 warn_unused_configs = true
-# Strict baseline: applies to every module NOT exempted below.
-disallow_untyped_defs = true
-disallow_incomplete_defs = true
-no_implicit_optional = true
-check_untyped_defs = true
-warn_redundant_casts = true
-warn_unused_ignores = true
-warn_return_any = true
+# Full strict baseline (applies to every module NOT exempted below), with two
+# documented exceptions that don't fit this codebase:
+strict = true
+# interfaces/__init__.py re-exports the public names without an __all__; keep
+# implicit re-export so `from cloudbridge.interfaces import CloudProvider`
+# keeps working for factory.py and downstream consumers.
+implicit_reexport = true
+# Cross-class property setters (@LabeledCloudResource.label.setter) and the
+# `deprecation` library's decorators are untyped; don't fail the build on them.
+disallow_untyped_decorators = false
 
 # Gradual-typing exemptions. Remove a module from this list once it is fully
 # annotated; it then falls under the strict baseline above.