11from abc import ABC , abstractmethod
22from dataclasses import dataclass , asdict
3+ from collections import OrderedDict
34from diplomat .processing .type_casters import StrictCallable , PathLike , Union , List , Dict , Any , Optional , TypeCaster , NoneType
45import typing
56
6-
77class Select (Union ):
88 def __eq__ (self , other : TypeCaster ):
99 if (isinstance (other , Union )):
@@ -62,29 +62,100 @@ def to_type_hint(self) -> typing.Type:
6262)
6363
6464
65- @dataclass (frozen = False )
66- class DIPLOMATBaselineCommands :
65+ @dataclass (frozen = True )
66+ class DIPLOMATContract :
67+ """
68+ Represents a 'contract'
69+ """
70+ method_name : str
71+ method_type : StrictCallable
72+
73+
74+ class CommandManager (type ):
75+
76+ __no_type_check__ = False
77+ def __new__ (cls , * args , ** kwargs ):
78+ obj = super ().__new__ (cls , * args , ** kwargs )
79+
80+ annotations = typing .get_type_hints (obj )
81+
82+ for name , annot in annotations .items ():
83+ if (name in obj .__dict__ ):
84+ raise TypeError (f"Command annotation '{ name } ' has default value, which is not allowed." )
85+
86+ return obj
87+
88+ def __getattr__ (self , item ):
89+ annot = typing .get_type_hints (self )[item ]
90+ return DIPLOMATContract (item , annot )
91+
92+
93+ def required (typecaster : TypeCaster ) -> TypeCaster :
94+ typecaster ._required = True
95+ return typecaster
96+
97+
98+ class DIPLOMATCommands (metaclass = CommandManager ):
6799 """
68100 The baseline set of functions each DIPLOMAT backend must implement. Backends can add additional commands
69- by extending this base class.. .
101+ by passing the methods to this classes constructor .
70102 """
71- _verifier : VerifierFunction
103+ _verifier : required ( VerifierFunction )
72104 analyze_videos : AnalyzeVideosFunction (NoneType )
73105 analyze_frames : AnalyzeFramesFunction (NoneType )
74106 label_videos : LabelVideosFunction (NoneType )
75107 tweak_videos : LabelVideosFunction (NoneType )
76108 convert_results : ConvertResultsFunction (NoneType )
77109
78- def __post_init__ (self ):
110+ def __init__ (self , ** kwargs ):
111+ missing = object ()
112+ self ._commands = OrderedDict ()
113+
79114 annotations = typing .get_type_hints (type (self ))
80115
81- for name , value in asdict ( self ) .items ():
82- annot = annotations .get (name , None )
116+ for name , annot in annotations .items ():
117+ value = kwargs .get (name , missing )
83118
119+ if (value is missing ):
120+ if (getattr (annot , "_required" , False )):
121+ raise ValueError (f"Command '{ name } ' is required, but was not provided." )
122+ continue
84123 if (annot is None or (not isinstance (annot , TypeCaster ))):
85124 raise TypeError ("DIPLOMAT Command Struct can only contain typecaster types." )
86125
87- setattr (self , name , annot (value ))
126+ self ._commands [name ] = annot (value )
127+
128+ for name , value in kwargs .items ():
129+ if (name not in annotations ):
130+ self ._commands [name ] = value
131+
132+ def __iter__ (self ):
133+ return iter (self ._commands .items ())
134+
135+ def __getattr__ (self , item : str ):
136+ return self ._commands .get (item )
137+
138+ def verify (self , contract : DIPLOMATContract , config : Union [List [PathLike ], PathLike ], ** kwargs : Any ) -> bool :
139+ """
140+ Verify this backend can handle the provided command type, config file, and arguments.
141+
142+ :param contract: The contract for the command. Includes the name of the method and the type of the method,
143+ which will typically be a strict callable.
144+ :param config: The configuration file, checks if the backend can handle this configuration file.
145+ :param kwargs: Any additional arguments to pass to the backends verifier.
146+
147+ :return: A boolean, True if the backend can handle the provided command and arguments, otherwise False.
148+ """
149+ if (contract .method_name in self ._commands ):
150+ func = self ._commands [contract .method_name ]
151+ try :
152+ contract .method_type (func )
153+ except Exception :
154+ return False
155+
156+ return self ._verifier (config , ** kwargs )
157+
158+ return False
88159
89160
90161class DIPLOMATFrontend (ABC ):
@@ -93,7 +164,7 @@ class DIPLOMATFrontend(ABC):
93164 """
94165 @classmethod
95166 @abstractmethod
96- def init (cls ) -> typing .Optional [DIPLOMATBaselineCommands ]:
167+ def init (cls ) -> typing .Optional [DIPLOMATCommands ]:
97168 """
98169 Attempt to initialize the frontend, returning a list of api functions. If the backend can't initialize due to missing imports/requirements,
99170 this function should return None.
0 commit comments