@@ -79,11 +79,7 @@ def _extract_param_names(func):
7979 """
8080 sig = inspect .signature (func )
8181 param_names = list (sig .parameters .keys ())
82-
83- # Remove 'self' if present (for class methods)
84- if param_names and param_names [0 ] == 'self' :
85- param_names = param_names [1 :]
86-
82+
8783 return param_names
8884
8985
@@ -154,6 +150,8 @@ def __call__(self, row):
154150 Func .__name__ = name if name is not None else func .__name__
155151 Func .__doc__ = func .__doc__
156152 query_compile .FUNCTIONS [Func .__name__ ].append (Func )
153+ _add_to_doc_groups (Func , intypes , groups )
154+
157155 return func
158156 return decorator
159157
@@ -169,15 +167,14 @@ def register(name=None, groups=None):
169167 TYPE_CATEGORIES for valid group names.
170168 """
171169 def decorator (cls ):
172- # Extract parameter names from __init__ method, excluding 'self'
173- param_names = cls .__param_names__
174170 # Remove the context parameter
175- param_names = param_names [1 :]
176-
171+ cls . __param_names__ = cls . __param_names__ [1 :]
172+
177173 if name is not None :
178174 cls .__name__ = name
179- cls .__param_names__ = param_names
180175 query_compile .FUNCTIONS [cls .__name__ ].append (cls )
176+ _add_to_doc_groups (cls , cls .__intypes__ , groups )
177+
181178 return cls
182179 return decorator
183180
@@ -861,11 +858,7 @@ def interval(x):
861858
862859@function ([relativedelta , datetime .date , datetime .date ], datetime .date , groups = ['date' ])
863860def date_bin (stride , source , origin ):
864- """Bin a date into the specified stride aligned with the specified origin.
865861
866- As an extension to the the SQL standard ``date_bin()`` function this
867- function also accepts strides containing units of months and years.
868- """
869862 if stride .months or stride .years :
870863 if origin + stride <= origin :
871864 # FIXME: this should raise and error: stride must be greater than zero
@@ -1071,3 +1064,57 @@ def update(self, store, context):
10711064 cur = store [self .handle ]
10721065 if cur is None or value > cur :
10731066 store [self .handle ] = value
1067+
1068+ def _describe_functions (functions , aggregates = False , type_filter = None ):
1069+ """Describe functions, optionally filtered by input type category.
1070+
1071+ Args:
1072+ functions: Dictionary of (function name: EvalFunction subclass),
1073+ the actual class, not an object, which would represent a particular
1074+ function call.
1075+ aggregates: If True, show aggregates; if False, show regular functions
1076+ type_filter: Optional filter by input type category (see TYPE_CATEGORIES)
1077+ """
1078+ # Determine which functions to iterate over
1079+ if type_filter :
1080+ # Use the pre-populated FUNCTION_DOC_GROUPS for filtering
1081+ funcs_to_process = FUNCTION_DOC_GROUPS .get (type_filter , [])
1082+ else :
1083+ # Collect all functions from all groups
1084+ funcs_to_process = []
1085+ for name , funcs in functions .items ():
1086+ funcs_to_process .extend (funcs )
1087+
1088+ entries = []
1089+ for func in funcs_to_process :
1090+ # Filter by aggregate vs non-aggregate
1091+ if aggregates != issubclass (func , query_compile .EvalAggregator ):
1092+ continue
1093+
1094+ # Get the function name
1095+ name = func .__name__ .lower ()
1096+
1097+ # Assemble function signature for output using parameter names
1098+ args = ', ' .join (f'{ param_name } : { types .name (dtype )} '
1099+ for param_name , dtype in zip (func .__param_names__ , func .__intypes__ ))
1100+
1101+ if func .__outtype__ :
1102+ outtype = types .name (func .__outtype__ )
1103+ else :
1104+ outtype = None
1105+
1106+ doc = func .__doc__ or ''
1107+ entries .append ((name , doc , args , outtype ))
1108+
1109+ entries .sort ()
1110+ return entries
1111+
1112+ def _format_description (entries ):
1113+ out = io .StringIO ()
1114+ wrapper = textwrap .TextWrapper (initial_indent = ' ' , subsequent_indent = ' ' , width = 80 )
1115+ for key , entries in itertools .groupby (entries , key = lambda x : x [:2 ]): # noqa: B020
1116+ for name , doc , args , outtype in entries :
1117+ print (f'{ name } ({ args } ) -> { outtype } ' , file = out )
1118+ print (wrapper .fill (re .sub (r'[ \n\t]+' , ' ' , doc or '' )), file = out )
1119+ print (file = out )
1120+ return out .getvalue ().rstrip ()
0 commit comments