from typing import Literal, overload, TypeVar, Generic, Type
import enum
import abc
import typing
class Version(enum.Enum):
Version1 = 1
Version2 = 2
Version3 = 3
import abc
from typing import Type
class Machine1BaseConfig:
@abc.abstractmethod
def __init__(self, *args, **kwargs) -> None:
pass
class Machine1Config_1(Machine1BaseConfig):
def __init__(self, fueltype, speed) -> None:
self.fueltype = fueltype
self.speed = speed
class Machine1Config_2(Machine1BaseConfig):
def __init__(self, speed, weight) -> None:
self.speed = speed
self.weight = weight
class Machine1FacadeConfig:
@classmethod
def get_version(cls, version: Version) -> Type[typing.Union[Machine1Config_1, Machine1Config_2]]:
config_map = {
Version.Version1: Machine1Config_1,
Version.Version2: Machine1Config_2,
Version.Version3: Machine1Config_2,
}
return config_map[version]
class Machine2BaseConfig:
@abc.abstractmethod
def __init__(self, *args, **kwargs) -> None:
pass
class Machine2Config_1(Machine2BaseConfig):
def __init__(self, gridsize) -> None:
self.gridsize = gridsize
class Machine2Config_2(Machine2BaseConfig):
def __init__(self, loadtype, duration) -> None:
self.loadtype = loadtype
self.duration = duration
class Machine2FacadeConfig:
@classmethod
def get_version(cls, version: Version) -> Type[typing.Union[Machine2Config_1, Machine2Config_2]]:
config_map = {
Version.Version1: Machine2Config_1,
Version.Version2: Machine2Config_1,
Version.Version3: Machine2Config_2,
}
return config_map[version]
class Factory:
def __init__(self, version: Version) -> None:
self.version = version
@property
def Machine1Config(self):
return Machine1FacadeConfig.get_version(self.version)
@property
def Machine2Config(self):
return Machine2FacadeConfig.get_version(self.version)
factory_instance = Factory(Version.Version1)
machine1_config_instance = factory_instance.Machine1Config()
machine2_config_instance = factory_instance.Machine2Config()
In the provided Python code, the Factory class is used to instantiate configuration objects for two different types of machines (Machine1 and Machine2) based on a specified version. The problem is when using Pylance/Pyright with Visual Studio Code, I'm experiencing issues with autocomplete not correctly suggesting parameters for dynamically instantiated classes (Machine1Config and Machine2Config) in a factory design pattern. How can I improve my code to enable more accurate and helpful autocompletion suggestions by Pylance for these dynamically determined types?
I have thought that this should somehow work with @overload decorater but I can't wrap my head around it how to quite implement it.
Furthermore currently with the type hint Type[typing.Union[Machine1Config_1, Machine1Config_2]]
Pylance suggests all key word arguments of Machine1Config_1 and Machine1Config_2, so fueltype, speed, weight. If I leave this type hint away there is no autocompletion at all.
Looking at the factory, there is no way to tell which of
Type[typing.Union[Machine2Config_1, Machine2Config_2]]
will be returned when callingMachine1FacadeConfig.get_version(self.version)
in isolation.As the facade and the factory are extremely coupled anyways, I would suggest combining these into a single utility, where the types for version and configs can be more tightly coupled.
You can declare a generic class for factory and provide a helper function which returns an instance of that factory where the version and config types have been bound together. The helper function would be overloaded for the different combinations.
Example screenshot from vscode: