cdxcore 0.1.26__py3-none-any.whl → 0.1.28__py3-none-any.whl

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/__init__.py CHANGED
@@ -4,4 +4,4 @@ Created on June 2022
4
4
  @author: hansb
5
5
  """
6
6
 
7
- __version__ = "0.1.26" # auto-updated by setup.py
7
+ __version__ = "0.1.28" # auto-updated by setup.py
cdxcore/deferred.py CHANGED
@@ -180,7 +180,6 @@ from .err import verify
180
180
  from. util import qualified_name, fmt_list
181
181
  from .verbose import Context
182
182
  from collections.abc import Collection, Sequence, Mapping
183
- import gc as gc
184
183
 
185
184
  class _ParentDeleted:
186
185
  pass
@@ -273,48 +272,7 @@ class Deferred(object):
273
272
  ----------
274
273
  info : str
275
274
  Descriptive name of the usually not-yet-created object deferred actions
276
- will act upon.
277
-
278
- deferred__eq__ : bool, optional
279
- If ``False``, the default, the ``__eq__`` operator will not be deferred. That means
280
- that you are able to use :func:`cdxcore.deferred.Deferred.Create` and :class:`cdxcore.deferred.Deferred`
281
- objects in Python contexts which require ``__eq__``, for example using them in lists.
282
-
283
- By default this works:
284
-
285
- .. code-block:: python
286
-
287
- a = Create("A")
288
- b = Create("B")
289
- a in [b] # -> returns False
290
- b in [b] # -> returns True
291
-
292
- *However* that may also break your deferred semantics as the boolean returned by a deferred operator
293
- may differ from the boolean returned by the non-deferred ``__eq__`` operator (e.g. comparison using ``id``).
294
-
295
- Accordingly,
296
-
297
- .. code-block:: python
298
-
299
- a = Create("A",deferred__eq__=True)
300
- b = Create("B",deferred__eq__=True)
301
- a in [b]
302
-
303
- will raise an error.
304
- Note that this will be a :class:`cdxcore.deferred.NotSupportedError` for the attempt
305
- to call ``__bool__`` on the
306
- deferred ``__eq__`` operator:
307
-
308
- .. code-block:: python
309
-
310
- NotSupportedError:(
311
- '__bool__',
312
- "Deferring action `__bool__` for '($A!=$B)' is "\
313
- "not possible: '__bool__' must return a 'bool'.")
314
-
315
- Note that this setting does not affect ``__neq__`` or any other relational opertors.
316
-
317
- Set to ``True`` to defer ``__eq__``.
275
+ will act upon.
318
276
  """
319
277
  @classmethod
320
278
  def Create( cls, info : str ):
@@ -330,10 +288,11 @@ class Deferred(object):
330
288
  def __init__(self, info : str, *,
331
289
  action : str = "",
332
290
  #
333
- parent : type = None,
291
+ parent : type|None = None,
292
+ base_info : bool|None = None,
334
293
  # arguments passed to the action
335
- args : Collection = None,
336
- kwargs : Mapping = None,
294
+ args : Collection|None = None,
295
+ kwargs : Mapping|None = None,
337
296
  ):
338
297
  """
339
298
  Initialize a deferred action.
@@ -353,8 +312,7 @@ class Deferred(object):
353
312
  self._deferred_action = action # action code
354
313
  self._deferred_parent = parent
355
314
  self._deferred_depth = parent._deferred_depth+1 if not parent is None else 0
356
- self._deferred__eq__ = parent._deferred__eq__ if not parent is None else False
357
- self._deferred__hash__ = False
315
+ self._deferred_base_info = parent._deferred_base_info if not parent is None else ( base_info if not base_info is None else False )
358
316
  self._deferred_args = [] if args is None else list(args)
359
317
  self._deferred_kwargs = {} if kwargs is None else dict(kwargs)
360
318
  self._deferred_live = None # once the object exists, it goes here.
@@ -621,7 +579,7 @@ class Deferred(object):
621
579
  for _ in sources:
622
580
  s += _+","
623
581
  s = s[:-1]
624
- verbose.write( f"{self._deferred_info} <= {s}" )
582
+ verbose.write( lambda : f"{self._deferred_info} <= {s}" )
625
583
  else:
626
584
  verbose.write( self._deferred_info )
627
585
 
@@ -688,7 +646,6 @@ class Deferred(object):
688
646
  raise e
689
647
 
690
648
  try:
691
-
692
649
  live = action( *args, **kwargs )
693
650
  except Exception as e:
694
651
  arguments = self._deferred_fmt_args(args,kwargs)
@@ -701,7 +658,7 @@ class Deferred(object):
701
658
  raise e
702
659
  del action
703
660
 
704
- verbose.write(f"{self._deferred_info} -> '{qualified_name(live)}' : {self._deferred_to_str(live)}")
661
+ verbose.write(lambda : f"{self._deferred_info} -> '{qualified_name(live)}' : {self._deferred_to_str(live)}")
705
662
 
706
663
  # clear object
707
664
  # note that as soon as we write to self._deferred_live we can no longer
@@ -715,7 +672,6 @@ class Deferred(object):
715
672
  self._deferred_kwargs = None
716
673
  self._deferred_args = None
717
674
  self._deferred_parent = _ParentDeleted() # None is a valid parent --> choose someting bad
718
- gc.collect()
719
675
 
720
676
  # action
721
677
  self._deferred_was_resolved = True
@@ -755,7 +711,7 @@ class Deferred(object):
755
711
  verify( not element is None, lambda : f"You cannot resolve '{self._deferred_info}' with an empty 'element'")
756
712
  verbose = Context.quiet if verbose is None else verbose
757
713
 
758
- verbose.write(f"{self._deferred_info} -> '{qualified_name(element)}' : {self._deferred_to_str(element)}")
714
+ verbose.write(lambda : f"{self._deferred_info} -> '{qualified_name(element)}' : {self._deferred_to_str(element)}")
759
715
  self._deferred_live = element
760
716
  self._deferred_was_resolved = True
761
717
 
@@ -764,8 +720,7 @@ class Deferred(object):
764
720
  dlevel = daction._deferred_depth
765
721
  daction._deferred_resolve( verbose=verbose(dlevel) )
766
722
  del daction
767
-
768
- gc.collect()
723
+
769
724
  return element
770
725
 
771
726
  # Iteration
@@ -810,7 +765,7 @@ class Deferred(object):
810
765
  verify( not fmt is None, lambda : f"Error defining action '{action}' for '{self._deferred_info}': 'fmt' not specified" )
811
766
 
812
767
  def label(x):
813
- return x if not isinstance(x, Deferred) else x.__dict__.get('_deferred_info',action)
768
+ return self._deferred_to_str(x) if not isinstance(x, Deferred) else x.__dict__.get('_deferred_info',action)
814
769
  arguments = { f"arg{i}" : label(arg) for i, arg in enumerate(args) }
815
770
  arguments['parent'] = self._deferred_info
816
771
  try:
cdxcore/dynaplot.py CHANGED
@@ -614,7 +614,8 @@ class DynaFig(_DynaDeferred):
614
614
  verify( not 'figsize' in fig_kwargs, "Cannot specify both `figsize` and `fig_size`", exception=ValueError)
615
615
  fig_kwargs['figsize'] = fig_size
616
616
 
617
- _DynaDeferred.__init__(self, f"figure('{str(title)[:20]}')" if not title is None else"figure()" )
617
+ dyna_title = title if len(title) <= 20 else ( title[:17] + "..." ) if not title is None else None
618
+ _DynaDeferred.__init__(self, f"figure('{dyna_title}')" if not title is None else "figure()" )
618
619
 
619
620
  def __str__(self):
620
621
  return self.deferred_info[1:]
@@ -953,6 +954,16 @@ class DynaFig(_DynaDeferred):
953
954
  ax.remove()
954
955
  if render:
955
956
  self.render()
957
+
958
+ # context for cleaning up
959
+ # -----------------------
960
+
961
+ def __enter__(self):
962
+ return self
963
+
964
+ def __exit__(self, *args, **kwargs):
965
+ self.close()
966
+ return False
956
967
 
957
968
  def figure( title : str = None, *,
958
969
  row_size : int = 5,
cdxcore/subdir.py CHANGED
@@ -2801,14 +2801,14 @@ class SubDir(object):
2801
2801
  # caching
2802
2802
  # -------
2803
2803
 
2804
- def cache( self, version : str = None , *,
2805
- dependencies : list = None,
2806
- label : Callable = None,
2807
- uid : Callable = None,
2808
- name : str = None,
2809
- exclude_args : list[str] = None,
2810
- include_args : list[str] = None,
2811
- exclude_arg_types : list[type] = None,
2804
+ def cache( self, version : str|None = None , *,
2805
+ dependencies : list|None = None,
2806
+ label : Callable|None = None,
2807
+ uid : Callable|None = None,
2808
+ name : str|None = None,
2809
+ exclude_args : list[str]|None = None,
2810
+ include_args : list[str]|None = None,
2811
+ exclude_arg_types : list[type]|None = None,
2812
2812
  version_auto_class : bool = True):
2813
2813
  """
2814
2814
  Advanced versioned caching for callables.
@@ -3178,9 +3178,13 @@ class SubDir(object):
3178
3178
  :dec:`cdxcore.subdir.SubDir.cache`
3179
3179
  to provide version information at class level. Only version information are provided here.
3180
3180
 
3181
+ You can use :dec:`cdxcore.subdir.SubDir.cache_class` as an alias.
3182
+
3181
3183
  2) Secondly, decorate ``__init__``. You do not need to specify a version
3182
3184
  for ``__init__`` as its version usually coincides with the version of the class. At ``__init__``
3183
3185
  you define how unique IDs are generated from the parameters passed to object construction.
3186
+
3187
+ You can use :dec:`cdxcore.subdir.SubDir.cache_init` as an alias.
3184
3188
 
3185
3189
  Simple example:
3186
3190
 
@@ -3190,10 +3194,10 @@ class SubDir(object):
3190
3194
  cache = SubDir("!/.cache")
3191
3195
  cache.delete_all_content() # for illustration
3192
3196
 
3193
- @cache.cache("0.1")
3197
+ @cache.cache_class("0.1")
3194
3198
  class A(object):
3195
3199
 
3196
- @cache.cache(exclude_args=['debug'])
3200
+ @cache.cache_init(exclude_args=['debug'])
3197
3201
  def __init__(self, x, debug):
3198
3202
  if debug:
3199
3203
  print("__init__",x)
@@ -3216,10 +3220,10 @@ class SubDir(object):
3216
3220
 
3217
3221
  .. code-block:: python
3218
3222
 
3219
- @cache.cache("0.1")
3223
+ @cache.cache_class("0.1")
3220
3224
  class A(object):
3221
3225
 
3222
- @cache.cache("0.1", id=lambda x, debug: f"A.__init__(x={x})") # <-- 'self' is not passed to the lambda function; no need to add **_
3226
+ @cache.cache_init(id=lambda x, debug: f"A.__init__(x={x})") # <-- 'self' is not passed to the lambda function; no need to add **_
3223
3227
  def __init__(self, x, debug):
3224
3228
  if debug:
3225
3229
  print("__init__",x)
@@ -3227,35 +3231,79 @@ class SubDir(object):
3227
3231
 
3228
3232
  Decorating classes with ``__slots__`` does not yet work.
3229
3233
 
3230
- See also
3231
- ^^^^^^^^
3234
+ Managing Caching Accross a Project
3235
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3232
3236
 
3233
- For project-wide use it is usually inconvenient to control caching at the level of a
3237
+ For project-wide use it is usually convenient to control caching at the level of a
3234
3238
  project-wide cache root directory.
3235
- See :class:`cdxcore.subdir.VersionedCacheRoot` for a thin convenience wrapper around a :class:`cdxcore.subdir.SubDir`
3239
+ The classs :class:`cdxcore.subdir.VersionedCacheRoot` is a thin convenience wrapper around a :class:`cdxcore.subdir.SubDir`
3236
3240
  with a :class:`cdxcore.subdir.CacheController`.
3237
3241
 
3242
+ The idea is to have a central file, ``cache.py`` which contains the central root for caching.
3243
+ We recommend using an environment variable to be able to control the location of this directory
3244
+ out side the code. Here is an example with an environment variable ``PROJECT_CACHE_DIR``::
3245
+
3246
+ # file cache.py
3247
+
3248
+ from cdxcore.subdir import VersionedCacheRoot
3249
+ import os as os
3250
+
3251
+ cache_root = VersionedCacheRoot(
3252
+ os.getenv("PROJECT_CACHE_DIR", "!/.cache")
3253
+ )
3254
+
3255
+ In a particular project file, say ``pipeline.py`` create a file-local cache directory
3256
+ and use it::
3257
+
3258
+ # file pipeline.py
3259
+
3260
+ from cache import cache_root
3261
+
3262
+ cache_dir = cache_root("pipeline")
3263
+
3264
+ @cache_dir.cache("0.1")
3265
+ def f(x):
3266
+ return x+2
3267
+
3268
+ @cache_dir.cache("0.1", dependencies=[f])
3269
+ def g(x)
3270
+ return f(x)**2
3271
+
3272
+ # ...
3273
+
3274
+ In case you have issues with caching you can use the central root directory to turn on tracing
3275
+ accross your project:
3276
+
3277
+ .. code-block:: python
3278
+ :emphasize-lines: 4
3279
+
3280
+ from cdxcore.verbose import Context
3281
+ cache_root = VersionedCacheRoot(
3282
+ os.getenv("PROJECT_CACHE_DIR", "!/.cache"),
3283
+ debug_verbose=Context.all # turn full traing on
3284
+ )
3285
+
3238
3286
  Parameters
3239
3287
  ----------
3240
- version : str, optional
3288
+ version : str | None, default ``None``
3241
3289
  Version of the function.
3242
3290
 
3243
- * If ``None`` then ``F`` must be decorated with :dec:`cdxcore.version.version`.
3244
- * If set, the function ``F`` is first decorated with :dec:`cdxcore.version.version`.
3291
+ * If ``None`` then a common `F`` must be decorated manually with :dec:`cdxcore.version.version`.
3292
+ * If set, the function ``F`` is automatically first decorated with :dec:`cdxcore.version.version` for you.
3245
3293
 
3246
- dependencies : list[type], optional
3294
+ dependencies : list[type] | None, default ``None``
3247
3295
  A list of version dependencies, either by reference or by name.
3248
3296
  See :dec:`cdxcore.version.version` for details on name lookup if strings are used.
3249
3297
 
3250
- label : str | Callable
3251
- Specify a human-readable label for the function call given its parameters.
3298
+ label : str | Callable | None, default ``None``
3299
+ Specify a human-readabl label for the function call given its parameters.
3252
3300
  This label is used to generate the cache file name, and is also printed in when tracing
3253
3301
  hashing operations. Labels are not assumed to be unique, hence a unique hash of
3254
3302
  the label and the parameters to this function will be appended to generate
3255
3303
  the actual cache file name.
3256
3304
 
3257
- Use ``uid`` instead if ``label`` represents valid unique filenames.
3258
-
3305
+ Use ``uid`` instead if ``label`` represents valid unique filenames. You cannot specify both ``uid`` and ``label``.
3306
+ If neither ``uid`` and ``label`` are present, ``name`` will be used.
3259
3307
 
3260
3308
  **Usage:**
3261
3309
 
@@ -3271,30 +3319,34 @@ class SubDir(object):
3271
3319
 
3272
3320
  ``label`` cannot be used alongside ``uid``.
3273
3321
 
3274
- uid : str | Callable
3322
+ uid : str | Callable | None, default ``None``
3275
3323
  Alternative to ``label`` which is assumed to generate a unique cache file name. It has the same
3276
- semantics as ``label``. When used, parameters to the decorated function are not hashed.
3324
+ semantics as ``label``. When used, parameters to the decorated function are not hashed
3325
+ as the ``uid`` is assumed to be already unique. The string must be a valid file name
3277
3326
 
3278
- ``uid`` be used alongside ``label``.
3327
+ Use ``label`` if the id is not unique. You cannot specify both ``uid`` and ``label``.
3328
+ If neither ``uid`` and ``label`` are present, ``name`` will be used (as non-unique ``label``).
3279
3329
 
3280
- name : str, optional
3281
- Name of this function which is used either on its own if neither ``label`` not ``uid`` are used.
3282
- If either of them is used, ``name`` is passed as a parameter to either the callable or the
3283
- formatting operator.
3330
+ name : str | None, default ``None``
3331
+ 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 ``name`` to either the callable or the
3333
+ formatting operator. See above for more details.
3284
3334
 
3285
3335
  If ``name`` is not specified it defaults to ``__qualname__`` expanded
3286
3336
  by the module name the function is defined in.
3287
3337
 
3288
- include_args : list[str]
3338
+ include_args : list[str] | None, default ``None``
3289
3339
  List of arguments to include in generating an unqiue ID, or ``None`` for all.
3290
3340
 
3291
- exclude_args : list[str]:
3292
- List of arguments to exclude from generating an unique ID.
3341
+ exclude_args : list[str] | None, default ``None``
3342
+ List of arguments to exclude from generating an unique ID. Examples of such non-functional arguments
3343
+ are workflow controls (debugging) and i/o elements.
3293
3344
 
3294
- exclude_arg_types : list[type]
3295
- List of parameter types to exclude from generating an unique ID.
3345
+ exclude_arg_types : list[type] | None, default ``None``
3346
+ List of parameter types to exclude from generating an unique ID. Examples of such non-functional arguments
3347
+ are workflow controls (debugging) and i/o elements.
3296
3348
 
3297
- version_auto_class : bool
3349
+ version_auto_class : bool, default ``True``
3298
3350
  Whether to automaticallty add version dependencies on base classes or, for member functions, on containing
3299
3351
  classes. This is the ``auto_class`` parameter for :dec:`cdxcore.version.version`.
3300
3352
 
@@ -3326,12 +3378,39 @@ class SubDir(object):
3326
3378
 
3327
3379
  The decorated ``F()`` has additional function parameters, namely:
3328
3380
 
3329
- * ``override_cache_mode`` : allows to override caching mode temporarily, in particular you can set it to ``"off"``.
3330
- * ``track_cached_files`` : allows passing a :class:`cdxcore.subdir.CacheTracker`
3381
+ * ``override_cache_mode`` : ``CacheMode`` | None, default ``None``
3382
+
3383
+ Allows overriding the ``CacheMode`` temporarily, in particular you can set it to ``"off"``.
3384
+
3385
+ * ``track_cached_files`` : :class:`cdxcore.subdir.CacheTracker` | None, default ``None``
3386
+
3387
+ Allows passing a :class:`cdxcore.subdir.CacheTracker`
3331
3388
  object to keep track of all
3332
3389
  files used (loaded from or saved to).
3333
3390
  The function :meth:`cdxcore.subdir.CacheTracker.delete_cache_files` can be used
3334
3391
  to delete all files involved in caching.
3392
+
3393
+ * ``return_cache_uid`` : bool, default ``False``
3394
+
3395
+ If ``True``, then the decorated function will return a tuple ``uid, result``
3396
+ where ``uid`` is the unique filename generated for this function call,
3397
+ and where ``result`` is the actual result from the function, cached or not.
3398
+
3399
+ Usage::
3400
+
3401
+ from cdxcore.subdir import SubDir
3402
+ cache_dir = SubDir("!/.cache")
3403
+
3404
+ @cache_dir.cache()
3405
+ def f(x, y):
3406
+ return x*y
3407
+
3408
+ uid, xy = f( x=1, y=2, return_cache_uid=True )
3409
+
3410
+ This pattern is thread-safe when compared to using::
3411
+
3412
+ xy = f( x=1, y=2 )
3413
+ uid = f.cache_info.filename
3335
3414
  """
3336
3415
  return CacheCallable(subdir = self,
3337
3416
  version = version,
@@ -3361,17 +3440,41 @@ class SubDir(object):
3361
3440
  @cache.cache_class("0.1")
3362
3441
  class A(object):
3363
3442
 
3364
- @cache.cache(exclude_args=['debug'])
3443
+ @cache.cache_init(exclude_args=['debug'])
3365
3444
  def __init__(self, x, debug):
3366
3445
  if debug:
3367
3446
  print("__init__",x)
3368
3447
  self.x = x
3369
3448
 
3370
3449
  """
3371
- return self.cache( name=name,
3372
- version=version,
3373
- dependencies=dependencies,
3374
- version_auto_class=version_auto_class)
3450
+ return self.cache( name=name, version=version, dependencies=dependencies, version_auto_class=version_auto_class)
3451
+
3452
+ def cache_init( self,
3453
+ label : Callable = None,
3454
+ uid : Callable = None,
3455
+ exclude_args : list[str] = None,
3456
+ include_args : list[str] = None,
3457
+ exclude_arg_types : list[type] = None,
3458
+ ):
3459
+ """
3460
+ Short-cut for :dec:`cdxcore.subdir.SubDir.cache` applied to decorating ``__init__``
3461
+ with a reduced number of available parameters.
3462
+
3463
+ Example::
3464
+
3465
+ cache = SubDir("!/.cache")
3466
+
3467
+ @cache.cache_class("0.1")
3468
+ class A(object):
3469
+
3470
+ @cache.cache_init(exclude_args=['debug'])
3471
+ def __init__(self, x, debug):
3472
+ if debug:
3473
+ print("__init__",x)
3474
+ self.x = x
3475
+
3476
+ """
3477
+ return self.cache( label=label, uid=uid, exclude_args=exclude_args, include_args=include_args, exclude_arg_types=exclude_arg_types )
3375
3478
 
3376
3479
  # ========================================================================
3377
3480
  # Caching, convenience
@@ -3444,6 +3547,9 @@ def VersionedCacheRoot( directory : str, *,
3444
3547
 
3445
3548
  * ``hash_length``: length used for hashes, see :class:`cdxcore.uniquehash.UniqueHash`.
3446
3549
 
3550
+ * ``debug_verbose`` set to ``Context.all`` after importing ``from cdxcore.verbose import Context``
3551
+ will turn on tracing all caching operations.
3552
+
3447
3553
  Returns
3448
3554
  -------
3449
3555
  Root : :class:`cdxcore.subdir.SubDir`
@@ -3768,8 +3874,9 @@ class CacheCallable(object):
3768
3874
  exclude_types = ( self._exclude_arg_types if not self._exclude_arg_types is None else set() )\
3769
3875
  | ( self.global_exclude_arg_types if not self.global_exclude_arg_types is None else set())
3770
3876
 
3771
- def execute( *args, override_cache_mode : CacheMode = None,
3772
- track_cached_files : CacheTracker = None,
3877
+ def execute( *args, override_cache_mode : CacheMode|None = None,
3878
+ track_cached_files : CacheTracker|None = None,
3879
+ return_cache_uid : bool = False,
3773
3880
  **kwargs ):
3774
3881
  """
3775
3882
  Cached execution of the wrapped function
@@ -3899,7 +4006,7 @@ class CacheCallable(object):
3899
4006
  # -------------------------
3900
4007
 
3901
4008
  execute.cache_info.label = str(label) if not label is None else None
3902
- execute.cache_info.filename = filename
4009
+ execute.cache_info.filename = filename # that is the unique ID for this call
3903
4010
  execute.cache_info.version = version_
3904
4011
 
3905
4012
  if self.cache_controller.keep_last_arguments:
@@ -3937,6 +4044,8 @@ class CacheCallable(object):
3937
4044
  assert r.__magic_cache_call_init__ is None, ("**** Internal error. __init__ should reset __magic_cache_call_init__", F.__qualname__, label)
3938
4045
  r.__magic_cache_call_init__ = False # since we called __new__, __init__ will be called next
3939
4046
 
4047
+ if return_cache_uid:
4048
+ return filename, r
3940
4049
  return r
3941
4050
 
3942
4051
  r = F(*args, **kwargs)
@@ -3966,6 +4075,9 @@ class CacheCallable(object):
3966
4075
  debug_verbose.write(f"cache({name}): called '{label}' version 'version {version_}' and wrote result into '{self._subdir.full_file_name(filename)}'.")
3967
4076
  else:
3968
4077
  debug_verbose.write(f"cache({name}): called '{label}' version 'version {version_}' but did *not* write into '{self._subdir.full_file_name(filename)}'.")
4078
+
4079
+ if return_cache_uid:
4080
+ return filename, r
3969
4081
  return r
3970
4082
 
3971
4083
  update_wrapper( wrapper=execute, wrapped=F )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cdxcore
3
- Version: 0.1.26
3
+ Version: 0.1.28
4
4
  Summary: Basic Python Tools; upgraded cdxbasics
5
5
  Author-email: Hans Buehler <github@buehler.london>
6
6
  License-Expression: MIT
@@ -1,20 +1,20 @@
1
- cdxcore/__init__.py,sha256=fkVQQ_XdGdXhWA35RMrTjwmBD2jNYOcKPhNKMgPF8Vw,127
1
+ cdxcore/__init__.py,sha256=eAlbvVVYLS8fmoJ4F2yMdJWFO8woTjXMND4ABmbz1sw,127
2
2
  cdxcore/config.py,sha256=RaxpAnTEyGXLiq-1O2prXFBb2sdyqB6hHDHPAFGe2k0,98472
3
- cdxcore/deferred.py,sha256=5bREI2IQ3uW3EMDcAO17gJbVnbUIV__-bdEUQpZIEeU,44684
3
+ cdxcore/deferred.py,sha256=41buUit1SOAnhVSrzlzM5hd2OOhnAS2QRDSWEpjXfdE,43138
4
4
  cdxcore/dynalimits.py,sha256=TXEwa4wCdeM5epG1ceezaHwvmhnbU-oXlXpHNbOMQR8,11293
5
- cdxcore/dynaplot.py,sha256=1g4IFTfUir13BEDNxDXxUvlwh__kq5n0kgehZ-e8ah4,51031
5
+ cdxcore/dynaplot.py,sha256=Bnjt9GEkPRXcsSqbVSsr08d71dhTZXrVab-zPxLEkaA,51355
6
6
  cdxcore/err.py,sha256=3JP1IZDMIKayTt-ZNV9PCYdjtvkybQTjZOrRmBZWyfg,14709
7
7
  cdxcore/filelock.py,sha256=D2U8rzxYkeuDc8RLRbePBQ939PPxcJuRC8sahmVx-x8,34781
8
8
  cdxcore/jcpool.py,sha256=Vw8o8S4_qTVQNIr2sRaBR_kHz_wO0zpYs0QjlKSYFz8,27280
9
9
  cdxcore/npio.py,sha256=SVpKkFt6lyRogkns-oky0wEiWgVTalgB0OdaSvyWtlU,24212
10
10
  cdxcore/npshm.py,sha256=9buYPNJoNlw69NZQ-nLF13PEWBWNx51a0gjQw5Gc24U,18368
11
11
  cdxcore/pretty.py,sha256=FsI62rlaqRX1E-uPCSnu0M4UoCQ5Z55TDvYPnzTNO70,17220
12
- cdxcore/subdir.py,sha256=QnFgWq464KtoFXedAuo1r5PHvsN3whDs_WA_vXf5BdQ,179560
12
+ cdxcore/subdir.py,sha256=S-ir_1pUQxQI8B3JkflLzvA3P0_XkBU-Qqkm3S9Fi_M,184881
13
13
  cdxcore/uniquehash.py,sha256=WCtieiRBavfpSVT-hW4QNYGHFrfgXG3kJuTUtAi9_b0,50729
14
14
  cdxcore/util.py,sha256=dqCEhrDvUM4qjeUwb6n8jPfS8WC4Navcj83gDjXfRFE,39349
15
15
  cdxcore/verbose.py,sha256=vsjGTVnAHMPg2L2RfsowWKKPjUSnQJ3F653vDTydBkI,30223
16
16
  cdxcore/version.py,sha256=S3H0ktEZsM0zAj3EQ5AdrZ2KxWhQtemd8clzEFE9hOQ,27160
17
- cdxcore-0.1.26.dist-info/licenses/LICENSE,sha256=M-cisgK9kb1bqVRJ7vrCxHcMQQfDxdY3c2YFJJWfNQg,1090
17
+ cdxcore-0.1.28.dist-info/licenses/LICENSE,sha256=M-cisgK9kb1bqVRJ7vrCxHcMQQfDxdY3c2YFJJWfNQg,1090
18
18
  docs/source/conf.py,sha256=yn3LYgw3sT45mUyll-B2emVp6jg7H6KfAHOcBg_MNv4,4182
19
19
  tests/test_config.py,sha256=N86mH3y7k3LXEmU8uPLfrmRMZ-80VhlD35nBbpLmebg,15617
20
20
  tests/test_deferred.py,sha256=4Xsb76r-XqHKiBuHa4jbErjMWbrgHXfPwewzzY4lf9Y,7922
@@ -23,7 +23,7 @@ tests/test_jcpool.py,sha256=bcGC3UcJ7SOHVgzZ-cooEJgncLWmhutBfqH7P5qB-iw,7901
23
23
  tests/test_npio.py,sha256=v-_oO7bj9obSJD4TBk4gBM8ACL62DMNjyQ-iZmvOOdw,3039
24
24
  tests/test_npshm.py,sha256=uVivQ3zI4_v3a4qDIY-1gjf5_2pEtKM00VlC3hubUl8,3270
25
25
  tests/test_pretty.py,sha256=pVwTBjm3XqwEf2jq5GdZvT4cDSTGqiQFBMLqmGJYuB0,11644
26
- tests/test_subdir.py,sha256=Ab6pCrUnbMR3kpLsKcDwLUP5QY1HvFVTmSI9D1lXhbM,11444
26
+ tests/test_subdir.py,sha256=LGofLSthZMmZBOAq_XwYOOS2nmhdGgrNJuHN3Sl8ZFM,13026
27
27
  tests/test_uniquehash.py,sha256=n6ZCkdBw-iRsyzeAEmrnLK0hJLGH6l_Dtt_KIkSa6KA,24630
28
28
  tests/test_util.py,sha256=mwtz3o_RiLNn808928xP4jawHmGNxzUXiatMh0zBc3o,24342
29
29
  tests/test_verbose.py,sha256=zXheIqAVOnwML2zsCjLugjYzB_KNzU_S4Xu2CSb4o10,4723
@@ -34,7 +34,7 @@ tmp/npsh1.py,sha256=mNucUl2-jNmE84GlMlliB4aJ0UQ9FqdymgcY_9mLeZY,15432
34
34
  tmp/sharedarray.py,sha256=dNOT1ObCc3nM3qA3OA508NcENIBnkmWMxRPCqvMVa8A,12862
35
35
  up/git_message.py,sha256=EfSH7Pit3ZoCiRqSMwRCUN_QyuwreU4LTIyGSutBlm4,123
36
36
  up/pip_modify_setup.py,sha256=Esaml4yA9tFsqxLhk5bWSwvKCURONjQqfyChgFV2TSY,1584
37
- cdxcore-0.1.26.dist-info/METADATA,sha256=YT7I_dWXka3a7RSKznr_zZO-9WC_LFmMy0DkyGSDJdE,5939
38
- cdxcore-0.1.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
39
- cdxcore-0.1.26.dist-info/top_level.txt,sha256=phNSwCyJFe7UP2YMoi8o6ykhotatlIbJHjTp9EHM51k,26
40
- cdxcore-0.1.26.dist-info/RECORD,,
37
+ cdxcore-0.1.28.dist-info/METADATA,sha256=pQce48t_g2AM8yerQ7Ltd7ddv4DbPszOXa25upO2AOs,5939
38
+ cdxcore-0.1.28.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
39
+ cdxcore-0.1.28.dist-info/top_level.txt,sha256=phNSwCyJFe7UP2YMoi8o6ykhotatlIbJHjTp9EHM51k,26
40
+ cdxcore-0.1.28.dist-info/RECORD,,
tests/test_subdir.py CHANGED
@@ -25,14 +25,14 @@ import_local()
25
25
  """
26
26
  Imports
27
27
  """
28
- from cdxcore.subdir import SubDir, CacheMode, VersionError, VersionPresentError
28
+ from cdxcore.subdir import SubDir, CacheMode, VersionError, VersionPresentError, VersionedCacheRoot
29
29
  import numpy as np
30
30
 
31
31
  class Test(unittest.TestCase):
32
32
 
33
33
  def test_subdir(self):
34
34
 
35
- sub = SubDir("!/.tmp_test_for_cdxbasics.subdir", delete_everything=True )
35
+ sub = SubDir("?/.tmp_test_for_cdxbasics.subdir", delete_everything=True )
36
36
  sub['y'] = 2
37
37
  sub.write('z',3)
38
38
  sub.write_string('l',"hallo")
@@ -193,11 +193,8 @@ class Test(unittest.TestCase):
193
193
  with self.assertRaises(VersionError):
194
194
  r = sub.read("test", None, version="2", raise_on_error=True)
195
195
  # wrong version
196
- sub.delete_everything()
197
196
 
198
197
  def test_new(self):
199
-
200
-
201
198
  subdir = SubDir("my_directory") # relative to current working directory
202
199
  subdir = SubDir("./my_directory") # relative to current working directory
203
200
  subdir = SubDir("~/my_directory") # relative to home directory
@@ -248,8 +245,6 @@ class Test(unittest.TestCase):
248
245
  test_format( SubDir.GZIP, True )
249
246
  test_format( SubDir.JSON_PICKLE )
250
247
 
251
-
252
-
253
248
  def test_cache_mode(self):
254
249
 
255
250
  on = CacheMode("on")
@@ -275,6 +270,66 @@ class Test(unittest.TestCase):
275
270
  self.assertEqual( [ x.write for x in allc ], [True, True, False, False, True, False] )
276
271
  self.assertEqual( [ x.delete for x in allc ], [False, False, False, True, True, False ] )
277
272
  self.assertEqual( [ x.del_incomp for x in allc ], [True, False, False, True, True, False ] )
273
+
274
+ class A(object):
275
+ def __init__(self, x):
276
+ self.x = x
277
+ class B(object):
278
+ def __init__(self, x):
279
+ self.x = x
280
+
281
+ class Test(unittest.TestCase):
282
+
283
+ def test_cache( self ):
284
+
285
+ sub = VersionedCacheRoot("?/subdir_cache_test", exclude_arg_types=[A] )
286
+
287
+ @sub.cache("1.0")
288
+ def f(x):
289
+ return x
290
+
291
+ _ = f(1)
292
+ _ = f(1)
293
+ self.assertTrue( f.cache_info.last_cached )
294
+
295
+ @sub.cache("1.0")
296
+ def g(x):
297
+ return x
298
+
299
+ # a new B object each time -> no caching
300
+ _ = f(B(1))
301
+ _ = f(B(2))
302
+ self.assertFalse( f.cache_info.last_cached )
303
+
304
+ # same B object each time -> no caching
305
+ _ = f(B(1))
306
+ _ = f(B(1))
307
+ self.assertTrue( f.cache_info.last_cached )
308
+
309
+ # a new A object each time -> caching, as we ignore 'A' types
310
+ _ = f(A(1))
311
+ _ = f(A(2))
312
+ self.assertTrue( f.cache_info.last_cached )
313
+
314
+ @sub.cache("1.0", label=lambda x, **_: f"f({x})")
315
+ def f(x):
316
+ return x
317
+
318
+ _ = f(1)
319
+ self.assertEqual( f.cache_info.filename[:5], "f(1) " )
320
+ uid, _ = f(1, return_cache_uid=True)
321
+ self.assertEqual( uid[:5], "f(1) " )
322
+
323
+ @sub.cache("1.0", uid=lambda x, **_: f"f({x})")
324
+ def f(x):
325
+ return x
326
+
327
+ _ = f(1)
328
+ self.assertEqual( f.cache_info.filename, "f(1)" )
329
+ uid, _ = f(1, return_cache_uid=True)
330
+ self.assertEqual( uid, "f(1)" )
331
+
332
+
278
333
  if __name__ == '__main__':
279
334
  unittest.main()
280
335