@@ -58,9 +58,7 @@ def _function_thread(core, key, func, args, kwds):
5858 print (f"Function call failed with the following exception:\n { exc } " )
5959
6060
61- def _calc_entry (
62- core , key , func , args , kwds , printer = lambda * _ : None
63- ) -> Optional [Any ]:
61+ def _calc_entry (core , key , func , args , kwds , printer = lambda * _ : None ) -> Optional [Any ]:
6462 core .mark_entry_being_calculated (key )
6563 try :
6664 func_res = func (* args , ** kwds )
@@ -75,37 +73,67 @@ def _calc_entry(
7573 core .mark_entry_not_calculated (key )
7674
7775
78- def _convert_args_kwargs (
79- func , _is_method : bool , args : tuple , kwds : dict
80- ) -> dict :
76+ def _convert_args_kwargs (func , _is_method : bool , args : tuple , kwds : dict ) -> dict :
8177 """Convert mix of positional and keyword arguments to aggregated kwargs."""
8278 # unwrap if the function is functools.partial
8379 if hasattr (func , "func" ):
8480 args = func .args + args
8581 kwds .update ({k : v for k , v in func .keywords .items () if k not in kwds })
8682 func = func .func
87- func_params = list (inspect .signature (func ).parameters )
88- args_as_kw = dict (
89- zip (func_params [1 :], args [1 :])
90- if _is_method
91- else zip (func_params , args )
92- )
93- # init with default values
94- kwargs = {
95- k : v .default
96- for k , v in inspect .signature (func ).parameters .items ()
97- if v .default is not inspect .Parameter .empty
98- }
99- # merge args expanded as kwargs and the original kwds
100- kwargs .update (dict (** args_as_kw , ** kwds ))
83+
84+ sig = inspect .signature (func )
85+ func_params = list (sig .parameters )
86+
87+ # Separate regular parameters from VAR_POSITIONAL
88+ regular_params = []
89+ var_positional_name = None
90+
91+ for param_name in func_params :
92+ param = sig .parameters [param_name ]
93+ if param .kind == inspect .Parameter .VAR_POSITIONAL :
94+ var_positional_name = param_name
95+ elif param .kind in (
96+ inspect .Parameter .POSITIONAL_ONLY ,
97+ inspect .Parameter .POSITIONAL_OR_KEYWORD
98+ ):
99+ regular_params .append (param_name )
100+
101+ # Map positional arguments to regular parameters
102+ if _is_method :
103+ # Skip 'self' for methods
104+ args_to_map = args [1 :]
105+ params_to_use = regular_params [1 :]
106+ else :
107+ args_to_map = args
108+ params_to_use = regular_params
109+
110+ # Map as many args as possible to regular parameters
111+ num_regular = len (params_to_use )
112+ args_as_kw = dict (zip (params_to_use , args_to_map [:num_regular ]))
113+
114+ # Handle variadic positional arguments
115+ # Store them with indexed keys like __varargs_0__, __varargs_1__, etc.
116+ if var_positional_name and len (args_to_map ) > num_regular :
117+ var_args = args_to_map [num_regular :]
118+ for i , arg in enumerate (var_args ):
119+ args_as_kw [f"__varargs_{ i } __" ] = arg
120+
121+ # Init with default values
122+ kwargs = {k : v .default for k , v in sig .parameters .items () if v .default is not inspect .Parameter .empty }
123+
124+ # Merge args expanded as kwargs and the original kwds
125+ kwargs .update (args_as_kw )
126+
127+ # Handle keyword arguments (including variadic keyword arguments)
128+ kwargs .update (kwds )
129+
101130 return OrderedDict (sorted (kwargs .items ()))
102131
103132
104133def _pop_kwds_with_deprecation (kwds , name : str , default_value : bool ):
105134 if name in kwds :
106135 warnings .warn (
107- f"`{ name } ` is deprecated and will be removed in a future release,"
108- " use `cachier__` alternative instead." ,
136+ f"`{ name } ` is deprecated and will be removed in a future release, use `cachier__` alternative instead." ,
109137 DeprecationWarning ,
110138 stacklevel = 2 ,
111139 )
@@ -216,10 +244,7 @@ def cachier(
216244 """
217245 # Check for deprecated parameters
218246 if hash_params is not None :
219- message = (
220- "hash_params will be removed in a future release, "
221- "please use hash_func instead"
222- )
247+ message = "hash_params will be removed in a future release, please use hash_func instead"
223248 warn (message , DeprecationWarning , stacklevel = 2 )
224249 hash_func = hash_params
225250 # Update parameters with defaults if input is None
@@ -261,7 +286,7 @@ def cachier(
261286 hash_func = hash_func ,
262287 wait_for_calc_timeout = wait_for_calc_timeout ,
263288 entry_size_limit = size_limit_bytes ,
264- metrics = cache_metrics ,
289+ metrics = cache_metrics
265290 )
266291 elif backend == "sql" :
267292 core = _SQLCore (
@@ -316,41 +341,25 @@ def _call(*args, max_age: Optional[timedelta] = None, **kwds):
316341 nonlocal allow_none , last_cleanup
317342 _allow_none = _update_with_defaults (allow_none , "allow_none" , kwds )
318343 # print('Inside general wrapper for {}.'.format(func.__name__))
319- ignore_cache = _pop_kwds_with_deprecation (
320- kwds , "ignore_cache" , False
321- )
322- overwrite_cache = _pop_kwds_with_deprecation (
323- kwds , "overwrite_cache" , False
324- )
344+ ignore_cache = _pop_kwds_with_deprecation (kwds , "ignore_cache" , False )
345+ overwrite_cache = _pop_kwds_with_deprecation (kwds , "overwrite_cache" , False )
325346 verbose = _pop_kwds_with_deprecation (kwds , "verbose_cache" , False )
326347 ignore_cache = kwds .pop ("cachier__skip_cache" , ignore_cache )
327- overwrite_cache = kwds .pop (
328- "cachier__overwrite_cache" , overwrite_cache
329- )
348+ overwrite_cache = kwds .pop ("cachier__overwrite_cache" , overwrite_cache )
330349 verbose = kwds .pop ("cachier__verbose" , verbose )
331- _stale_after = _update_with_defaults (
332- stale_after , "stale_after" , kwds
333- )
350+ _stale_after = _update_with_defaults (stale_after , "stale_after" , kwds )
334351 _next_time = _update_with_defaults (next_time , "next_time" , kwds )
335- _cleanup_flag = _update_with_defaults (
336- cleanup_stale , "cleanup_stale" , kwds
337- )
338- _cleanup_interval_val = _update_with_defaults (
339- cleanup_interval , "cleanup_interval" , kwds
340- )
352+ _cleanup_flag = _update_with_defaults (cleanup_stale , "cleanup_stale" , kwds )
353+ _cleanup_interval_val = _update_with_defaults (cleanup_interval , "cleanup_interval" , kwds )
341354 # merge args expanded as kwargs and the original kwds
342- kwargs = _convert_args_kwargs (
343- func , _is_method = core .func_is_method , args = args , kwds = kwds
344- )
355+ kwargs = _convert_args_kwargs (func , _is_method = core .func_is_method , args = args , kwds = kwds )
345356
346357 if _cleanup_flag :
347358 now = datetime .now ()
348359 with cleanup_lock :
349360 if now - last_cleanup >= _cleanup_interval_val :
350361 last_cleanup = now
351- _get_executor ().submit (
352- core .delete_stale_entries , _stale_after
353- )
362+ _get_executor ().submit (core .delete_stale_entries , _stale_after )
354363
355364 _print = print if verbose else lambda x : None
356365
@@ -401,10 +410,7 @@ def _call(*args, max_age: Optional[timedelta] = None, **kwds):
401410 nonneg_max_age = True
402411 if max_age is not None :
403412 if max_age < ZERO_TIMEDELTA :
404- _print (
405- "max_age is negative. "
406- "Cached result considered stale."
407- )
413+ _print ("max_age is negative. Cached result considered stale." )
408414 nonneg_max_age = False
409415 else :
410416 assert max_age is not None # noqa: S101
@@ -459,9 +465,7 @@ def _call(*args, max_age: Optional[timedelta] = None, **kwds):
459465 cache_metrics .record_recalculation ()
460466 core .mark_entry_being_calculated (key )
461467 try :
462- _get_executor ().submit (
463- _function_thread , core , key , func , args , kwds
464- )
468+ _get_executor ().submit (_function_thread , core , key , func , args , kwds )
465469 finally :
466470 core .mark_entry_not_calculated (key )
467471 if cache_metrics :
@@ -542,9 +546,7 @@ def _precache_value(*args, value_to_cache, **kwds): # noqa: D417
542546
543547 """
544548 # merge args expanded as kwargs and the original kwds
545- kwargs = _convert_args_kwargs (
546- func , _is_method = core .func_is_method , args = args , kwds = kwds
547- )
549+ kwargs = _convert_args_kwargs (func , _is_method = core .func_is_method , args = args , kwds = kwds )
548550 return core .precache_value ((), kwargs , value_to_cache )
549551
550552 func_wrapper .clear_cache = _clear_cache
0 commit comments