diff --git a/cfme/infrastructure/datastore.py b/cfme/infrastructure/datastore.py index 1a5cba327a..019cd84550 100644 --- a/cfme/infrastructure/datastore.py +++ b/cfme/infrastructure/datastore.py @@ -18,6 +18,7 @@ from cfme.exceptions import displayed_not_implemented from cfme.exceptions import ItemNotFound from cfme.exceptions import MenuItemNotFound +from cfme.infrastructure.provider import InfraProvider from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity from cfme.optimize.utilization import DatastoreUtilizationTrendsView @@ -216,7 +217,7 @@ class Datastore(Pretty, BaseEntity, Taggable, CustomButtonEventsMixin): pretty_attrs = ['name', 'provider_key'] _param_name = ParamClassName('name') name = attr.ib() - provider = attr.ib() + provider: InfraProvider = attr.ib() type = attr.ib(default=None) def __attrs_post_init__(self): @@ -358,7 +359,7 @@ def wait_candu_data_available(self, timeout=900): @attr.s -class DatastoreCollection(BaseCollection): +class DatastoreCollection(BaseCollection[Datastore]): """Collection class for :py:class:`cfme.infrastructure.datastore.Datastore`""" ENTITY = Datastore @@ -421,7 +422,6 @@ def run_smartstate_analysis(self, *datastores): datastores = list(datastores) checked_datastores = list() - view = navigate_to(self, 'All') for datastore in datastores: @@ -469,6 +469,7 @@ class DetailsFromProvider(CFMENavigateStep): VIEW = DatastoreDetailsView def prerequisite(self): + # TODO use DatastoresOfProvider prov_view = navigate_to(self.obj.provider, 'Details') prov_view.entities.summary('Relationships').click_at('Datastores') return self.obj.create_view(DatastoresView) diff --git a/cfme/modeling/base.py b/cfme/modeling/base.py index b9d0e3ca61..7d55ea0a2a 100644 --- a/cfme/modeling/base.py +++ b/cfme/modeling/base.py @@ -1,4 +1,7 @@ from collections.abc import Callable +from typing import Generic +from typing import Type +from typing import TypeVar import attr from cached_property import cached_property @@ -87,8 +90,11 @@ def __getattr__(self, name): return self._collection_cache[name] +T = TypeVar('T') + + @attr.s -class BaseCollection(NavigatableMixin): +class BaseCollection(NavigatableMixin, Generic[T]): """Class for helping create consistent Collections The BaseCollection class is responsible for ensuring two things: @@ -99,8 +105,7 @@ class BaseCollection(NavigatableMixin): This class works in tandem with the entrypoint loader which ensures that the correct argument names have been used. """ - - ENTITY = None + ENTITY: Type[T] parent = attr.ib(repr=False) filters = attr.ib(default=attr.Factory(dict)) @@ -120,13 +125,13 @@ def for_appliance(cls, appliance, *k, **kw): def for_entity(cls, obj, *k, **kw): return cls(obj, *k, **kw) + def instantiate(self, *args, **kwargs) -> T: + return self.ENTITY.from_collection(self, *args, **kwargs) + @classmethod def for_entity_with_filter(cls, obj, filt, *k, **kw): return cls.for_entity(obj, *k, **kw).filter(filt) - def instantiate(self, *args, **kwargs): - return self.ENTITY.from_collection(self, *args, **kwargs) - def filter(self, filter): filters = self.filters.copy() filters.update(filter) @@ -146,7 +151,7 @@ class BaseEntity(NavigatableMixin): argument names have been used. """ - parent = attr.ib(repr=False) # This is the collection or not + parent: BaseCollection = attr.ib(repr=False) # This is the collection or not # TODO This needs removing as we need proper __eq__ on objects, but it is part of a # much larger discussion @@ -157,7 +162,7 @@ def appliance(self): return self.parent.appliance @classmethod - def from_collection(cls, collection, *k, **kw): + def from_collection(cls, collection: BaseCollection, *k, **kw): return cls(collection, *k, **kw) @cached_property