@@ -5126,8 +5126,7 @@ def state_dsl_start(self, line: str) -> None:
51265126
51275127 self .next (self .state_modulename_name , line )
51285128
5129- @staticmethod
5130- def parse_function_names (line : str ) -> FunctionNames :
5129+ def parse_function_names (self , line : str ) -> FunctionNames :
51315130 left , as_ , right = line .partition (' as ' )
51325131 full_name = left .strip ()
51335132 c_basename = right .strip ()
@@ -5142,28 +5141,101 @@ def parse_function_names(line: str) -> FunctionNames:
51425141 fail (f"Illegal function name: { full_name !r} " )
51435142 if not is_legal_c_identifier (c_basename ):
51445143 fail (f"Illegal C basename: { c_basename !r} " )
5145- return FunctionNames (full_name = full_name , c_basename = c_basename )
5144+ names = FunctionNames (full_name = full_name , c_basename = c_basename )
5145+ self .normalize_function_kind (names .full_name )
5146+ return names
51465147
5147- def update_function_kind (self , fullname : str ) -> None :
5148+ def normalize_function_kind (self , fullname : str ) -> None :
5149+ # Fetch the method name and possibly class.
51485150 fields = fullname .split ('.' )
51495151 name = fields .pop ()
51505152 _ , cls = self .clinic ._module_and_class (fields )
5153+
5154+ # Check special method requirements.
51515155 if name in unsupported_special_methods :
51525156 fail (f"{ name !r} is a special method and cannot be converted to Argument Clinic!" )
5153-
5157+ if name == '__init__' and (self .kind is not CALLABLE or not cls ):
5158+ fail (f"{ name !r} must be a normal method; got '{ self .kind } '!" )
5159+ if name == '__new__' and (self .kind is not CLASS_METHOD or not cls ):
5160+ fail ("'__new__' must be a class method!" )
5161+ if self .kind in {GETTER , SETTER } and not cls :
5162+ fail ("@getter and @setter must be methods" )
5163+
5164+ # Normalise self.kind.
51545165 if name == '__new__' :
5155- if (self .kind is CLASS_METHOD ) and cls :
5156- self .kind = METHOD_NEW
5157- else :
5158- fail ("'__new__' must be a class method!" )
5166+ self .kind = METHOD_NEW
51595167 elif name == '__init__' :
5160- if (self .kind is CALLABLE ) and cls :
5161- self .kind = METHOD_INIT
5168+ self .kind = METHOD_INIT
5169+
5170+ def resolve_return_converter (
5171+ self , full_name : str , forced_converter : str
5172+ ) -> CReturnConverter :
5173+ if forced_converter :
5174+ if self .kind in {GETTER , SETTER }:
5175+ fail (f"@{ self .kind .name .lower ()} method cannot define a return type" )
5176+ ast_input = f"def x() -> { forced_converter } : pass"
5177+ try :
5178+ module_node = ast .parse (ast_input )
5179+ except SyntaxError :
5180+ fail (f"Badly formed annotation for { full_name !r} : { forced_converter !r} " )
5181+ function_node = module_node .body [0 ]
5182+ assert isinstance (function_node , ast .FunctionDef )
5183+ try :
5184+ name , legacy , kwargs = self .parse_converter (function_node .returns )
5185+ if legacy :
5186+ fail (f"Legacy converter { name !r} not allowed as a return converter" )
5187+ if name not in return_converters :
5188+ fail (f"No available return converter called { name !r} " )
5189+ return return_converters [name ](** kwargs )
5190+ except ValueError :
5191+ fail (f"Badly formed annotation for { full_name !r} : { forced_converter !r} " )
5192+
5193+ if self .kind is METHOD_INIT :
5194+ return init_return_converter ()
5195+ return CReturnConverter ()
5196+
5197+ def parse_cloned_function (self , names : FunctionNames , existing : str ) -> None :
5198+ full_name , c_basename = names
5199+ fields = [x .strip () for x in existing .split ('.' )]
5200+ function_name = fields .pop ()
5201+ module , cls = self .clinic ._module_and_class (fields )
5202+ parent = cls or module
5203+
5204+ for existing_function in parent .functions :
5205+ if existing_function .name == function_name :
5206+ break
5207+ else :
5208+ print (f"{ cls = } , { module = } , { existing = } " , file = sys .stderr )
5209+ print (f"{ (cls or module ).functions = } " , file = sys .stderr )
5210+ fail (f"Couldn't find existing function { existing !r} !" )
5211+
5212+ fields = [x .strip () for x in full_name .split ('.' )]
5213+ function_name = fields .pop ()
5214+ module , cls = self .clinic ._module_and_class (fields )
5215+
5216+ overrides : dict [str , Any ] = {
5217+ "name" : function_name ,
5218+ "full_name" : full_name ,
5219+ "module" : module ,
5220+ "cls" : cls ,
5221+ "c_basename" : c_basename ,
5222+ "docstring" : "" ,
5223+ }
5224+ if not (existing_function .kind is self .kind and
5225+ existing_function .coexist == self .coexist ):
5226+ # Allow __new__ or __init__ methods.
5227+ if existing_function .kind .new_or_init :
5228+ overrides ["kind" ] = self .kind
5229+ # Future enhancement: allow custom return converters
5230+ overrides ["return_converter" ] = CReturnConverter ()
51625231 else :
5163- fail (
5164- "'__init__' must be a normal method; "
5165- f"got '{ self .kind } '!"
5166- )
5232+ fail ("'kind' of function and cloned function don't match! "
5233+ "(@classmethod/@staticmethod/@coexist)" )
5234+ function = existing_function .copy (** overrides )
5235+ self .function = function
5236+ self .block .signatures .append (function )
5237+ (cls or module ).functions .append (function )
5238+ self .next (self .state_function_docstring )
51675239
51685240 def state_modulename_name (self , line : str ) -> None :
51695241 # looking for declaration, which establishes the leftmost column
@@ -5188,111 +5260,56 @@ def state_modulename_name(self, line: str) -> None:
51885260 # are we cloning?
51895261 before , equals , existing = line .rpartition ('=' )
51905262 if equals :
5191- full_name , c_basename = self .parse_function_names (before )
51925263 existing = existing .strip ()
51935264 if is_legal_py_identifier (existing ):
51945265 # we're cloning!
5195- fields = [x .strip () for x in existing .split ('.' )]
5196- function_name = fields .pop ()
5197- module , cls = self .clinic ._module_and_class (fields )
5198-
5199- for existing_function in (cls or module ).functions :
5200- if existing_function .name == function_name :
5201- break
5202- else :
5203- print (f"{ cls = } , { module = } , { existing = } " , file = sys .stderr )
5204- print (f"{ (cls or module ).functions = } " , file = sys .stderr )
5205- fail (f"Couldn't find existing function { existing !r} !" )
5206-
5207- fields = [x .strip () for x in full_name .split ('.' )]
5208- function_name = fields .pop ()
5209- module , cls = self .clinic ._module_and_class (fields )
5210-
5211- self .update_function_kind (full_name )
5212- overrides : dict [str , Any ] = {
5213- "name" : function_name ,
5214- "full_name" : full_name ,
5215- "module" : module ,
5216- "cls" : cls ,
5217- "c_basename" : c_basename ,
5218- "docstring" : "" ,
5219- }
5220- if not (existing_function .kind is self .kind and
5221- existing_function .coexist == self .coexist ):
5222- # Allow __new__ or __init__ methods.
5223- if existing_function .kind .new_or_init :
5224- overrides ["kind" ] = self .kind
5225- # Future enhancement: allow custom return converters
5226- overrides ["return_converter" ] = CReturnConverter ()
5227- else :
5228- fail ("'kind' of function and cloned function don't match! "
5229- "(@classmethod/@staticmethod/@coexist)" )
5230- function = existing_function .copy (** overrides )
5231- self .function = function
5232- self .block .signatures .append (function )
5233- (cls or module ).functions .append (function )
5234- self .next (self .state_function_docstring )
5235- return
5266+ names = self .parse_function_names (before )
5267+ return self .parse_cloned_function (names , existing )
52365268
52375269 line , _ , returns = line .partition ('->' )
52385270 returns = returns .strip ()
52395271 full_name , c_basename = self .parse_function_names (line )
5240-
5241- return_converter = None
5242- if returns :
5243- if self .kind in {GETTER , SETTER }:
5244- fail (f"@{ self .kind .name .lower ()} method cannot define a return type" )
5245- ast_input = f"def x() -> { returns } : pass"
5246- try :
5247- module_node = ast .parse (ast_input )
5248- except SyntaxError :
5249- fail (f"Badly formed annotation for { full_name !r} : { returns !r} " )
5250- function_node = module_node .body [0 ]
5251- assert isinstance (function_node , ast .FunctionDef )
5252- try :
5253- name , legacy , kwargs = self .parse_converter (function_node .returns )
5254- if legacy :
5255- fail (f"Legacy converter { name !r} not allowed as a return converter" )
5256- if name not in return_converters :
5257- fail (f"No available return converter called { name !r} " )
5258- return_converter = return_converters [name ](** kwargs )
5259- except ValueError :
5260- fail (f"Badly formed annotation for { full_name !r} : { returns !r} " )
5272+ return_converter = self .resolve_return_converter (full_name , returns )
52615273
52625274 fields = [x .strip () for x in full_name .split ('.' )]
52635275 function_name = fields .pop ()
52645276 module , cls = self .clinic ._module_and_class (fields )
52655277
5266- if self .kind in {GETTER , SETTER }:
5267- if not cls :
5268- fail ("@getter and @setter must be methods" )
5269-
5270- self .update_function_kind (full_name )
5271- if self .kind is METHOD_INIT and not return_converter :
5272- return_converter = init_return_converter ()
5273-
5274- if not return_converter :
5275- return_converter = CReturnConverter ()
5276-
5277- self .function = Function (name = function_name , full_name = full_name , module = module , cls = cls , c_basename = c_basename ,
5278- return_converter = return_converter , kind = self .kind , coexist = self .coexist ,
5279- critical_section = self .critical_section ,
5280- target_critical_section = self .target_critical_section )
5281- self .block .signatures .append (self .function )
5282-
5283- # insert a self converter automatically
5284- type , name = correct_name_for_self (self .function )
5285- kwargs = {}
5286- if cls and type == "PyObject *" :
5287- kwargs ['type' ] = cls .typedef
5288- sc = self .function .self_converter = self_converter (name , name , self .function , ** kwargs )
5289- p_self = Parameter (name , inspect .Parameter .POSITIONAL_ONLY ,
5290- function = self .function , converter = sc )
5291- self .function .parameters [name ] = p_self
5292-
5293- (cls or module ).functions .append (self .function )
5278+ func = Function (
5279+ name = function_name ,
5280+ full_name = full_name ,
5281+ module = module ,
5282+ cls = cls ,
5283+ c_basename = c_basename ,
5284+ return_converter = return_converter ,
5285+ kind = self .kind ,
5286+ coexist = self .coexist ,
5287+ critical_section = self .critical_section ,
5288+ target_critical_section = self .target_critical_section
5289+ )
5290+ self .add_function (func )
5291+
52945292 self .next (self .state_parameters_start )
52955293
5294+ def add_function (self , func : Function ) -> None :
5295+ # Insert a self converter automatically.
5296+ tp , name = correct_name_for_self (func )
5297+ if func .cls and tp == "PyObject *" :
5298+ func .self_converter = self_converter (name , name , func ,
5299+ type = func .cls .typedef )
5300+ else :
5301+ func .self_converter = self_converter (name , name , func )
5302+ func .parameters [name ] = Parameter (
5303+ name ,
5304+ inspect .Parameter .POSITIONAL_ONLY ,
5305+ function = func ,
5306+ converter = func .self_converter
5307+ )
5308+
5309+ self .block .signatures .append (func )
5310+ self .function = func
5311+ (func .cls or func .module ).functions .append (func )
5312+
52965313 # Now entering the parameters section. The rules, formally stated:
52975314 #
52985315 # * All lines must be indented with spaces only.
0 commit comments