@@ -93,9 +93,9 @@ def validate_decorator_func(decorator_func):
9393 f"First argument of the decorator function needs to default to None. "
9494 f"Was { first_param .default } "
9595 )
96- assert all (p . kind in { p . KEYWORD_ONLY , p . VAR_KEYWORD } for p in other_params ), (
97- f"All arguments (besides the first) need to be keyword-only"
98- )
96+ assert all (
97+ p . kind in { p . KEYWORD_ONLY , p . VAR_KEYWORD } for p in other_params
98+ ), f"All arguments (besides the first) need to be keyword-only"
9999 return True
100100
101101 validate_decorator_func (decorator_func )
@@ -986,9 +986,9 @@ def _cached_keys(
986986 )
987987 # assert keys_cache == iter_to_container
988988
989- assert isinstance (store , type ), (
990- f"store_cls must be a type, was a { type ( store ) } : { store } "
991- )
989+ assert isinstance (
990+ store , type
991+ ), f"store_cls must be a type, was a { type ( store ) } : { store } "
992992
993993 # name = name or 'IterCached' + get_class_name(store_cls)
994994 name = name or get_class_name (store )
@@ -1220,9 +1220,9 @@ def catch_and_cache_error_keys(
12201220
12211221 """
12221222
1223- assert isinstance (store , type ), (
1224- f"store_cls must be a type, was a { type ( store ) } : { store } "
1225- )
1223+ assert isinstance (
1224+ store , type
1225+ ), f"store_cls must be a type, was a { type ( store ) } : { store } "
12261226
12271227 # assert isinstance(store, Mapping), f"store_cls must be a Mapping.
12281228 # Was not. mro is {store.mro()}: {store}"
@@ -3245,9 +3245,9 @@ def assert_min_num_of_args(func: Callable, num_of_args: int):
32453245 That is, it should have a signature that takes the store as the first argument
32463246 """
32473247 try :
3248- assert len ( Sig ( func ). parameters ) >= num_of_args , (
3249- f"Function { func } doesn't have at least { num_of_args } arguments"
3250- )
3248+ assert (
3249+ len ( Sig ( func ). parameters ) >= num_of_args
3250+ ), f"Function { func } doesn't have at least { num_of_args } arguments"
32513251 except Exception as e :
32523252 warn (
32533253 f"Encountered error checking if { func } can be a store method. "
@@ -3256,11 +3256,24 @@ def assert_min_num_of_args(func: Callable, num_of_args: int):
32563256 )
32573257
32583258
3259+ # --------------------------------------------------------------------------------------
3260+ # Missing key handling
3261+
32593262@store_decorator
3260- def add_missing_key_handling (store = None , * , missing_key_callback : Callable ):
3263+ def add_missing_key_handling (
3264+ store = None ,
3265+ * ,
3266+ missing_key_callback : Callable ,
3267+ errors_that_trigger_missing = (KeyError ,),
3268+ ):
32613269 """Overrides the ``__missing__`` method of a store with a custom callback.
32623270
3263- The callback must have two arguments: the store and the key.
3271+ Note: The callback must have two arguments: the store and the key.
3272+
3273+ Args:
3274+ store: The store class to wrap.
3275+ missing_key_callback: Function(store, key) -> value for missing keys.
3276+ errors_that_trigger_missing: Tuple of exceptions that trigger __missing__.
32643277
32653278 In the following example, we endow a store to return a sub-store when a key is
32663279 missing. This substore will contain only keys that start with that missing key.
@@ -3284,15 +3297,59 @@ def add_missing_key_handling(store=None, *, missing_key_callback: Callable):
32843297 >>> v = s['a/']
32853298 >>> assert dict(v) == {'a/b': 1, 'a/c': 2}
32863299 """
3300+ if isinstance (errors_that_trigger_missing , BaseException ):
3301+ errors_that_trigger_missing = (errors_that_trigger_missing ,)
32873302
32883303 assert_min_num_of_args (missing_key_callback , 2 )
32893304
32903305 @wraps (store , updated = ())
3291- class StoreWithMissingKeyCallaback (store ):
3306+ class StoreWithMissingKeyCallback (store ):
3307+ pass
3308+
3309+ StoreWithMissingKeyCallback .__missing__ = missing_key_callback
3310+ StoreWithMissingKeyCallback ._errors_that_trigger_missing = (
3311+ errors_that_trigger_missing
3312+ )
3313+ return StoreWithMissingKeyCallback
3314+
3315+
3316+ def ignore_if_error (store = None , * , errors = (KeyError ,)):
3317+ def _ignore (store , k ):
32923318 pass
32933319
3294- StoreWithMissingKeyCallaback .__missing__ = missing_key_callback
3295- return StoreWithMissingKeyCallaback
3320+ return add_missing_key_handling (
3321+ store , missing_key_callback = _ignore , errors_that_trigger_missing = errors
3322+ )
3323+
3324+
3325+ def warn_and_ignore_if_error (
3326+ store = None ,
3327+ * ,
3328+ errors = (KeyError ,),
3329+ warn_msg = 'Ignoring error in __getitem__ for key {k}: {e}' ,
3330+ ):
3331+ def _warn (store , k ):
3332+ import sys
3333+
3334+ exc_type , exc_value , _ = sys .exc_info ()
3335+ e = exc_value
3336+ warn (warn_msg .format (k = k , e = e ))
3337+
3338+ return add_missing_key_handling (
3339+ store , missing_key_callback = _warn , errors_that_trigger_missing = errors
3340+ )
3341+
3342+
3343+ def return_default_if_error (store = None , * , default = None , errors = (KeyError ,)):
3344+ def _default (store , k ):
3345+ return default
3346+
3347+ return add_missing_key_handling (
3348+ store , missing_key_callback = _default , errors_that_trigger_missing = errors
3349+ )
3350+
3351+ # --------------------------------------------------------------------------------------
3352+ # Codecs
32963353
32973354
32983355EncodedType = TypeVar ("EncodedType" )
0 commit comments