cdxcore 0.1.28__tar.gz → 0.1.30__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cdxcore might be problematic. Click here for more details.
- {cdxcore-0.1.28 → cdxcore-0.1.30}/PKG-INFO +1 -1
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/__init__.py +1 -1
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/subdir.py +216 -106
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/uniquehash.py +8 -9
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/version.py +7 -1
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore.egg-info/PKG-INFO +1 -1
- {cdxcore-0.1.28 → cdxcore-0.1.30}/pyproject.toml +1 -1
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_subdir.py +83 -2
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_uniquehash.py +9 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/LICENSE +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/README.md +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/config.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/deferred.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/dynalimits.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/dynaplot.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/err.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/filelock.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/jcpool.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/npio.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/npshm.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/pretty.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/util.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore/verbose.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore.egg-info/SOURCES.txt +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore.egg-info/dependency_links.txt +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore.egg-info/requires.txt +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/cdxcore.egg-info/top_level.txt +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/docs/source/conf.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/setup.cfg +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_config.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_deferred.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_err.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_jcpool.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_npio.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_npshm.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_pretty.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_util.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_verbose.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tests/test_version.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tmp/filelock.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tmp/np.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tmp/npsh1.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/tmp/sharedarray.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/up/git_message.py +0 -0
- {cdxcore-0.1.28 → cdxcore-0.1.30}/up/pip_modify_setup.py +0 -0
|
@@ -366,6 +366,7 @@ from collections import OrderedDict
|
|
|
366
366
|
from collections.abc import Collection, Mapping, Callable, Iterable
|
|
367
367
|
from enum import Enum
|
|
368
368
|
from functools import update_wrapper
|
|
369
|
+
from string import Formatter
|
|
369
370
|
|
|
370
371
|
import json as json
|
|
371
372
|
import gzip as gzip
|
|
@@ -375,7 +376,7 @@ from .err import verify, error, warn, fmt as txtfmt
|
|
|
375
376
|
from .pretty import PrettyObject
|
|
376
377
|
from .verbose import Context
|
|
377
378
|
from .version import Version, version as version_decorator, VersionError
|
|
378
|
-
from .util import fmt_list, fmt_filename, DEF_FILE_NAME_MAP, plain, is_filename
|
|
379
|
+
from .util import fmt_list, fmt_filename, DEF_FILE_NAME_MAP, plain, is_filename, fmt_dict
|
|
379
380
|
from .uniquehash import unique_hash48, UniqueLabel, NamedUniqueHash, named_unique_filename48_8
|
|
380
381
|
|
|
381
382
|
"""
|
|
@@ -1031,8 +1032,9 @@ class SubDir(object):
|
|
|
1031
1032
|
"""
|
|
1032
1033
|
Delete all of the current directory if ``tclean`` is ``True``
|
|
1033
1034
|
"""
|
|
1034
|
-
if self
|
|
1035
|
+
if getattr(self, "_tclean", False):
|
|
1035
1036
|
self.delete_everything(keep_directory=False)
|
|
1037
|
+
self._path = None
|
|
1036
1038
|
|
|
1037
1039
|
@staticmethod
|
|
1038
1040
|
def expand_std_root( name ):
|
|
@@ -1078,7 +1080,9 @@ class SubDir(object):
|
|
|
1078
1080
|
|
|
1079
1081
|
def path_exists(self) -> bool:
|
|
1080
1082
|
""" Whether the current directory exists """
|
|
1081
|
-
|
|
1083
|
+
if self._path is None:
|
|
1084
|
+
return False
|
|
1085
|
+
return os.path.exists( self._path[:-1] )
|
|
1082
1086
|
|
|
1083
1087
|
# -- a few basic properties --
|
|
1084
1088
|
|
|
@@ -2193,8 +2197,11 @@ class SubDir(object):
|
|
|
2193
2197
|
----------
|
|
2194
2198
|
delete_self: bool
|
|
2195
2199
|
Whether to delete the directory itself as well, or only its contents.
|
|
2200
|
+
If ``True``, the current object will be left in ``None`` state.
|
|
2201
|
+
|
|
2196
2202
|
raise_on_error: bool
|
|
2197
2203
|
``False`` for silent failure
|
|
2204
|
+
|
|
2198
2205
|
ext : str | None, default ``None``
|
|
2199
2206
|
Extension for keys, or ``None`` for the directory's default.
|
|
2200
2207
|
Use ``""`` to match all files regardless of extension.
|
|
@@ -2228,8 +2235,9 @@ class SubDir(object):
|
|
|
2228
2235
|
Deletes the entire sub directory will all contents.
|
|
2229
2236
|
|
|
2230
2237
|
*WARNING:* deletes *all* files and sub-directories, not just those with the present extension.
|
|
2231
|
-
If ``keep_directory`` is ``False``, the directory referred to by this object will also be deleted.
|
|
2232
|
-
|
|
2238
|
+
If ``keep_directory`` is ``False``, then the directory referred to by this object will also be deleted.
|
|
2239
|
+
|
|
2240
|
+
In this case, ``self`` will be set to ``None`` state.
|
|
2233
2241
|
"""
|
|
2234
2242
|
if self._path is None:
|
|
2235
2243
|
return
|
|
@@ -2801,15 +2809,16 @@ class SubDir(object):
|
|
|
2801
2809
|
# caching
|
|
2802
2810
|
# -------
|
|
2803
2811
|
|
|
2804
|
-
def cache( self, version
|
|
2805
|
-
dependencies
|
|
2806
|
-
label
|
|
2807
|
-
uid
|
|
2808
|
-
name
|
|
2809
|
-
exclude_args
|
|
2810
|
-
include_args
|
|
2811
|
-
exclude_arg_types
|
|
2812
|
-
version_auto_class
|
|
2812
|
+
def cache( self, version : str|None = None , *,
|
|
2813
|
+
dependencies : list|None = None,
|
|
2814
|
+
label : Callable|None = None,
|
|
2815
|
+
uid : Callable|None = None,
|
|
2816
|
+
name : str|None = None,
|
|
2817
|
+
exclude_args : list[str]|None = None,
|
|
2818
|
+
include_args : list[str]|None = None,
|
|
2819
|
+
exclude_arg_types : list[type]|None = None,
|
|
2820
|
+
version_auto_class : bool = True,
|
|
2821
|
+
name_of_func_name_arg: str = "func_name"):
|
|
2813
2822
|
"""
|
|
2814
2823
|
Advanced versioned caching for callables.
|
|
2815
2824
|
|
|
@@ -2971,12 +2980,9 @@ class SubDir(object):
|
|
|
2971
2980
|
return x*y
|
|
2972
2981
|
|
|
2973
2982
|
We can also use a function to generate a ``label``. In that case all parameters
|
|
2974
|
-
to the function including its ``
|
|
2975
|
-
we eat any parameters we are not interested in with ``** _``:
|
|
2976
|
-
|
|
2977
|
-
.. code-block:: python
|
|
2983
|
+
to the function including its ``func_name`` are passed to the function.::
|
|
2978
2984
|
|
|
2979
|
-
@cache.cache("0.1", label=lambda x,y
|
|
2985
|
+
@cache.cache("0.1", label=lambda x,y: f"h({x},{y})", exclude_args='debug')
|
|
2980
2986
|
def h(x,y,debug=False):
|
|
2981
2987
|
if debug:
|
|
2982
2988
|
print(f"h(x={x},y={y})")
|
|
@@ -3010,7 +3016,7 @@ class SubDir(object):
|
|
|
3010
3016
|
|
|
3011
3017
|
.. code-block:: python
|
|
3012
3018
|
|
|
3013
|
-
@cache.cache("0.1", uid=lambda x,y
|
|
3019
|
+
@cache.cache("0.1", uid=lambda x,y: f"h2({x},{y})", exclude_args='debug')
|
|
3014
3020
|
def h2(x,y,debug=False):
|
|
3015
3021
|
if debug:
|
|
3016
3022
|
print(f"h(x={x},y={y})")
|
|
@@ -3158,7 +3164,7 @@ class SubDir(object):
|
|
|
3158
3164
|
return self.x*y
|
|
3159
3165
|
|
|
3160
3166
|
a = A(x=1)
|
|
3161
|
-
f = cache.cache("0.1",
|
|
3167
|
+
f = cache.cache("0.1", uid=lambda self, y : f"a.f({y})")(a.f) # <- decorate bound 'f'.
|
|
3162
3168
|
r = c(y=2)
|
|
3163
3169
|
|
|
3164
3170
|
In this case the function ``f`` is bound to ``a``. The object is added as ``self`` to the function
|
|
@@ -3223,7 +3229,7 @@ class SubDir(object):
|
|
|
3223
3229
|
@cache.cache_class("0.1")
|
|
3224
3230
|
class A(object):
|
|
3225
3231
|
|
|
3226
|
-
@cache.cache_init(
|
|
3232
|
+
@cache.cache_init(uid=lambda x, debug: f"A.__init__(x={x})") # <-- 'self' is not passed to the lambda function
|
|
3227
3233
|
def __init__(self, x, debug):
|
|
3228
3234
|
if debug:
|
|
3229
3235
|
print("__init__",x)
|
|
@@ -3296,7 +3302,8 @@ class SubDir(object):
|
|
|
3296
3302
|
See :dec:`cdxcore.version.version` for details on name lookup if strings are used.
|
|
3297
3303
|
|
|
3298
3304
|
label : str | Callable | None, default ``None``
|
|
3299
|
-
Specify a human-
|
|
3305
|
+
Specify a human-readable label for the function call given its parameters.
|
|
3306
|
+
|
|
3300
3307
|
This label is used to generate the cache file name, and is also printed in when tracing
|
|
3301
3308
|
hashing operations. Labels are not assumed to be unique, hence a unique hash of
|
|
3302
3309
|
the label and the parameters to this function will be appended to generate
|
|
@@ -3307,14 +3314,22 @@ class SubDir(object):
|
|
|
3307
3314
|
|
|
3308
3315
|
**Usage:**
|
|
3309
3316
|
|
|
3317
|
+
* If ``label`` is a ``Callable`` then ``label( func_name=name, **parameters )`` will be called
|
|
3318
|
+
to generate the actual label.
|
|
3319
|
+
|
|
3320
|
+
The parameter ``func_name`` refers to the qualified
|
|
3321
|
+
name of the function. Its value can be overwitten by ``name``, while the parameter name itself
|
|
3322
|
+
can be overwritten using ``name_of_func_name_arg``, see below.
|
|
3323
|
+
|
|
3310
3324
|
* If ``label`` is a plain string without ``{}`` formatting: use this string as-is.
|
|
3311
3325
|
|
|
3312
|
-
* If ``label`` is a string with ``{}`` formatting, then ``label.format(
|
|
3313
|
-
will be used to generate the actual label.
|
|
3326
|
+
* If ``label`` is a string with ``{}`` formatting, then ``label.format( func_name=name, **parameters )``
|
|
3327
|
+
will be used to generate the actual label.
|
|
3328
|
+
|
|
3329
|
+
The parameter ``func_name`` refers to the qualified
|
|
3330
|
+
name of the function. Its value can be overwitten by ``name``, while the parameter name itself
|
|
3331
|
+
can be overwritten using ``name_of_func_name_arg``, see below.
|
|
3314
3332
|
|
|
3315
|
-
* If ``label`` is a ``Callable`` then ``label( name=name, **parameters )`` will be called
|
|
3316
|
-
to generate the actual label.
|
|
3317
|
-
|
|
3318
3333
|
See above for examples.
|
|
3319
3334
|
|
|
3320
3335
|
``label`` cannot be used alongside ``uid``.
|
|
@@ -3329,7 +3344,7 @@ class SubDir(object):
|
|
|
3329
3344
|
|
|
3330
3345
|
name : str | None, default ``None``
|
|
3331
3346
|
Name of this function which is used either on its own if neither ``label`` not ``uid`` are used,
|
|
3332
|
-
or which passed as a parameter ``
|
|
3347
|
+
or which passed as a parameter ``func_name`` to either the callable or the
|
|
3333
3348
|
formatting operator. See above for more details.
|
|
3334
3349
|
|
|
3335
3350
|
If ``name`` is not specified it defaults to ``__qualname__`` expanded
|
|
@@ -3350,6 +3365,34 @@ class SubDir(object):
|
|
|
3350
3365
|
Whether to automaticallty add version dependencies on base classes or, for member functions, on containing
|
|
3351
3366
|
classes. This is the ``auto_class`` parameter for :dec:`cdxcore.version.version`.
|
|
3352
3367
|
|
|
3368
|
+
name_of_func_name_arg : str, default ``"func_name"``
|
|
3369
|
+
When formatting ``label`` or ``uid``, by default ``"func_name"`` is used to refer to the current
|
|
3370
|
+
function name. If there is already a parameter ``func_name`` for the function, an error will be raised.
|
|
3371
|
+
Use this flag to change the parameter name. Example::
|
|
3372
|
+
|
|
3373
|
+
.. code-block:: python
|
|
3374
|
+
|
|
3375
|
+
from cdxcore.subdir import SubDir
|
|
3376
|
+
cache = SubDir("?/temp")
|
|
3377
|
+
|
|
3378
|
+
@cache.cache("0.1")
|
|
3379
|
+
def f( func_name, x ):
|
|
3380
|
+
pass
|
|
3381
|
+
|
|
3382
|
+
f("test", 1)
|
|
3383
|
+
|
|
3384
|
+
Generates a :class:`RuntimeError` ``f@__main__: 'func_name' is a reserved keyword
|
|
3385
|
+
and used as formatting parameter
|
|
3386
|
+
name for the function name. Found it also in the function parameter list. Use 'name_of_name_arg' to change the internal parameter name used.``.
|
|
3387
|
+
|
|
3388
|
+
Instead, use:
|
|
3389
|
+
|
|
3390
|
+
.. code-block:: python
|
|
3391
|
+
|
|
3392
|
+
@cache.cache("0.1", label=lambda new_func_name, func_name, x : f"{new_func_name}(): {func_name} {x}", name_of_func_name_arg="new_func_name")
|
|
3393
|
+
def f( func_name, x ):
|
|
3394
|
+
pass
|
|
3395
|
+
|
|
3353
3396
|
Returns
|
|
3354
3397
|
-------
|
|
3355
3398
|
Decorated F: Callable
|
|
@@ -3421,7 +3464,8 @@ class SubDir(object):
|
|
|
3421
3464
|
exclude_args = exclude_args,
|
|
3422
3465
|
include_args = include_args,
|
|
3423
3466
|
exclude_arg_types = exclude_arg_types,
|
|
3424
|
-
version_auto_class = version_auto_class
|
|
3467
|
+
version_auto_class = version_auto_class,
|
|
3468
|
+
name_of_func_name_arg = name_of_func_name_arg)
|
|
3425
3469
|
|
|
3426
3470
|
def cache_class( self,
|
|
3427
3471
|
version : str = None , *,
|
|
@@ -3633,7 +3677,7 @@ def _ensure_has_version( F,
|
|
|
3633
3677
|
dependencies=dependencies,
|
|
3634
3678
|
auto_class=auto_class)(F)
|
|
3635
3679
|
|
|
3636
|
-
def _qualified_name( F, name ):
|
|
3680
|
+
def _qualified_name( F, name = None ):
|
|
3637
3681
|
"""
|
|
3638
3682
|
Return qualified name including module name, robustly
|
|
3639
3683
|
"""
|
|
@@ -3652,6 +3696,36 @@ def _qualified_name( F, name ):
|
|
|
3652
3696
|
warn( f"Cannot determine module name for '{name}' of {type(F)}" )
|
|
3653
3697
|
return name
|
|
3654
3698
|
|
|
3699
|
+
def _expected_str_fmt_args(fmt: str):
|
|
3700
|
+
"""
|
|
3701
|
+
Inspect a format string and report what arguments it expects.
|
|
3702
|
+
Returns:
|
|
3703
|
+
- auto_positional: count of automatic '{}' fields
|
|
3704
|
+
- positional_indices: explicit numeric field indices used (e.g., {0}, {2})
|
|
3705
|
+
- keywords: named fields used (e.g., {user}, {price})
|
|
3706
|
+
"""
|
|
3707
|
+
f = Formatter()
|
|
3708
|
+
pos = set()
|
|
3709
|
+
auto = 0
|
|
3710
|
+
kws = set()
|
|
3711
|
+
|
|
3712
|
+
for literal, field, spec, conv in f.parse(fmt):
|
|
3713
|
+
if field is None:
|
|
3714
|
+
continue
|
|
3715
|
+
# Keep only the first identifier before attribute/index access
|
|
3716
|
+
head = field.split('.')[0].split('[')[0]
|
|
3717
|
+
if head == "": # '{}' → automatic positional
|
|
3718
|
+
auto += 1
|
|
3719
|
+
elif head.isdigit(): # '{0}', '{2}' → explicit positional
|
|
3720
|
+
pos.add(int(head))
|
|
3721
|
+
else: # '{name}' → keyword
|
|
3722
|
+
kws.add(head)
|
|
3723
|
+
|
|
3724
|
+
return PrettyObject( positional=auto,
|
|
3725
|
+
posindices=pos,
|
|
3726
|
+
keywords=kws
|
|
3727
|
+
)
|
|
3728
|
+
|
|
3655
3729
|
class CacheCallable(object):
|
|
3656
3730
|
"""
|
|
3657
3731
|
Wrapper for a cached function.
|
|
@@ -3660,17 +3734,17 @@ class CacheCallable(object):
|
|
|
3660
3734
|
"""
|
|
3661
3735
|
|
|
3662
3736
|
def __init__(self,
|
|
3663
|
-
subdir
|
|
3664
|
-
version
|
|
3665
|
-
dependencies
|
|
3666
|
-
label
|
|
3667
|
-
uid
|
|
3668
|
-
name
|
|
3669
|
-
exclude_args
|
|
3670
|
-
include_args
|
|
3671
|
-
exclude_arg_types
|
|
3672
|
-
version_auto_class
|
|
3673
|
-
|
|
3737
|
+
subdir : SubDir, *,
|
|
3738
|
+
version : str = None,
|
|
3739
|
+
dependencies : list,
|
|
3740
|
+
label : Callable = None,
|
|
3741
|
+
uid : Callable = None,
|
|
3742
|
+
name : str = None,
|
|
3743
|
+
exclude_args : set[str] = None,
|
|
3744
|
+
include_args : set[str] = None,
|
|
3745
|
+
exclude_arg_types : set[type] = None,
|
|
3746
|
+
version_auto_class : bool = True,
|
|
3747
|
+
name_of_func_name_arg: str = "name"):
|
|
3674
3748
|
"""
|
|
3675
3749
|
Utility class for :dec:`cdxcore.subdir.SubDir.cache`.
|
|
3676
3750
|
|
|
@@ -3679,18 +3753,38 @@ class CacheCallable(object):
|
|
|
3679
3753
|
if not label is None and not uid is None:
|
|
3680
3754
|
error("Cannot specify both 'label' and 'uid'.")
|
|
3681
3755
|
|
|
3682
|
-
self._subdir
|
|
3683
|
-
self.
|
|
3684
|
-
self._dependencies
|
|
3685
|
-
self._label
|
|
3686
|
-
self._uid
|
|
3687
|
-
self._name
|
|
3688
|
-
self._exclude_args
|
|
3689
|
-
self._include_args
|
|
3690
|
-
self._exclude_arg_types
|
|
3691
|
-
self._version_auto_class
|
|
3692
|
-
self.
|
|
3756
|
+
self._subdir = SubDir(subdir)
|
|
3757
|
+
self._input_version = str(version) if not version is None else None
|
|
3758
|
+
self._dependencies = list(dependencies) if not dependencies is None else None
|
|
3759
|
+
self._label = label
|
|
3760
|
+
self._uid = uid
|
|
3761
|
+
self._name = str(name) if not name is None else None
|
|
3762
|
+
self._exclude_args = set(exclude_args) if not exclude_args is None and len(exclude_args) > 0 else None
|
|
3763
|
+
self._include_args = set(include_args) if not include_args is None and len(include_args) > 0 else None
|
|
3764
|
+
self._exclude_arg_types = set(exclude_arg_types) if not exclude_arg_types is None and len(exclude_arg_types) > 0 else None
|
|
3765
|
+
self._version_auto_class = bool(version_auto_class)
|
|
3766
|
+
self._name_of_func_name_arg = str(name_of_func_name_arg)
|
|
3767
|
+
self._uid_label_params = None
|
|
3693
3768
|
|
|
3769
|
+
if not self.uid_or_label is None:
|
|
3770
|
+
F = self.uid_or_label
|
|
3771
|
+
which = "'uid'" if not uid is None else "'label'"
|
|
3772
|
+
if isinstance( F, str ):
|
|
3773
|
+
r = _expected_str_fmt_args( F )
|
|
3774
|
+
if r.positional + len(r.posindices) > 0:
|
|
3775
|
+
raise ValueError("f{which} '{F}' cannot have positional arguments (empty brackets {} or brackets with integer position {1}). Use only named arguments.")
|
|
3776
|
+
self._uid_label_params = list(r.keywords)
|
|
3777
|
+
del r
|
|
3778
|
+
else:
|
|
3779
|
+
if not inspect.isfunction(F):
|
|
3780
|
+
if not callable(F):
|
|
3781
|
+
raise ValueError(f"{which} '{_qualified_name(F)}' is not callable")
|
|
3782
|
+
F = F.__call__
|
|
3783
|
+
assert inspect.isfunction(F), ("Internal error - function expected")
|
|
3784
|
+
self._uid_label_params = list( inspect.signature(F).parameters )
|
|
3785
|
+
del F, which
|
|
3786
|
+
self._uid_label_params = self._uid_label_params if len(self._uid_label_params) > 0 else None
|
|
3787
|
+
|
|
3694
3788
|
@property
|
|
3695
3789
|
def uid_or_label(self) -> Callable:
|
|
3696
3790
|
""" ID or label """
|
|
@@ -3723,6 +3817,10 @@ class CacheCallable(object):
|
|
|
3723
3817
|
def global_exclude_arg_types(self) -> list[type]:
|
|
3724
3818
|
""" Returns ``exclude_arg_types`` of the underlying :class:`cdxcore.subdir.CacheController` """
|
|
3725
3819
|
return self.cache_controller.exclude_arg_types
|
|
3820
|
+
@property
|
|
3821
|
+
def uid_label_params(self) -> list:
|
|
3822
|
+
""" Returns the ``set`` of parameters the ``uid`` or ``label`` function expects """
|
|
3823
|
+
return self._uid_label_params
|
|
3726
3824
|
|
|
3727
3825
|
def __call__(self, F : Callable):
|
|
3728
3826
|
"""
|
|
@@ -3774,7 +3872,7 @@ class CacheCallable(object):
|
|
|
3774
3872
|
# apply version
|
|
3775
3873
|
# this also ensures that __init__ picks up a version dependency on the class itse
|
|
3776
3874
|
# (as we forceed 'auto_class' to be true)
|
|
3777
|
-
C = _ensure_has_version( C, version=self.
|
|
3875
|
+
C = _ensure_has_version( C, version=self._input_version,
|
|
3778
3876
|
dependencies=self._dependencies,
|
|
3779
3877
|
auto_class=self._version_auto_class)
|
|
3780
3878
|
|
|
@@ -3795,7 +3893,7 @@ class CacheCallable(object):
|
|
|
3795
3893
|
# Cannot currently decorate classes.
|
|
3796
3894
|
|
|
3797
3895
|
|
|
3798
|
-
is_method = inspect.ismethod(F)
|
|
3896
|
+
is_method = inspect.ismethod(F) # for *bound* methods
|
|
3799
3897
|
if is_method:
|
|
3800
3898
|
assert not getattr(F, "__self__", None) is None, ("Method type must have __self__...?", F.__qualname__ )
|
|
3801
3899
|
elif not inspect.isfunction(F):
|
|
@@ -3858,11 +3956,12 @@ class CacheCallable(object):
|
|
|
3858
3956
|
# -------
|
|
3859
3957
|
# Decorate now or pick up existing @version
|
|
3860
3958
|
|
|
3861
|
-
F = _ensure_has_version( F, version=self.
|
|
3959
|
+
F = _ensure_has_version( F, version=self._input_version,
|
|
3862
3960
|
dependencies=self._dependencies,
|
|
3863
3961
|
auto_class=self._version_auto_class,
|
|
3864
3962
|
allow_default=is_new )
|
|
3865
|
-
|
|
3963
|
+
version = F.version.unique_id64
|
|
3964
|
+
|
|
3866
3965
|
# name
|
|
3867
3966
|
# ----
|
|
3868
3967
|
|
|
@@ -3892,38 +3991,46 @@ class CacheCallable(object):
|
|
|
3892
3991
|
|
|
3893
3992
|
uid_or_label = self.uid_or_label
|
|
3894
3993
|
filename = None
|
|
3895
|
-
if
|
|
3896
|
-
#
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
try:
|
|
3900
|
-
filename = uid_or_label.format() # throws a KeyError if 'id' contains formatting information
|
|
3901
|
-
except KeyError:
|
|
3902
|
-
pass
|
|
3994
|
+
if self.unique and self._uid_label_params is None:
|
|
3995
|
+
# the string or function do not require any parameters, and is unique
|
|
3996
|
+
assert not uid_or_label is None
|
|
3997
|
+
filename = uid_or_label if isinstance( uid_or_label, str ) else uid_or_label()
|
|
3903
3998
|
|
|
3904
|
-
if not filename is None:
|
|
3905
|
-
# generate name with the unique string provided by the user
|
|
3906
3999
|
if not is_filename(filename):
|
|
3907
4000
|
raise ValueError(f"The unique filename '{filename}' computed for '{name}' contains invalid characters for filename. When using `uid` make sure that "+\
|
|
3908
|
-
"the returned ID is a valid filename (and unique)")
|
|
4001
|
+
"the returned ID is a valid filename (and is unique)")
|
|
3909
4002
|
label = filename
|
|
3910
|
-
filename = self.uniqueFileName( filename )
|
|
3911
4003
|
arguments = None
|
|
3912
|
-
|
|
4004
|
+
|
|
3913
4005
|
else:
|
|
4006
|
+
# need the list of parameters to compute a hash and/or a label
|
|
4007
|
+
which = 'uid' if not self._uid is None else 'label'
|
|
4008
|
+
|
|
3914
4009
|
# get dictionary of named arguments
|
|
3915
4010
|
arguments = execute.cache_info.signature.bind(*args,**kwargs)
|
|
3916
4011
|
arguments.apply_defaults()
|
|
3917
4012
|
arguments = arguments.arguments # ordered dict
|
|
3918
4013
|
|
|
4014
|
+
# delete 'cls' from argument list for class functions
|
|
3919
4015
|
if is_new:
|
|
3920
|
-
# delete 'cls' from argument list
|
|
3921
4016
|
assert len(arguments) >= 1, ("*** Internal error", F.__qualname__, is_new, arguments)
|
|
3922
4017
|
del arguments[list(arguments)[0]]
|
|
3923
|
-
|
|
4018
|
+
|
|
4019
|
+
# add 'self' for methods
|
|
4020
|
+
if is_method:
|
|
4021
|
+
# add __self__ to the beginning of all arguments
|
|
4022
|
+
full_arguments = OrderedDict()
|
|
4023
|
+
if is_method:
|
|
4024
|
+
if 'self' in set(arguments):
|
|
4025
|
+
raise RuntimeError(f"'self' found in bound method '{name}' argument list {fmt_dict(execute.cache_info.signature.bind(*args,**kwargs).arguments)}.")
|
|
4026
|
+
full_arguments['self'] = F.__self__
|
|
4027
|
+
full_arguments |= arguments
|
|
4028
|
+
arguments = full_arguments
|
|
4029
|
+
del full_arguments
|
|
3924
4030
|
|
|
3925
4031
|
# filter dictionary
|
|
3926
4032
|
if not self._exclude_args is None or not self._include_args is None:
|
|
4033
|
+
argus = set(arguments)
|
|
3927
4034
|
excl = set(self._exclude_args) if not self._exclude_args is None else set()
|
|
3928
4035
|
if not self._exclude_args is None:
|
|
3929
4036
|
if self._exclude_args > argus:
|
|
@@ -3937,7 +4044,7 @@ class CacheCallable(object):
|
|
|
3937
4044
|
for arg in excl:
|
|
3938
4045
|
if arg in arguments:
|
|
3939
4046
|
del arguments[arg]
|
|
3940
|
-
del excl
|
|
4047
|
+
del excl, argus
|
|
3941
4048
|
|
|
3942
4049
|
if len(exclude_types) > 0:
|
|
3943
4050
|
excl = []
|
|
@@ -3948,41 +4055,45 @@ class CacheCallable(object):
|
|
|
3948
4055
|
if arg in arguments:
|
|
3949
4056
|
del arguments[arg]
|
|
3950
4057
|
|
|
3951
|
-
# did the user provide a label or unique ID?
|
|
3952
4058
|
if uid_or_label is None:
|
|
4059
|
+
# no label or unique ID
|
|
4060
|
+
assert not self.unique
|
|
3953
4061
|
uid_or_label = name
|
|
3954
4062
|
|
|
4063
|
+
elif self._uid_label_params is None:
|
|
4064
|
+
# label function or string does not need any parameters
|
|
4065
|
+
assert not self.unique
|
|
4066
|
+
uid_or_label = uid_or_label if isinstance( uid_or_label, str ) else uid_or_label()
|
|
4067
|
+
|
|
3955
4068
|
else:
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
4069
|
+
# function or format string required parameters
|
|
4070
|
+
# add parameters in order of label/uid parameters
|
|
4071
|
+
assert not self._uid_label_params is None
|
|
4072
|
+
|
|
4073
|
+
fmt_arguments = {}
|
|
4074
|
+
for k in self._uid_label_params:
|
|
4075
|
+
if k == self._name_of_func_name_arg:
|
|
4076
|
+
if self._name_of_func_name_arg in arguments:
|
|
4077
|
+
error(f"{name}: '{self._name_of_func_name_arg}' is a reserved keyword for '{which}' which refers to the current function name. "
|
|
4078
|
+
"Found it also in the function parameter list. Use 'name_of_func_name_arg' to change the internal parameter name used.")
|
|
4079
|
+
fmt_arguments[k] = name
|
|
4080
|
+
else:
|
|
4081
|
+
if not k in arguments:
|
|
4082
|
+
args_ = [ f"'{_}'" for _ in arguments ]
|
|
4083
|
+
raise ValueError(f"Error while generating '{which}' for '{name}': formatting function expected a parameter '{k}' which is not present "+\
|
|
4084
|
+
f"in the list of parameters passed to '{name}': {fmt_list(args_)}.")
|
|
4085
|
+
fmt_arguments[k] = arguments[k]
|
|
3969
4086
|
|
|
3970
4087
|
# call format or function
|
|
3971
4088
|
if isinstance( uid_or_label, str ):
|
|
3972
|
-
|
|
3973
|
-
uid_or_label = str.format( uid_or_label, **arguments )
|
|
3974
|
-
except KeyError as e:
|
|
3975
|
-
raise KeyError(e, f"Error while generating id for '{name}' using format string '{uid_or_label}': {e}. Available arguments: {list(arguments)}")
|
|
3976
|
-
|
|
4089
|
+
uid_or_label = str.format( uid_or_label, **fmt_arguments )
|
|
3977
4090
|
else:
|
|
3978
|
-
which = 'uid' if not self._uid is None else 'label'
|
|
3979
4091
|
try:
|
|
3980
|
-
uid_or_label = uid_or_label(**
|
|
3981
|
-
except TypeError as e:
|
|
3982
|
-
raise TypeError(e, f"Error while generating '{which}' for '{name}' using a function: {e}. Available arguments: {list(arguments)}")
|
|
4092
|
+
uid_or_label = uid_or_label(**fmt_arguments)
|
|
3983
4093
|
except Exception as e:
|
|
3984
|
-
raise type(e)(f"Error while generating '{which}' for '{name}': attempt to call '{which}' of type {type(uid_or_label)} failed: {e}")
|
|
3985
|
-
|
|
4094
|
+
raise type(e)(f"Error while generating '{which}' for '{name}': attempt to call '{which}' of callable type {type(uid_or_label)} failed: {e}")
|
|
4095
|
+
if not isinstance(uid_or_label, str):
|
|
4096
|
+
raise ValueError("Error calling callable '{which}' for '{name}': callable must return a string. Found {type(uid_or_label))}")
|
|
3986
4097
|
|
|
3987
4098
|
if self.unique:
|
|
3988
4099
|
if not is_filename(uid_or_label):
|
|
@@ -3998,7 +4109,6 @@ class CacheCallable(object):
|
|
|
3998
4109
|
# determine version, cache mode
|
|
3999
4110
|
# ------------------
|
|
4000
4111
|
|
|
4001
|
-
version_ = self._version if not self._version is None else F.version.unique_id64
|
|
4002
4112
|
cache_mode = CacheMode(override_cache_mode) if not override_cache_mode is None else self.cache_mode
|
|
4003
4113
|
del override_cache_mode
|
|
4004
4114
|
|
|
@@ -4007,7 +4117,7 @@ class CacheCallable(object):
|
|
|
4007
4117
|
|
|
4008
4118
|
execute.cache_info.label = str(label) if not label is None else None
|
|
4009
4119
|
execute.cache_info.filename = filename # that is the unique ID for this call
|
|
4010
|
-
execute.cache_info.version =
|
|
4120
|
+
execute.cache_info.version = version
|
|
4011
4121
|
|
|
4012
4122
|
if self.cache_controller.keep_last_arguments:
|
|
4013
4123
|
info_arguments = OrderedDict()
|
|
@@ -4026,11 +4136,11 @@ class CacheCallable(object):
|
|
|
4026
4136
|
pass
|
|
4027
4137
|
tag = Tag()
|
|
4028
4138
|
if not is_new:
|
|
4029
|
-
r = self._subdir.read( filename, tag, version=
|
|
4139
|
+
r = self._subdir.read( filename, tag, version=version )
|
|
4030
4140
|
else:
|
|
4031
4141
|
try:
|
|
4032
4142
|
execute.__new_during_read = True
|
|
4033
|
-
r = self._subdir.read( filename, tag, version=
|
|
4143
|
+
r = self._subdir.read( filename, tag, version=version )
|
|
4034
4144
|
finally:
|
|
4035
4145
|
execute.__new_during_read = False
|
|
4036
4146
|
|
|
@@ -4039,7 +4149,7 @@ class CacheCallable(object):
|
|
|
4039
4149
|
track_cached_files += self._fullFileName(filename)
|
|
4040
4150
|
execute.cache_info.last_cached = True
|
|
4041
4151
|
if not debug_verbose is None:
|
|
4042
|
-
debug_verbose.write(f"cache({name}): read '{label}' version 'version {
|
|
4152
|
+
debug_verbose.write(f"cache({name}): read '{label}' version 'version {version}' from cache '{self._subdir.full_file_name(filename)}'.")
|
|
4043
4153
|
if is_new:
|
|
4044
4154
|
assert r.__magic_cache_call_init__ is None, ("**** Internal error. __init__ should reset __magic_cache_call_init__", F.__qualname__, label)
|
|
4045
4155
|
r.__magic_cache_call_init__ = False # since we called __new__, __init__ will be called next
|
|
@@ -4060,7 +4170,7 @@ class CacheCallable(object):
|
|
|
4060
4170
|
assert r.__magic_cache_call_init__ is None, ("**** Internal error. __init__ should reset __magic_cache_call_init__")
|
|
4061
4171
|
|
|
4062
4172
|
if cache_mode.write:
|
|
4063
|
-
self._subdir.write(filename,r,version=
|
|
4173
|
+
self._subdir.write(filename,r,version=version)
|
|
4064
4174
|
if not track_cached_files is None:
|
|
4065
4175
|
track_cached_files += self._subdir.full_file_name(filename)
|
|
4066
4176
|
execute.cache_info.last_cached = False
|
|
@@ -4072,9 +4182,9 @@ class CacheCallable(object):
|
|
|
4072
4182
|
|
|
4073
4183
|
if not debug_verbose is None:
|
|
4074
4184
|
if cache_mode.write:
|
|
4075
|
-
debug_verbose.write(f"cache({name}): called '{label}' version 'version {
|
|
4185
|
+
debug_verbose.write(f"cache({name}): called '{label}' version 'version {version}' and wrote result into '{self._subdir.full_file_name(filename)}'.")
|
|
4076
4186
|
else:
|
|
4077
|
-
debug_verbose.write(f"cache({name}): called '{label}' version 'version {
|
|
4187
|
+
debug_verbose.write(f"cache({name}): called '{label}' version 'version {version}' but did *not* write into '{self._subdir.full_file_name(filename)}'.")
|
|
4078
4188
|
|
|
4079
4189
|
if return_cache_uid:
|
|
4080
4190
|
return filename, r
|
|
@@ -266,11 +266,10 @@ class UniqueHash( object ):
|
|
|
266
266
|
""" Return copy of `self`. """
|
|
267
267
|
return UniqueHash( **{ k:v for k,v in self.__dict__.items() if not k[:1] == "_"} )
|
|
268
268
|
|
|
269
|
-
def __call__(
|
|
269
|
+
def __call__(__self__, # LEAVE THIS NAME. **kwargs might contain 'self' arguments.
|
|
270
|
+
*args, debug_trace : DebugTrace = None, **kwargs) -> str:
|
|
270
271
|
"""
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
Returns a unique hash for the `arg` and `kwargs` parameters passed to this function.
|
|
272
|
+
Returns a unique hash for the ``arg`` and ``kwargs`` parameters passed to this function.
|
|
274
273
|
|
|
275
274
|
Example::
|
|
276
275
|
|
|
@@ -289,7 +288,7 @@ class UniqueHash( object ):
|
|
|
289
288
|
args, kwargs:
|
|
290
289
|
Parameters to hash.
|
|
291
290
|
|
|
292
|
-
debug_trace : :class:`cdxcore.uniquehash.DebugTrace`
|
|
291
|
+
debug_trace : :class:`cdxcore.uniquehash.DebugTrace` | None, default ``None``
|
|
293
292
|
Allows tracing of hashing activity for debugging purposes.
|
|
294
293
|
Two implementations of ``DebugTrace`` are available:
|
|
295
294
|
|
|
@@ -302,13 +301,13 @@ class UniqueHash( object ):
|
|
|
302
301
|
Returns
|
|
303
302
|
-------
|
|
304
303
|
Hash : str
|
|
305
|
-
String of at most
|
|
304
|
+
String of at most ``self.length``.
|
|
306
305
|
"""
|
|
307
|
-
h, _ =
|
|
306
|
+
h, _ = __self__._mk_blake( h=__self__.length//2 )
|
|
308
307
|
if len(args) > 0:
|
|
309
|
-
|
|
308
|
+
__self__._hash_any( h, args, debug_trace = debug_trace )
|
|
310
309
|
if len(kwargs) > 0:
|
|
311
|
-
|
|
310
|
+
__self__._hash_any( h, kwargs, debug_trace = debug_trace )
|
|
312
311
|
return h.hexdigest()
|
|
313
312
|
|
|
314
313
|
# Utility functions
|
|
@@ -655,7 +655,13 @@ def version( version : str = "0.0.1" ,
|
|
|
655
655
|
if not gversion._class is None:
|
|
656
656
|
continue
|
|
657
657
|
gversion._class = f
|
|
658
|
-
|
|
658
|
+
|
|
659
|
+
version_ = Version(f, version, dep, auto_class=auto_class )
|
|
660
|
+
try:
|
|
661
|
+
f.version = version_
|
|
662
|
+
except AttributeError:
|
|
663
|
+
f.__dict__['version'] = version_
|
|
664
|
+
del version_
|
|
659
665
|
assert type(f.version).__name__ == Version.__name__
|
|
660
666
|
return f
|
|
661
667
|
return wrap
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "cdxcore"
|
|
9
|
-
version = "0.1.
|
|
9
|
+
version = "0.1.30"
|
|
10
10
|
description = "Basic Python Tools; upgraded cdxbasics"
|
|
11
11
|
authors = [{ name = "Hans Buehler", email = "github@buehler.london" }]
|
|
12
12
|
readme = "README.md"
|
|
@@ -26,6 +26,7 @@ import_local()
|
|
|
26
26
|
Imports
|
|
27
27
|
"""
|
|
28
28
|
from cdxcore.subdir import SubDir, CacheMode, VersionError, VersionPresentError, VersionedCacheRoot
|
|
29
|
+
from cdxcore.version import version
|
|
29
30
|
import numpy as np
|
|
30
31
|
|
|
31
32
|
class Test(unittest.TestCase):
|
|
@@ -277,6 +278,15 @@ class A(object):
|
|
|
277
278
|
class B(object):
|
|
278
279
|
def __init__(self, x):
|
|
279
280
|
self.x = x
|
|
281
|
+
|
|
282
|
+
raw_sub = VersionedCacheRoot("?/subdir_cache_test2", exclude_arg_types=[A] )
|
|
283
|
+
|
|
284
|
+
class C(object):
|
|
285
|
+
def __init__(self, x):
|
|
286
|
+
self.x = x
|
|
287
|
+
@raw_sub.cache("0.1")
|
|
288
|
+
def f(self, y):
|
|
289
|
+
return self.x*y
|
|
280
290
|
|
|
281
291
|
class Test(unittest.TestCase):
|
|
282
292
|
|
|
@@ -311,7 +321,7 @@ class Test(unittest.TestCase):
|
|
|
311
321
|
_ = f(A(2))
|
|
312
322
|
self.assertTrue( f.cache_info.last_cached )
|
|
313
323
|
|
|
314
|
-
@sub.cache("1.0", label=lambda x
|
|
324
|
+
@sub.cache("1.0", label=lambda x: f"f({x})")
|
|
315
325
|
def f(x):
|
|
316
326
|
return x
|
|
317
327
|
|
|
@@ -320,7 +330,7 @@ class Test(unittest.TestCase):
|
|
|
320
330
|
uid, _ = f(1, return_cache_uid=True)
|
|
321
331
|
self.assertEqual( uid[:5], "f(1) " )
|
|
322
332
|
|
|
323
|
-
@sub.cache("1.0", uid=lambda x
|
|
333
|
+
@sub.cache("1.0", uid=lambda x: f"f({x})")
|
|
324
334
|
def f(x):
|
|
325
335
|
return x
|
|
326
336
|
|
|
@@ -329,6 +339,77 @@ class Test(unittest.TestCase):
|
|
|
329
339
|
uid, _ = f(1, return_cache_uid=True)
|
|
330
340
|
self.assertEqual( uid, "f(1)" )
|
|
331
341
|
|
|
342
|
+
# test member caching
|
|
343
|
+
|
|
344
|
+
c = C(2.)
|
|
345
|
+
_ = c.f(3)
|
|
346
|
+
self.assertFalse( c.f.cache_info.last_cached )
|
|
347
|
+
_ = c.f(3)
|
|
348
|
+
self.assertTrue( c.f.cache_info.last_cached )
|
|
349
|
+
_ = c.f(2)
|
|
350
|
+
self.assertFalse( c.f.cache_info.last_cached )
|
|
351
|
+
|
|
352
|
+
# test versioning
|
|
353
|
+
|
|
354
|
+
@version("F")
|
|
355
|
+
def F(x):
|
|
356
|
+
return x
|
|
357
|
+
|
|
358
|
+
@sub.cache("G")
|
|
359
|
+
def G(x):
|
|
360
|
+
return x
|
|
361
|
+
|
|
362
|
+
@sub.cache("H", dependencies=[F,G])
|
|
363
|
+
def H(x):
|
|
364
|
+
return G(x)*F(x)
|
|
365
|
+
|
|
366
|
+
_ = H(2.)
|
|
367
|
+
self.assertEqual( H.cache_info.version, "H { Test.test_cache.<locals>.F: F, Test.test_cache.<loc 3fabc694" )
|
|
368
|
+
self.assertEqual( H.cache_info.version, H.version.unique_id64 )
|
|
369
|
+
|
|
370
|
+
# decorate live member functions
|
|
371
|
+
|
|
372
|
+
class AA(object):
|
|
373
|
+
def __init__(self,x):
|
|
374
|
+
self.x = x
|
|
375
|
+
def f(self,y):
|
|
376
|
+
return self.x*y
|
|
377
|
+
|
|
378
|
+
a = AA(x=1)
|
|
379
|
+
f = sub.cache("0.1", label=lambda y : f"a.f({y})")(a.f) # <- decorate bound 'f'.
|
|
380
|
+
_ = f(y=2)
|
|
381
|
+
self.assertFalse( f.cache_info.last_cached )
|
|
382
|
+
self.assertEqual( f.cache_info.version, "0.1" )
|
|
383
|
+
_ = f(y=2)
|
|
384
|
+
self.assertTrue( f.cache_info.last_cached )
|
|
385
|
+
|
|
386
|
+
# funcname
|
|
387
|
+
|
|
388
|
+
sub = VersionedCacheRoot("?/subdir_cache_test", exclude_arg_types=[A], keep_last_arguments=True)
|
|
389
|
+
|
|
390
|
+
@sub.cache("0.1", label=lambda new_func_name, func_name, x, y : f"{new_func_name}(): {func_name} {x} {y}", name_of_func_name_arg="new_func_name")
|
|
391
|
+
def f( func_name, x, y ):
|
|
392
|
+
pass
|
|
393
|
+
f("test",1,y=2)
|
|
394
|
+
self.assertEqual( repr(f.cache_info.arguments), "OrderedDict({'func_name': 'test', 'x': '1', 'y': '2'})" )
|
|
395
|
+
|
|
396
|
+
# this should just work
|
|
397
|
+
@sub.cache("0.1", label=lambda func_name, x : f"{func_name} {x}")
|
|
398
|
+
def f( func_name, x ):
|
|
399
|
+
pass
|
|
400
|
+
with self.assertRaises(RuntimeError):
|
|
401
|
+
f("test",1)
|
|
402
|
+
|
|
403
|
+
# cuttinf off
|
|
404
|
+
@sub.cache("0.1", uid=lambda x,y: f"h2({x},{y})_______________________________________________________________________", exclude_args='debug')
|
|
405
|
+
def h2(x,y,debug=False):
|
|
406
|
+
if debug:
|
|
407
|
+
print(f"h(x={x},y={y})")
|
|
408
|
+
return x*y
|
|
409
|
+
h2(1,1)
|
|
410
|
+
# %%
|
|
411
|
+
self.assertEqual( h2.cache_info.filename, "h2(1,1)________________________________ 46a70d67" )
|
|
412
|
+
|
|
332
413
|
|
|
333
414
|
if __name__ == '__main__':
|
|
334
415
|
unittest.main()
|
|
@@ -457,16 +457,25 @@ class Test(unittest.TestCase):
|
|
|
457
457
|
def __unique_hash__( self, unique_hash, debug_trace ):
|
|
458
458
|
return ( self._seed, self._size )
|
|
459
459
|
|
|
460
|
+
class E(A):
|
|
461
|
+
""" Fixed string """
|
|
462
|
+
def __init__(self):
|
|
463
|
+
self.__unique_hash__ = "some_string"
|
|
464
|
+
|
|
460
465
|
empty = unique_hash( ('Test.test_uniqueHash.<locals>.A') )
|
|
461
466
|
hash1 = unique_hash( A() )
|
|
462
467
|
hash2 = unique_hash( B() )
|
|
463
468
|
hash3 = unique_hash( C() )
|
|
464
469
|
hash4 = unique_hash( D() )
|
|
470
|
+
hash5 = unique_hash( E() )
|
|
471
|
+
h_5 = unique_hash( E().__unique_hash__ )
|
|
465
472
|
self.assertEqual( empty, "653a05ac14649dbceebbd7f3d4a0b89f" )
|
|
466
473
|
self.assertEqual( hash1, empty )
|
|
467
474
|
self.assertEqual( hash2, "ae7cc6d56596eaa20dcd6aedc6e89d85" )
|
|
468
475
|
self.assertEqual( hash3, "5e387f9e86426319577d2121a4e1437b" )
|
|
469
476
|
self.assertEqual( hash4, "c9b449e95339458df155752acadbebb1" )
|
|
477
|
+
self.assertEqual( hash5, "79ee12b070c036e874263c6f4d70df98" )
|
|
478
|
+
self.assertEqual( hash5, h_5 )
|
|
470
479
|
|
|
471
480
|
|
|
472
481
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|