partis-pyproj 0.1.4__py3-none-any.whl → 0.1.5__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.
Files changed (42) hide show
  1. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/__init__.py +9 -1
  2. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/_legacy_setup.py +11 -11
  3. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/_nonprintable.py +4 -3
  4. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/backend.py +44 -37
  5. partis_pyproj-0.1.5.data/purelib/partis/pyproj/builder/builder.py +351 -0
  6. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/cargo.py +2 -2
  7. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/cmake.py +9 -15
  8. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/meson.py +5 -13
  9. partis_pyproj-0.1.5.data/purelib/partis/pyproj/builder/process.py +42 -0
  10. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/__init__.py +1 -1
  11. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_base.py +75 -86
  12. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_binary.py +6 -24
  13. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_copy.py +7 -18
  14. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_source.py +4 -21
  15. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_targz.py +5 -12
  16. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/dist_file/dist_zip.py +5 -14
  17. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/file.py +2 -1
  18. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/legacy.py +3 -2
  19. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/load_module.py +7 -6
  20. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/norms.py +35 -31
  21. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/__init__.py +2 -1
  22. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/match.py +42 -35
  23. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/path/pattern.py +60 -54
  24. partis_pyproj-0.1.5.data/purelib/partis/pyproj/path/utils.py +94 -0
  25. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pep.py +36 -35
  26. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pkginfo.py +7 -16
  27. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pptoml.py +125 -120
  28. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/pyproj.py +39 -36
  29. partis_pyproj-0.1.5.data/purelib/partis/pyproj/template.py +229 -0
  30. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/validate.py +273 -268
  31. partis_pyproj-0.1.5.dist-info/METADATA +500 -0
  32. partis_pyproj-0.1.5.dist-info/RECORD +37 -0
  33. partis_pyproj-0.1.4.data/purelib/partis/pyproj/builder/builder.py +0 -267
  34. partis_pyproj-0.1.4.data/purelib/partis/pyproj/builder/process.py +0 -75
  35. partis_pyproj-0.1.4.data/purelib/partis/pyproj/path/utils.py +0 -40
  36. partis_pyproj-0.1.4.dist-info/METADATA +0 -51
  37. partis_pyproj-0.1.4.dist-info/RECORD +0 -36
  38. {partis_pyproj-0.1.4.data → partis_pyproj-0.1.5.data}/purelib/partis/pyproj/builder/__init__.py +0 -0
  39. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/LICENSE.txt +0 -0
  40. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/WHEEL +0 -0
  41. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/entry_points.txt +0 -0
  42. {partis_pyproj-0.1.4.dist-info → partis_pyproj-0.1.5.dist-info}/top_level.txt +0 -0
@@ -1,25 +1,23 @@
1
+ from __future__ import annotations
1
2
  import sys
2
- import os.path as osp
3
- import io
4
3
  import warnings
5
- import stat
6
- import re
7
- import pathlib
4
+ from functools import partial
8
5
  import inspect
9
6
  import types
10
7
  from copy import copy
8
+ from abc import ABCMeta
11
9
  from collections.abc import (
12
10
  Mapping,
13
11
  Sequence,
14
12
  Iterable )
15
13
 
16
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
14
+ #===============================================================================
17
15
  # NOTE: Filtering works by changing the traceback linked-list, which means
18
16
  # writing to the 'tb_next' attrbute to the next frame not to be skipped.
19
17
  # However, 'tb_next' was a read-only attribute until Python 3.7
20
18
  FILTER_VALIDATING_FRAMES = sys.version_info >= (3,7)
21
19
 
22
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
20
+ #===============================================================================
23
21
  def filter_traceback(traceback, ignore):
24
22
  # NOTE: always keep the first frame in the trace-back (even if it would be filtered)
25
23
  cur_tb = traceback
@@ -44,18 +42,18 @@ def filter_traceback(traceback, ignore):
44
42
  else:
45
43
  prev_kept_tb = cur_tb
46
44
 
47
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
45
+ #===============================================================================
48
46
  def ignore_validating(frame, lineno):
49
47
  if frame.f_code.co_filename == __file__:
50
48
  return True
51
49
 
52
50
  return False
53
51
 
54
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
52
+ #===============================================================================
55
53
  class ValidationWarning( RuntimeWarning ):
56
54
  pass
57
55
 
58
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
56
+ #===============================================================================
59
57
  class ValidationError( ValueError ):
60
58
  """General validation error
61
59
 
@@ -130,27 +128,27 @@ class ValidationError( ValueError ):
130
128
  filename = self.doc_file,
131
129
  path = self.doc_path ))
132
130
 
133
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
131
+ #===============================================================================
134
132
  class RequiredValueError( ValidationError ):
135
133
  pass
136
134
 
137
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
135
+ #===============================================================================
138
136
  class ValidDefinitionError( ValidationError ):
139
137
  pass
140
138
 
141
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
139
+ #===============================================================================
142
140
  class ValidPathError(ValidationError):
143
141
  """File is not valid
144
142
  """
145
143
  pass
146
144
 
147
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
145
+ #===============================================================================
148
146
  class FileOutsideRootError(ValidPathError):
149
147
  """File path is outside a desired root directory
150
148
  """
151
149
  pass
152
150
 
153
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
151
+ #===============================================================================
154
152
  class validating:
155
153
  """Context manager to append information to a ValidationError as it propagates
156
154
 
@@ -200,7 +198,7 @@ class validating:
200
198
 
201
199
  else:
202
200
  raise ValidationError(
203
- f"Error while validating",
201
+ "Error while validating",
204
202
  doc_root = self.root,
205
203
  doc_file = self.file,
206
204
  doc_path = None if self.key is None else [self.key] ) from value
@@ -208,15 +206,11 @@ class validating:
208
206
  # do not handle any exceptions here
209
207
  return False
210
208
 
211
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
209
+ #===============================================================================
212
210
  class Special:
213
- #-----------------------------------------------------------------------------
214
- def __str__(self):
215
- return type(self).__name__
216
-
217
211
  #-----------------------------------------------------------------------------
218
212
  def __repr__(self):
219
- return str(self)
213
+ return type(self).__name__
220
214
 
221
215
  #-----------------------------------------------------------------------------
222
216
  def __eq__(self, other):
@@ -230,25 +224,25 @@ class Special:
230
224
  def __hash__(self):
231
225
  return hash(str(self))
232
226
 
233
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
227
+ #===============================================================================
234
228
  class Optional(Special):
235
229
  """Optional value
236
230
  """
237
231
  pass
238
232
 
239
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
233
+ #===============================================================================
240
234
  class OptionalNone(Special):
241
235
  """Optional value, but is set to None if not initially set
242
236
  """
243
237
  pass
244
238
 
245
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
239
+ #===============================================================================
246
240
  class Required(Special):
247
241
  """Required value
248
242
  """
249
243
  pass
250
244
 
251
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
245
+ #===============================================================================
252
246
  class NotSet(Special):
253
247
  """Special value indicating a value is not set
254
248
  """
@@ -259,7 +253,7 @@ OPTIONAL_NONE = OptionalNone()
259
253
  REQUIRED = Required()
260
254
  NOTSET = NotSet()
261
255
 
262
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
256
+ #===============================================================================
263
257
  def validate(val, default, validators):
264
258
  """Internal method to apply default value and validators
265
259
  """
@@ -274,7 +268,7 @@ def validate(val, default, validators):
274
268
  return None if default == OPTIONAL_NONE else val
275
269
 
276
270
  elif default == REQUIRED:
277
- raise RequiredValueError(f"Value is required")
271
+ raise RequiredValueError("Value is required")
278
272
 
279
273
  else:
280
274
  val = default
@@ -318,7 +312,7 @@ def validate(val, default, validators):
318
312
 
319
313
  return val
320
314
 
321
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
315
+ #===============================================================================
322
316
  def fmt_validator(v):
323
317
 
324
318
  if isinstance(v, Validator):
@@ -360,10 +354,12 @@ def fmt_validator(v):
360
354
 
361
355
  return f"<{name}>"
362
356
 
363
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
357
+ #===============================================================================
364
358
  class Validator:
365
359
  """Validates a value
366
360
  """
361
+ __slots__ = ('default', 'validators')
362
+
367
363
  #-----------------------------------------------------------------------------
368
364
  def __init__(self, *args, default = NOTSET):
369
365
 
@@ -400,7 +396,7 @@ class Validator:
400
396
  except Exception as e:
401
397
  # the type cannot be instantiated without arguments
402
398
  raise ValidDefinitionError(
403
- f"Default value must be specified, or explicitly set as optional or required") from e
399
+ "Default value must be specified, or explicitly set as optional or required") from e
404
400
 
405
401
  else:
406
402
  # cannot be used as default, put back to use as validator
@@ -415,16 +411,16 @@ class Validator:
415
411
  # convenience method to used default value to derive type
416
412
  args.append(type(default))
417
413
 
418
- self._default = default
419
- self._validators = args
414
+ self.default = default
415
+ self.validators = tuple(args)
420
416
 
421
417
  #-----------------------------------------------------------------------------
422
418
  def __str__(self):
423
419
  args = list()
424
- for v in self._validators:
420
+ for v in self.validators:
425
421
  args.append(fmt_validator(v))
426
422
 
427
- args.append(f"default = {fmt_validator(self._default)}")
423
+ args.append(f"default = {fmt_validator(self.default)}")
428
424
  args = ', '.join(args)
429
425
  return f"{type(self).__name__}({args})"
430
426
 
@@ -434,16 +430,18 @@ class Validator:
434
430
 
435
431
  #-----------------------------------------------------------------------------
436
432
  def __call__(self, val = NOTSET):
437
- return validate(val, self._default, self._validators)
433
+ return validate(val, self.default, self.validators)
438
434
 
439
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
435
+ #===============================================================================
440
436
  class Restricted(Validator):
441
437
  """Restricts a value to one of listed options
442
438
  """
439
+ __slots__ = ('options',)
440
+
443
441
  #-----------------------------------------------------------------------------
444
442
  def __init__(self, *options ):
445
443
  if len(options) == 0:
446
- raise ValidDefinitionError(f"Must have at least one option")
444
+ raise ValidDefinitionError("Must have at least one option")
447
445
 
448
446
  super().__init__(options[0], type(options[0]))
449
447
 
@@ -454,19 +452,19 @@ class Restricted(Validator):
454
452
  with validating(key = i):
455
453
  _options.append(super().__call__(v))
456
454
 
457
- self._options = set(_options)
455
+ self.options = set(_options)
458
456
 
459
457
  #-----------------------------------------------------------------------------
460
458
  def __call__(self, val):
461
459
  val = super().__call__(val)
462
460
 
463
- if val not in self._options:
461
+ if val not in self.options:
464
462
  raise ValidationError(
465
- f"Must be one of {self._options}: {val}")
463
+ f"Must be one of {self.options}: {val}")
466
464
 
467
465
  return val
468
466
 
469
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
467
+ #===============================================================================
470
468
  def valid(*validators, default = NOTSET):
471
469
  """Casts list of objects to Validator, if needed
472
470
  """
@@ -478,19 +476,19 @@ def valid(*validators, default = NOTSET):
478
476
 
479
477
  return Validator(*validators, default = default)
480
478
 
481
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
479
+ #===============================================================================
482
480
  def union(*validators):
483
481
  """Value must pass at least one of listed validators
484
482
  """
485
483
  return Validator([valid(v) for v in validators], default = REQUIRED)
486
484
 
487
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
485
+ #===============================================================================
488
486
  def restrict(*options):
489
487
  """Restricts a value to one of listed options
490
488
  """
491
489
  return Restricted(*options)
492
490
 
493
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
491
+ #===============================================================================
494
492
  def valid_type(
495
493
  obj,
496
494
  types ):
@@ -502,7 +500,7 @@ def valid_type(
502
500
  raise ValidationError(
503
501
  f"Must be of type {types}: {type(obj)}" )
504
502
 
505
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
503
+ #===============================================================================
506
504
  def valid_keys(
507
505
  obj,
508
506
  key_valid = None,
@@ -560,7 +558,7 @@ def valid_keys(
560
558
  def copy_once(out):
561
559
  if out is obj:
562
560
  # copy only if 'out' is still the same object as obj
563
- return dict(obj)
561
+ return copy(obj)
564
562
 
565
563
  return out
566
564
 
@@ -705,110 +703,209 @@ def valid_keys(
705
703
 
706
704
  return out
707
705
 
708
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
709
- class validating_block:
710
- def __init__(self, obj):
711
- self._obj = obj
706
+ #===============================================================================
707
+ class _ValidDictAttr:
708
+ r"""Descriptor for accessing dictionary keys as attributes
709
+ """
710
+ __slots__ = ('name', 'key')
712
711
 
713
- def __enter__(self):
714
- self._obj._validating = True
712
+ #-----------------------------------------------------------------------------
713
+ def __init__(self, name: str, key: str):
714
+ self.name = name
715
+ self.key = key
715
716
 
716
- def __exit__(self, type, value, traceback):
717
- self._obj._validating = False
717
+ #-----------------------------------------------------------------------------
718
+ def __get__(self, obj, owner):
719
+ if obj is None:
720
+ return self
718
721
 
719
- # do not handle any exceptions here
720
- return False
722
+ return obj._data[self.key]
721
723
 
722
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
723
- def attrs_modifiable( obj ):
724
- return (
725
- not hasattr( obj, '_p_attrs_modify' )
726
- or obj._p_attrs_modify )
724
+ #-----------------------------------------------------------------------------
725
+ def __set__(self, obj, value):
726
+ obj._data[self.key] = value
727
+ obj._validate()
727
728
 
728
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
729
- class attrs_modify:
730
729
  #-----------------------------------------------------------------------------
731
- def __init__( self, obj ):
732
- self._obj = obj
730
+ def __delete__(self, obj):
731
+ del obj._data[self.key]
732
+ obj._validate()
733
733
 
734
734
  #-----------------------------------------------------------------------------
735
- def __enter__(self):
736
- self._obj._p_attrs_modify = True
735
+ def __repr__(self):
736
+ return f"{type(self).__name__}({self.name!r}, {self.key!r})"
737
737
 
738
+ #===============================================================================
739
+ class _ValidDictMeta(ABCMeta):
738
740
  #-----------------------------------------------------------------------------
739
- def __exit__(self, type, value, traceback):
740
- self._obj._p_attrs_modify = False
741
+ def __new__(mcls,
742
+ name,
743
+ bases,
744
+ namespace):
745
+
746
+ schema_keys = (
747
+ 'key_valid',
748
+ 'value_valid',
749
+ 'item_valid',
750
+ 'allow_keys',
751
+ 'require_keys',
752
+ 'min_keys',
753
+ 'wedge_keys',
754
+ 'mutex_keys',
755
+ 'deprecate_keys',
756
+ 'forbid_keys',
757
+ 'default',
758
+ 'proxy_keys')
759
+
760
+ schema = {}
761
+
762
+ for base in bases:
763
+ # include options from base classes
764
+ if isinstance(base, _ValidDictMeta):
765
+ for k in schema_keys:
766
+ v = base.__dict__.get(k)
767
+
768
+ if v is not None:
769
+ schema[k] = v
770
+
771
+ for k in schema_keys:
772
+ v = namespace.get(k)
773
+
774
+ if v is not None:
775
+ schema[k] = v
776
+
777
+ namespace.update(schema)
778
+
779
+ validator = schema.get('validator')
780
+ key_valid = schema.get('key_valid')
781
+ value_valid = schema.get('value_valid')
782
+ item_valid = schema.get('item_valid')
783
+ allow_keys = schema.get('allow_keys')
784
+ require_keys = schema.get('require_keys')
785
+ min_keys = schema.get('min_keys')
786
+ wedge_keys = schema.get('wedge_keys')
787
+ mutex_keys = schema.get('mutex_keys')
788
+ deprecate_keys = schema.get('deprecate_keys')
789
+ forbid_keys = schema.get('forbid_keys')
790
+ default = schema.get('default')
791
+ proxy_keys = schema.get('proxy_keys')
792
+
793
+ default = {
794
+ k: valid(v)
795
+ for k,v in (default or dict()).items()}
796
+ all_keys = list()
797
+
798
+ all_keys.extend(allow_keys or ())
799
+ all_keys.extend(require_keys or ())
800
+ all_keys.extend(default.keys())
801
+
802
+ if deprecate_keys:
803
+ all_keys.extend([
804
+ k_new
805
+ for k_old, k_new in deprecate_keys
806
+ if k_new not in [None, OPTIONAL, OPTIONAL_NONE, REQUIRED]])
807
+
808
+ if min_keys:
809
+ for keys in min_keys:
810
+ all_keys.extend(keys)
741
811
 
742
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
743
- class valid_dict(Mapping):
812
+ if wedge_keys:
813
+ for keys in wedge_keys:
814
+ all_keys.extend(keys)
815
+
816
+ if mutex_keys:
817
+ for keys in mutex_keys:
818
+ all_keys.extend(keys)
819
+
820
+ all_keys = tuple(all_keys)
821
+
822
+ # compose validator with parameterized valid_keys method
823
+ validator = valid(
824
+ validator or (lambda v: v),
825
+ partial(valid_keys,
826
+ key_valid = key_valid,
827
+ value_valid = value_valid,
828
+ item_valid = item_valid,
829
+ allow_keys = allow_keys,
830
+ require_keys = require_keys,
831
+ min_keys = min_keys,
832
+ wedge_keys = wedge_keys,
833
+ mutex_keys = mutex_keys,
834
+ deprecate_keys = deprecate_keys,
835
+ forbid_keys = forbid_keys,
836
+ default = default,
837
+ proxy_keys = proxy_keys))
838
+
839
+ namespace['_all_keys'] = all_keys
840
+ namespace['_validator'] = validator
841
+ namespace.setdefault('__slots__', ())
842
+
843
+ for k in all_keys:
844
+ # create attributes mapping to dictionary keys
845
+ name = k.replace('-','_')
846
+ attr = _ValidDictAttr(name, k)
847
+ namespace[name] = attr
848
+
849
+ cls = super().__new__(mcls, name, bases, namespace)
850
+
851
+ return cls
852
+
853
+ #===============================================================================
854
+ class valid_dict(Mapping, metaclass = _ValidDictMeta):
744
855
  """Validated Mapping
745
856
 
746
857
  Attributes
747
858
  ----------
748
- _proxy_key: None | str
859
+ proxy_key: None | str
749
860
  If initialized with a value that is not a Mapping, this key is assigned the
750
861
  value before performing validation.
751
- _key_valid: None | callable
862
+ key_valid: None | callable
752
863
  Validates all keys
753
- _value_valid: None | callable
864
+ value_valid: None | callable
754
865
  Validates all values
755
- _item_valid: None | callable
866
+ item_valid: None | callable
756
867
  Validates all (key,value) pairs
757
- _allow_keys: None | list[str]
868
+ allow_keys: None | list[str]
758
869
  Mapping may not contain keys that are not listed.
759
- _require_keys: None | list[str]
870
+ require_keys: None | list[str]
760
871
  Mapping must contain all listed keys.
761
- _min_keys: None | list[ list[str] ]
872
+ min_keys: None | list[ list[str] ]
762
873
  Mapping must contain at least one key from each list.
763
- _wedge_keys: None | list[ list[str] ]
874
+ wedge_keys: None | list[ list[str] ]
764
875
  Mapping must contain either none or all of the listed keys.
765
- _mutex_keys: None | list[ list[str] ]
876
+ mutex_keys: None | list[ list[str] ]
766
877
  Mapping may contain at most one key from each list.
767
- _deprecate_keys: None | list[ (str, None | str | Required) ]
878
+ deprecate_keys: None | list[ (str, None | str | Required) ]
768
879
  First key is marked as deprecated and removed from the Mapping.
769
880
  If new key is given, the value is remapped to the new key.
770
881
  If new key is Required, an error is raised, otherwise a deprecation warning
771
882
  is reported.
772
- _forbid_keys: None | list[str]
883
+ forbid_keys: None | list[str]
773
884
  Mapping must not contain any of the listed keys.
774
- _default: None | Mapping[object, object | type | Validator]
885
+ default: None | Mapping[object, object | type | Validator]
775
886
  Default value or validator for given keys.
776
- _validator : None | Validator
887
+ validator : None | Validator
777
888
  General validator for entire Mapping after above constraints are satisfied.
778
889
  See Also
779
890
  --------
780
891
  * :func:`valid_keys`
781
892
  """
782
-
783
- _proxy_key = None
784
- _proxy_keys = None
785
- _key_valid = None
786
- _value_valid = None
787
- _item_valid = None
788
- _allow_keys = None
789
- _require_keys = None
790
- _min_keys = None
791
- _wedge_keys = None
792
- _mutex_keys = None
793
- _deprecate_keys = None
794
- _forbid_keys = None
795
- _default = None
796
- _validator = None
797
-
798
- # internal
799
- _p_all_keys = list()
800
-
801
- #-----------------------------------------------------------------------------
802
- def __new__( cls, *args, **kwargs ):
803
-
804
- self = super().__new__( cls )
805
- self._p_attrs_modify = False
806
-
807
- with attrs_modify( self ):
808
- self._p_dict = dict()
809
- self._p_key_attr = dict()
810
-
811
- return self
893
+ __slots__ = ('_data',)
894
+
895
+ proxy_key = None
896
+ proxy_keys = None
897
+ key_valid = None
898
+ value_valid = None
899
+ item_valid = None
900
+ allow_keys = None
901
+ require_keys = None
902
+ min_keys = None
903
+ wedge_keys = None
904
+ mutex_keys = None
905
+ deprecate_keys = None
906
+ forbid_keys = None
907
+ default = None
908
+ validator = None
812
909
 
813
910
  #---------------------------------------------------------------------------#
814
911
  # pylint: disable-next=E0602
@@ -822,224 +919,132 @@ class valid_dict(Mapping):
822
919
 
823
920
  v = args[0]
824
921
 
825
- if v in [None, OPTIONAL, OPTIONAL_NONE]:
922
+ if v in (None, OPTIONAL, OPTIONAL_NONE):
826
923
  args = [dict()]
827
- elif cls._proxy_key:
828
- args = [{ cls._proxy_key : v }]
829
-
830
- self._p_dict = dict(*args, **kwargs)
831
-
832
- with attrs_modify( self ):
833
- self._default = { k: valid(v) for k,v in ( cls._default or dict() ).items() }
834
- self._validator = valid(cls._validator or (lambda v: v))
835
924
 
836
- self._p_all_keys = list()
837
- self._validating = False
925
+ elif cls.proxy_key:
926
+ args = [{ cls.proxy_key : v }]
838
927
 
839
- self._p_all_keys.extend( self._allow_keys or [] )
840
- self._p_all_keys.extend( self._require_keys or [] )
841
- self._p_all_keys.extend( self._default.keys() )
842
-
843
- if self._deprecate_keys:
844
- for keys in self._deprecate_keys:
845
- self._p_all_keys.extend( [
846
- k_new
847
- for k_old, k_new in self._deprecate_keys
848
- if k_new not in [None, OPTIONAL, OPTIONAL_NONE, REQUIRED] ] )
928
+ self._data = dict(*args, **kwargs)
929
+ self._validate()
849
930
 
850
- if self._min_keys:
851
- for keys in self._min_keys:
852
- self._p_all_keys.extend(keys)
931
+ #-----------------------------------------------------------------------------
932
+ def __eq__(self, other):
933
+ if isinstance(other, valid_dict):
934
+ other = other._data
853
935
 
854
- if self._wedge_keys:
855
- for keys in self._wedge_keys:
856
- self._p_all_keys.extend(keys)
936
+ return self._data.__eq__(other)
857
937
 
858
- if self._mutex_keys:
859
- for keys in self._mutex_keys:
860
- self._p_all_keys.extend(keys)
938
+ #-----------------------------------------------------------------------------
939
+ def __ne__(self, other):
940
+ if isinstance(other, valid_dict):
941
+ other = other._data
861
942
 
862
- self._p_key_attr = { k.replace('-','_') : k for k in self._p_all_keys }
863
- self._validate()
943
+ return self._data.__ne__(other)
864
944
 
865
945
  #-----------------------------------------------------------------------------
866
946
  def __copy__(self):
867
- obj = copy(super())
868
- obj._p_dict = copy(self._p_dict)
947
+ cls = type(self)
948
+ obj = cls.__new__(cls)
949
+ obj._data = copy(self._data)
869
950
  return obj
870
951
 
871
952
  #-----------------------------------------------------------------------------
872
953
  def __str__(self):
873
- return str(self._p_dict)
954
+ return str(self._data)
874
955
 
875
956
  #-----------------------------------------------------------------------------
876
957
  def __repr__(self):
877
- return str(self._p_dict)
958
+ return repr(self._data)
878
959
 
879
960
  #-----------------------------------------------------------------------------
880
- def __len__( self ):
881
- return len(self._p_dict)
961
+ def __len__(self):
962
+ return len(self._data)
882
963
 
883
964
  #-----------------------------------------------------------------------------
884
- def __iter__( self ):
885
- return iter(self._p_dict)
965
+ def __iter__(self):
966
+ return iter(self._data)
886
967
 
887
968
  #-----------------------------------------------------------------------------
888
- def keys( self ):
889
- return self._p_dict.keys()
969
+ def __contains__(self, name):
970
+ return self._data.__contains__(name)
890
971
 
891
972
  #-----------------------------------------------------------------------------
892
- def values( self ):
893
- return self._p_dict.values()
973
+ def keys(self):
974
+ return self._data.keys()
894
975
 
895
976
  #-----------------------------------------------------------------------------
896
- def items( self ):
897
- return self._p_dict.items()
977
+ def values(self):
978
+ return self._data.values()
898
979
 
899
980
  #-----------------------------------------------------------------------------
900
- def clear( self ):
901
- self._p_dict.clear()
981
+ def items(self):
982
+ return self._data.items()
983
+
984
+ #-----------------------------------------------------------------------------
985
+ def clear(self):
986
+ self._data.clear()
902
987
  self._validate()
903
988
 
904
989
  #---------------------------------------------------------------------------#
905
990
  def update(self, *args, **kwargs ):
906
- self._p_dict.update(*args, **kwargs)
991
+ self._data.update(*args, **kwargs)
907
992
  self._validate()
908
993
 
909
994
  #-----------------------------------------------------------------------------
910
995
  def setdefault( self, *args, **kwargs ):
911
- val = self._p_dict.setdefault(*args, **kwargs)
996
+ val = self._data.setdefault(*args, **kwargs)
912
997
  self._validate()
913
998
  return val
914
999
 
915
1000
  #-----------------------------------------------------------------------------
916
1001
  def get( self, *args, **kwargs ):
917
- return self._p_dict.get(*args, **kwargs)
1002
+ return self._data.get(*args, **kwargs)
918
1003
 
919
1004
  #-----------------------------------------------------------------------------
920
1005
  def pop( self, *args, **kwargs ):
921
- val = self._p_dict.pop(*args, **kwargs)
1006
+ val = self._data.pop(*args, **kwargs)
1007
+ self._validate()
1008
+ return val
1009
+
1010
+ #-----------------------------------------------------------------------------
1011
+ def popitem( self, *args, **kwargs ):
1012
+ val = self._data.popitem(*args, **kwargs)
922
1013
  self._validate()
923
1014
  return val
924
1015
 
925
1016
  #-----------------------------------------------------------------------------
926
1017
  def __getitem__( self, key ):
927
- return self._p_dict.__getitem__(key)
1018
+ return self._data.__getitem__(key)
928
1019
 
929
1020
  #-----------------------------------------------------------------------------
930
1021
  def __setitem__( self, key, val ):
931
- self._p_dict.__setitem__(key, val)
1022
+ self._data.__setitem__(key, val)
932
1023
  self._validate()
933
1024
 
934
1025
  #-----------------------------------------------------------------------------
935
1026
  def __delitem__( self, key ):
936
- self._p_dict.__delitem__( key )
1027
+ self._data.__delitem__( key )
937
1028
  self._validate()
938
1029
 
939
- #-----------------------------------------------------------------------------
940
- def __setattr__( self, name, val ):
941
-
942
- try:
943
-
944
- if name != '_p_attrs_modify' and not attrs_modifiable( self ):
945
- # only set mapping if base object doesn't have the attribute
946
- super().__getattribute__(name)
947
-
948
- if name in self._p_key_attr:
949
- warnings.warn(f"'{type(self).__name__}' attribute shadows mapping key: {name}")
950
-
951
- object.__setattr__( self, name, val )
952
- return
953
-
954
- except AttributeError as e:
955
- pass
956
-
957
- if name != '_p_dict' and name != '_p_key_attr':
958
- if name in self._p_dict:
959
- self._p_dict[ name ] = val
960
- self._validate()
961
- return
962
-
963
- if name in self._p_key_attr:
964
- self._p_dict[ self._p_key_attr[name] ] = val
965
- self._validate()
966
- return
967
-
968
- raise AttributeError(
969
- f"'{type(self).__name__}' object has no key '{name}'."
970
- " New keys must be added using a Mapping method;"
971
- f" E.G. x['{name}'] = {val}" )
972
-
973
-
974
- #-----------------------------------------------------------------------------
975
- def __getattribute__( self, name ):
976
-
977
- try:
978
- val = super().__getattribute__(name)
979
-
980
- if name != '_p_dict' and name != '_p_key_attr' and name in self._p_key_attr:
981
- warnings.warn(f"'{type(self).__name__}' attribute shadows mapping key: {name}")
982
-
983
- return val
984
-
985
- except AttributeError as e:
986
- pass
987
-
988
- # only get mapping if base object does not have attribute
989
- if name != '_p_dict' and name != '_p_key_attr':
990
- if name in self._p_dict:
991
- return self._p_dict[ name ]
992
-
993
- if name in self._p_key_attr:
994
- return self._p_dict[ self._p_key_attr[name] ]
995
-
996
- raise AttributeError(
997
- f"'{type(self).__name__}' object has no key '{name}'")
998
-
999
-
1000
1030
  #-----------------------------------------------------------------------------
1001
1031
  def _validate(self):
1002
- if self._validating:
1003
- return
1004
-
1005
- with validating_block(self):
1006
- self.update( **self._validator( valid_keys(
1007
- self._p_dict,
1008
- key_valid = self._key_valid,
1009
- value_valid = self._value_valid,
1010
- item_valid = self._item_valid,
1011
- allow_keys = self._allow_keys,
1012
- require_keys = self._require_keys,
1013
- min_keys = self._min_keys,
1014
- wedge_keys = self._wedge_keys,
1015
- mutex_keys = self._mutex_keys,
1016
- deprecate_keys = self._deprecate_keys,
1017
- forbid_keys = self._forbid_keys,
1018
- default = self._default,
1019
- proxy_keys = self._proxy_keys ) ) )
1020
-
1021
- #-----------------------------------------------------------------------------
1022
- def __str__(self):
1023
- return str(self._p_dict)
1024
-
1025
- #-----------------------------------------------------------------------------
1026
- def __repr__(self):
1027
- return str(self)
1032
+ self._data = self._validator(self._data)
1028
1033
 
1029
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1034
+ #===============================================================================
1030
1035
  class valid_list(list):
1031
1036
  """Validated list
1032
1037
  """
1033
1038
  _as_list = None
1034
- _value_valid = None
1039
+ value_valid = None
1035
1040
  _min_len = 0
1036
1041
 
1037
1042
  #---------------------------------------------------------------------------#
1038
1043
  def __init__( self, vals = None ):
1039
1044
  cls = type(self)
1040
1045
  self._as_list = cls._as_list or list
1041
- self._value_valid = valid(
1042
- cls._value_valid or (lambda v: v))
1046
+ self.value_valid = valid(
1047
+ cls.value_valid or (lambda v: v))
1043
1048
 
1044
1049
  if vals is None:
1045
1050
  vals = list()
@@ -1049,13 +1054,13 @@ class valid_list(list):
1049
1054
 
1050
1055
  for i,v in enumerate(vals):
1051
1056
  with validating(key = i):
1052
- vals[i] = self._value_valid(v)
1057
+ vals[i] = self.value_valid(v)
1053
1058
 
1054
1059
  super().__init__(vals)
1055
1060
  self._validate()
1056
1061
 
1057
1062
  #-----------------------------------------------------------------------------
1058
- def clear( self ):
1063
+ def clear(self):
1059
1064
  super().clear()
1060
1065
  self._validate()
1061
1066
 
@@ -1068,7 +1073,7 @@ class valid_list(list):
1068
1073
  #---------------------------------------------------------------------------#
1069
1074
  def append(self, val ):
1070
1075
  with validating(key = len(self)):
1071
- val = self._value_valid(val)
1076
+ val = self.value_valid(val)
1072
1077
 
1073
1078
  super().append(val)
1074
1079
 
@@ -1078,14 +1083,14 @@ class valid_list(list):
1078
1083
 
1079
1084
  for i,v in enumerate(vals):
1080
1085
  with validating(key = len(self) + i):
1081
- vals[i] = self._value_valid(v)
1086
+ vals[i] = self.value_valid(v)
1082
1087
 
1083
1088
  super().extend(vals)
1084
1089
 
1085
1090
  #-----------------------------------------------------------------------------
1086
1091
  def __setitem__( self, key, val ):
1087
1092
  with validating(key = key):
1088
- val = self._value_valid(val)
1093
+ val = self.value_valid(val)
1089
1094
 
1090
1095
  super().__setitem__(key, val)
1091
1096
 
@@ -1094,7 +1099,7 @@ class valid_list(list):
1094
1099
  if len(self) < self._min_len:
1095
1100
  raise ValidationError(f"Must have length >= {self._min_len}: {len(self)}")
1096
1101
 
1097
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1102
+ #===============================================================================
1098
1103
  def mapget(
1099
1104
  obj,
1100
1105
  path,
@@ -1124,7 +1129,7 @@ def mapget(
1124
1129
 
1125
1130
  return _obj
1126
1131
 
1127
- #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1132
+ #===============================================================================
1128
1133
  def as_list(obj):
1129
1134
  if isinstance(obj, (str, Mapping)) or not isinstance(obj, Iterable):
1130
1135
  return [obj]