sdata 0.19.0__tar.gz → 0.22.0__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 sdata might be problematic. Click here for more details.

Files changed (83) hide show
  1. sdata-0.22.0/PKG-INFO +128 -0
  2. {sdata-0.19.0 → sdata-0.22.0}/sdata/__init__.py +6 -2
  3. sdata-0.22.0/sdata/contrib/attrdict/__init__.py +10 -0
  4. sdata-0.22.0/sdata/contrib/attrdict/default.py +133 -0
  5. sdata-0.22.0/sdata/contrib/attrdict/dictionary.py +60 -0
  6. sdata-0.22.0/sdata/contrib/attrdict/mapping.py +100 -0
  7. sdata-0.22.0/sdata/contrib/attrdict/merge.py +47 -0
  8. sdata-0.22.0/sdata/contrib/attrdict/mixins.py +213 -0
  9. sdata-0.22.0/sdata/contrib/semver.py +1259 -0
  10. sdata-0.22.0/sdata/contrib/sqlitedict.py +598 -0
  11. {sdata-0.19.0 → sdata-0.22.0}/sdata/data.py +48 -5
  12. {sdata-0.19.0 → sdata-0.22.0}/sdata/doe.py +29 -15
  13. sdata-0.22.0/sdata/iolib/db.py +86 -0
  14. {sdata-0.19.0/sdata/io → sdata-0.22.0/sdata/iolib}/hdf.py +1 -1
  15. sdata-0.22.0/sdata/iolib/isomme.py +410 -0
  16. {sdata-0.19.0/sdata/io → sdata-0.22.0/sdata/iolib}/pud.py +1 -1
  17. sdata-0.22.0/sdata/iolib/simple_signature.py +446 -0
  18. {sdata-0.19.0 → sdata-0.22.0}/sdata/metadata.py +2 -2
  19. sdata-0.22.0/sdata/suuid.py +178 -0
  20. {sdata-0.19.0 → sdata-0.22.0}/sdata/timestamp.py +5 -2
  21. sdata-0.22.0/sdata.egg-info/PKG-INFO +128 -0
  22. {sdata-0.19.0 → sdata-0.22.0}/sdata.egg-info/SOURCES.txt +22 -9
  23. {sdata-0.19.0/tests/io → sdata-0.22.0/tests/iolib}/pud/test_pud.py +2 -2
  24. {sdata-0.19.0/tests/io → sdata-0.22.0/tests/iolib}/test_hdf.py +1 -1
  25. {sdata-0.19.0 → sdata-0.22.0}/tests/test_blob.py +3 -0
  26. {sdata-0.19.0 → sdata-0.22.0}/tests/test_data.py +11 -0
  27. sdata-0.22.0/tests/test_sqlite.py +15 -0
  28. {sdata-0.19.0 → sdata-0.22.0}/tests/test_vault.py +2 -2
  29. sdata-0.19.0/PKG-INFO +0 -125
  30. sdata-0.19.0/sdata.egg-info/PKG-INFO +0 -125
  31. {sdata-0.19.0 → sdata-0.22.0}/LICENSE-MIT +0 -0
  32. {sdata-0.19.0 → sdata-0.22.0}/MANIFEST.in +0 -0
  33. {sdata-0.19.0 → sdata-0.22.0}/README.md +0 -0
  34. {sdata-0.19.0 → sdata-0.22.0}/sdata/blob.py +0 -0
  35. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/__init__.py +0 -0
  36. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/ranger/__init__.py +0 -0
  37. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/ranger/collections/__init__.py +0 -0
  38. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/ranger/collections/rangebucketmap.py +0 -0
  39. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/ranger/collections/rangemap.py +0 -0
  40. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/ranger/collections/rangeset.py +0 -0
  41. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/ranger/range/__init__.py +0 -0
  42. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/ranger/range/cut.py +0 -0
  43. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/ranger/range/range.py +0 -0
  44. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/simple_graph_db/LICENSE +0 -0
  45. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/simple_graph_db/__init__.py +0 -0
  46. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/simple_graph_db/database.py +0 -0
  47. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/simple_graph_db/schema.sql +0 -0
  48. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/sobol_seq.py +0 -0
  49. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/sortedcontainers/LICENSE +0 -0
  50. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/sortedcontainers/__init__.py +0 -0
  51. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/sortedcontainers/sorteddict.py +0 -0
  52. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/sortedcontainers/sortedlist.py +0 -0
  53. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/sortedcontainers/sortedset.py +0 -0
  54. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/timeflake/__init__.py +0 -0
  55. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/timeflake/flake.py +0 -0
  56. {sdata-0.19.0 → sdata-0.22.0}/sdata/contrib/timeflake/utils.py +0 -0
  57. {sdata-0.19.0 → sdata-0.22.0}/sdata/experiments/__init__.py +0 -0
  58. {sdata-0.19.0 → sdata-0.22.0}/sdata/experiments/ks2.py +0 -0
  59. {sdata-0.19.0 → sdata-0.22.0}/sdata/experiments/lapshear.py +0 -0
  60. {sdata-0.19.0 → sdata-0.22.0}/sdata/experiments/material.py +0 -0
  61. {sdata-0.19.0/sdata/io → sdata-0.22.0/sdata/iolib}/__init__.py +0 -0
  62. {sdata-0.19.0/sdata/io → sdata-0.22.0/sdata/iolib}/pgp.py +0 -0
  63. {sdata-0.19.0/sdata/io → sdata-0.22.0/sdata/iolib}/vault.py +0 -0
  64. {sdata-0.19.0 → sdata-0.22.0}/sdata/tools.py +0 -0
  65. {sdata-0.19.0 → sdata-0.22.0}/sdata/workbook.py +0 -0
  66. {sdata-0.19.0 → sdata-0.22.0}/sdata.egg-info/dependency_links.txt +0 -0
  67. {sdata-0.19.0 → sdata-0.22.0}/sdata.egg-info/requires.txt +0 -0
  68. {sdata-0.19.0 → sdata-0.22.0}/sdata.egg-info/top_level.txt +0 -0
  69. {sdata-0.19.0 → sdata-0.22.0}/setup.cfg +0 -0
  70. {sdata-0.19.0 → sdata-0.22.0}/setup.py +0 -0
  71. {sdata-0.19.0 → sdata-0.22.0}/tests/__init__.py +0 -0
  72. {sdata-0.19.0/tests/io → sdata-0.22.0/tests/iolib}/__init__.py +0 -0
  73. {sdata-0.19.0/tests/io → sdata-0.22.0/tests/iolib}/pud/__init__.py +0 -0
  74. {sdata-0.19.0 → sdata-0.22.0}/tests/test_attribute.py +0 -0
  75. {sdata-0.19.0 → sdata-0.22.0}/tests/test_io.py +0 -0
  76. {sdata-0.19.0 → sdata-0.22.0}/tests/test_ks2.py +0 -0
  77. {sdata-0.19.0 → sdata-0.22.0}/tests/test_metadata.py +0 -0
  78. {sdata-0.19.0 → sdata-0.22.0}/tests/test_table.py +0 -0
  79. {sdata-0.19.0 → sdata-0.22.0}/tests/test_testprogram.py +0 -0
  80. {sdata-0.19.0 → sdata-0.22.0}/tests/test_timeflake.py +0 -0
  81. {sdata-0.19.0 → sdata-0.22.0}/tests/test_timestamp.py +0 -0
  82. {sdata-0.19.0 → sdata-0.22.0}/tests/test_tools.py +0 -0
  83. {sdata-0.19.0 → sdata-0.22.0}/tests/test_workbook.py +0 -0
sdata-0.22.0/PKG-INFO ADDED
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.1
2
+ Name: sdata
3
+ Version: 0.22.0
4
+ Summary: a structured data format
5
+ Home-page: https://github.com/lepy/sdata
6
+ Author: Lepy
7
+ Author-email: lepy@mailbox.org
8
+ Maintainer: Lepy
9
+ Maintainer-email: lepy@mailbox.org
10
+ License: MIT/Apache-2.0
11
+ Keywords: open data
12
+ Platform: UNKNOWN
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Natural Language :: English
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 2.7
19
+ Classifier: Programming Language :: Python :: 3.5
20
+ Classifier: Programming Language :: Python :: 3.6
21
+ Classifier: Programming Language :: Python :: Implementation :: CPython
22
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE-MIT
25
+
26
+
27
+ [![PyPI version](https://badge.fury.io/py/sdata.svg)](https://badge.fury.io/py/sdata)
28
+ [![PyPI](https://img.shields.io/pypi/v/sdata.svg?style=flat-square)](https://pypi.python.org/pypi/sdata/)
29
+ [![readthedocs](https://readthedocs.org/projects/sdata/badge/?version=latest)](http://sdata.readthedocs.io/en/latest/)
30
+ [![Build Status](https://travis-ci.org/lepy/sdata.svg?branch=master)](https://travis-ci.org/lepy/sdata)
31
+ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/107e46dc4eee4b58a6ef82fce3043a3e)](https://www.codacy.com/app/lepy/sdata?utm_source=github.com&utm_medium=referral&utm_content=lepy/sdata&utm_campaign=Badge_Grade)
32
+ [![Coverage Status](https://coveralls.io/repos/github/lepy/sdata/badge.svg?branch=master)](https://coveralls.io/github/lepy/sdata?branch=master)
33
+ [![Das sdata-Format v0.8.4](https://zenodo.org/badge/DOI/10.5281/zenodo.4311323.svg)](https://doi.org/10.5281/zenodo.4311323)
34
+
35
+ https://lepy.github.io/sdata/
36
+
37
+ # Structured data format (sdata)
38
+
39
+ ## Design goals
40
+
41
+ * open data format for open science projects
42
+ * self describing data
43
+ * flexible data structure layout
44
+ * hierarchical data structure (nesting groups, dictionaries)
45
+ * (posix path syntax support?)
46
+ * extendable data structure
47
+ * data format versions
48
+ * platform independent
49
+ * simple object model
50
+ * support of standard metadata formats (key/value, ...)
51
+ * support of standard dataset formats (hdf5, netcdf, csv, ...)
52
+ * support of standard dataset types (datacubes, tables, series, ...)
53
+ * support of physical units (conversion of units)
54
+ * transparent, optional data compression (zlib, blosc, ...)
55
+ * support of (de-)serialization of every dataset type (group, data, metadata)
56
+ * easy defineable (project) standards, e.g. for a uniaxial tension test (UT)
57
+ * (optional data encryption (gpg, ...))
58
+ * change management support?
59
+ * Enable use of data structures from existing tensor libraries transparently
60
+ * (single writer/ multiple reader (swmr) support)
61
+ * (nested data support)
62
+
63
+ ```
64
+ df = pandas.DataFrame({"a":[1,2,3]})
65
+ import sdata
66
+ data = sdata.Data(name="my_data", table=df, comment="A remarkable comment")
67
+ data.metadata.add("my_key", 123, unit="m^3", description="a volume")
68
+ data.metadata.add("force", 1.234, unit="kN", description="x force")
69
+ data.to_xlsx(filepath="my_data.xlsx")
70
+ print(data.metadata.df)
71
+ ```
72
+
73
+ ```
74
+ name value dtype unit description
75
+ key
76
+ name name my_data str -
77
+ uuid uuid 08222ca66e5047808bdc3b35d8f17224 str -
78
+ my_key my_key 123 int m^3 a volume
79
+ force force 1.234 float kN x force
80
+ ```
81
+ ## Howto
82
+
83
+
84
+ * [Das sdata-Format - slides](https://lepy.github.io/sdata/ipynb/Das_sdata_Format.slides.html#)
85
+
86
+
87
+ ## Demo App
88
+
89
+ * [test the demo app with editor](https://share.streamlit.io/lepy/sdata_streamlit/main/sdata_editor.py)
90
+
91
+ Try to paste some Excel-Data in the forms ...
92
+
93
+
94
+ ## Metadata
95
+
96
+ ### Attributes
97
+
98
+ * name
99
+ * value
100
+ * dtype
101
+ * unit
102
+ * description
103
+ * label
104
+ * required
105
+
106
+ ### dtypes for attributes
107
+
108
+ * int
109
+ * float
110
+ * str
111
+ * bool
112
+ * timestamp (datetime.isoformat with timezone)
113
+
114
+ ## paper
115
+
116
+ * [Das sdata-Format](https://zenodo.org/record/4311323#.X89yo9-YXys)
117
+ * Ingolf Lepenies. (2020). Das sdata-Format (Version 0.8.4). http://doi.org/10.5281/zenodo.4311323
118
+ * [slides](https://lepy.github.io/sdata/ipynb/Das_sdata_Format.slides.html#),
119
+ [html](https://lepy.github.io/sdata/paper/2020/Das_sdata-Format.html),
120
+ [pdf](https://lepy.github.io/sdata/paper/2020/Das_sdata-Format.pdf)
121
+ [temperaturmessung-001.json](https://lepy.github.io/sdata/paper/2020/temperaturmessung-001.json)
122
+ [temperaturmessung-001.xlsx](https://lepy.github.io/sdata/paper/2020/temperaturmessung-001.xlsx)
123
+
124
+ * [sdata](https://doi.org/10.5281/zenodo.4311396)
125
+ * Ingolf Lepenies. (2020, December 8). sdata - a structured data format (Version 0.8.4). Zenodo. http://doi.org/10.5281/zenodo.4311397
126
+
127
+
128
+
@@ -1,7 +1,7 @@
1
1
  # -*-coding: utf-8-*-
2
2
  from __future__ import division
3
3
 
4
- __version__ = '0.19.0'
4
+ __version__ = '0.22.0'
5
5
  __revision__ = None
6
6
  __version_info__ = tuple([int(num) for num in __version__.split('.')])
7
7
 
@@ -21,6 +21,7 @@ import pandas
21
21
  from sdata.metadata import Metadata, Attribute
22
22
  from sdata.data import Data
23
23
  from sdata.blob import Blob
24
+ from sdata.suuid import SUUID
24
25
 
25
26
  # import sdata.timestamp as timestamp
26
27
  from sdata.timestamp import today_str, now_utc_str, now_local_str
@@ -36,6 +37,7 @@ except:
36
37
  def uuid_from_str(name):
37
38
  return uuid.uuid3(uuid.NAMESPACE_DNS, name)
38
39
 
40
+
39
41
  def osname(name, lower=True):
40
42
  mapper = [("ä", "ae"), ("ö", "oe"), ("ü", "ue"), ("Ä", "Ae"), ("Ö", "Oe"), ("Ü", "Ue"),
41
43
  ("ß", "sz"), (" ", "_"), ("/", "_"), ("\\", "_")]
@@ -45,11 +47,13 @@ def osname(name, lower=True):
45
47
  name = name.lower()
46
48
  return name.encode('ascii', 'replace').decode("ascii")
47
49
 
50
+
48
51
  SDATACLS = {"Data": Data,
49
52
  "Blob": Blob,
53
+ "SUUID": SUUID,
50
54
  }
51
55
 
52
- __all__ = ["Data", "Blob"]
56
+ __all__ = ["Data", "Blob", "SUUID"]
53
57
 
54
58
 
55
59
  def print_classes():
@@ -0,0 +1,10 @@
1
+ """
2
+ attrdict contains several mapping objects that allow access to their
3
+ keys as attributes.
4
+ """
5
+ from .mapping import AttrMap
6
+ from .dictionary import AttrDict
7
+ from .default import AttrDefault
8
+
9
+
10
+ __all__ = ['AttrMap', 'AttrDict', 'AttrDefault']
@@ -0,0 +1,133 @@
1
+ """
2
+ A subclass of MutableAttr that has defaultdict support.
3
+ """
4
+ try:
5
+ from collections.abc import Mapping
6
+ except ImportError:
7
+ from collections import Mapping
8
+
9
+ import six
10
+
11
+ from .mixins import MutableAttr
12
+
13
+
14
+ __all__ = ['AttrDefault']
15
+
16
+
17
+ class AttrDefault(MutableAttr):
18
+ """
19
+ An implementation of MutableAttr with defaultdict support
20
+ """
21
+ def __init__(self, default_factory=None, items=None, sequence_type=tuple,
22
+ pass_key=False):
23
+ if items is None:
24
+ items = {}
25
+ elif not isinstance(items, Mapping):
26
+ items = dict(items)
27
+
28
+ self._setattr('_default_factory', default_factory)
29
+ self._setattr('_mapping', items)
30
+ self._setattr('_sequence_type', sequence_type)
31
+ self._setattr('_pass_key', pass_key)
32
+ self._setattr('_allow_invalid_attributes', False)
33
+
34
+ def _configuration(self):
35
+ """
36
+ The configuration for a AttrDefault instance
37
+ """
38
+ return self._sequence_type, self._default_factory, self._pass_key
39
+
40
+ def __getitem__(self, key):
41
+ """
42
+ Access a value associated with a key.
43
+
44
+ Note: values returned will not be wrapped, even if recursive
45
+ is True.
46
+ """
47
+ if key in self._mapping:
48
+ return self._mapping[key]
49
+ elif self._default_factory is not None:
50
+ return self.__missing__(key)
51
+
52
+ raise KeyError(key)
53
+
54
+ def __setitem__(self, key, value):
55
+ """
56
+ Add a key-value pair to the instance.
57
+ """
58
+ self._mapping[key] = value
59
+
60
+ def __delitem__(self, key):
61
+ """
62
+ Delete a key-value pair
63
+ """
64
+ del self._mapping[key]
65
+
66
+ def __len__(self):
67
+ """
68
+ Check the length of the mapping.
69
+ """
70
+ return len(self._mapping)
71
+
72
+ def __iter__(self):
73
+ """
74
+ Iterated through the keys.
75
+ """
76
+ return iter(self._mapping)
77
+
78
+ def __missing__(self, key):
79
+ """
80
+ Add a missing element.
81
+ """
82
+ if self._pass_key:
83
+ self[key] = value = self._default_factory(key)
84
+ else:
85
+ self[key] = value = self._default_factory()
86
+
87
+ return value
88
+
89
+ def __repr__(self):
90
+ """
91
+ Return a string representation of the object.
92
+ """
93
+ return six.u(
94
+ "AttrDefault({default_factory}, {pass_key}, {mapping})"
95
+ ).format(
96
+ default_factory=repr(self._default_factory),
97
+ pass_key=repr(self._pass_key),
98
+ mapping=repr(self._mapping),
99
+ )
100
+
101
+ def __getstate__(self):
102
+ """
103
+ Serialize the object.
104
+ """
105
+ return (
106
+ self._default_factory,
107
+ self._mapping,
108
+ self._sequence_type,
109
+ self._pass_key,
110
+ self._allow_invalid_attributes,
111
+ )
112
+
113
+ def __setstate__(self, state):
114
+ """
115
+ Deserialize the object.
116
+ """
117
+ (default_factory, mapping, sequence_type, pass_key,
118
+ allow_invalid_attributes) = state
119
+
120
+ self._setattr('_default_factory', default_factory)
121
+ self._setattr('_mapping', mapping)
122
+ self._setattr('_sequence_type', sequence_type)
123
+ self._setattr('_pass_key', pass_key)
124
+ self._setattr('_allow_invalid_attributes', allow_invalid_attributes)
125
+
126
+ @classmethod
127
+ def _constructor(cls, mapping, configuration):
128
+ """
129
+ A standardized constructor.
130
+ """
131
+ sequence_type, default_factory, pass_key = configuration
132
+ return cls(default_factory, mapping, sequence_type=sequence_type,
133
+ pass_key=pass_key)
@@ -0,0 +1,60 @@
1
+ """
2
+ A dict that implements MutableAttr.
3
+ """
4
+ from .mixins import MutableAttr
5
+
6
+ import six
7
+
8
+
9
+ __all__ = ['AttrDict']
10
+
11
+
12
+ class AttrDict(dict, MutableAttr):
13
+ """
14
+ A dict that implements MutableAttr.
15
+ """
16
+ def __init__(self, *args, **kwargs):
17
+ super(AttrDict, self).__init__(*args, **kwargs)
18
+
19
+ self._setattr('_sequence_type', tuple)
20
+ self._setattr('_allow_invalid_attributes', False)
21
+
22
+ def _configuration(self):
23
+ """
24
+ The configuration for an attrmap instance.
25
+ """
26
+ return self._sequence_type
27
+
28
+ def __getstate__(self):
29
+ """
30
+ Serialize the object.
31
+ """
32
+ return (
33
+ self.copy(),
34
+ self._sequence_type,
35
+ self._allow_invalid_attributes
36
+ )
37
+
38
+ def __setstate__(self, state):
39
+ """
40
+ Deserialize the object.
41
+ """
42
+ mapping, sequence_type, allow_invalid_attributes = state
43
+ self.update(mapping)
44
+ self._setattr('_sequence_type', sequence_type)
45
+ self._setattr('_allow_invalid_attributes', allow_invalid_attributes)
46
+
47
+ def __repr__(self):
48
+ return six.u('AttrDict({contents})').format(
49
+ contents=super(AttrDict, self).__repr__()
50
+ )
51
+
52
+ @classmethod
53
+ def _constructor(cls, mapping, configuration):
54
+ """
55
+ A standardized constructor.
56
+ """
57
+ attr = cls(mapping)
58
+ attr._setattr('_sequence_type', configuration)
59
+
60
+ return attr
@@ -0,0 +1,100 @@
1
+ """
2
+ An implementation of MutableAttr.
3
+ """
4
+ try:
5
+ from collections.abc import Mapping
6
+ except ImportError:
7
+ from collections import Mapping
8
+
9
+ import six
10
+
11
+ from .mixins import MutableAttr
12
+
13
+
14
+ __all__ = ['AttrMap']
15
+
16
+
17
+ class AttrMap(MutableAttr):
18
+ """
19
+ An implementation of MutableAttr.
20
+ """
21
+ def __init__(self, items=None, sequence_type=tuple):
22
+ if items is None:
23
+ items = {}
24
+ elif not isinstance(items, Mapping):
25
+ items = dict(items)
26
+
27
+ self._setattr('_sequence_type', sequence_type)
28
+ self._setattr('_mapping', items)
29
+ self._setattr('_allow_invalid_attributes', False)
30
+
31
+ def _configuration(self):
32
+ """
33
+ The configuration for an attrmap instance.
34
+ """
35
+ return self._sequence_type
36
+
37
+ def __getitem__(self, key):
38
+ """
39
+ Access a value associated with a key.
40
+ """
41
+ return self._mapping[key]
42
+
43
+ def __setitem__(self, key, value):
44
+ """
45
+ Add a key-value pair to the instance.
46
+ """
47
+ self._mapping[key] = value
48
+
49
+ def __delitem__(self, key):
50
+ """
51
+ Delete a key-value pair
52
+ """
53
+ del self._mapping[key]
54
+
55
+ def __len__(self):
56
+ """
57
+ Check the length of the mapping.
58
+ """
59
+ return len(self._mapping)
60
+
61
+ def __iter__(self):
62
+ """
63
+ Iterated through the keys.
64
+ """
65
+ return iter(self._mapping)
66
+
67
+ def __repr__(self):
68
+ """
69
+ Return a string representation of the object.
70
+ """
71
+ # sequence type seems like more trouble than it is worth.
72
+ # If people want full serialization, they can pickle, and in
73
+ # 99% of cases, sequence_type won't change anyway
74
+ return six.u("AttrMap({mapping})").format(mapping=repr(self._mapping))
75
+
76
+ def __getstate__(self):
77
+ """
78
+ Serialize the object.
79
+ """
80
+ return (
81
+ self._mapping,
82
+ self._sequence_type,
83
+ self._allow_invalid_attributes
84
+ )
85
+
86
+ def __setstate__(self, state):
87
+ """
88
+ Deserialize the object.
89
+ """
90
+ mapping, sequence_type, allow_invalid_attributes = state
91
+ self._setattr('_mapping', mapping)
92
+ self._setattr('_sequence_type', sequence_type)
93
+ self._setattr('_allow_invalid_attributes', allow_invalid_attributes)
94
+
95
+ @classmethod
96
+ def _constructor(cls, mapping, configuration):
97
+ """
98
+ A standardized constructor.
99
+ """
100
+ return cls(mapping, sequence_type=configuration)
@@ -0,0 +1,47 @@
1
+ """
2
+ A right-favoring Mapping merge.
3
+ """
4
+ try:
5
+ from collections.abc import Mapping
6
+ except ImportError:
7
+ from collections import Mapping
8
+
9
+
10
+ __all__ = ['merge']
11
+
12
+
13
+ def merge(left, right):
14
+ """
15
+ Merge two mappings objects together, combining overlapping Mappings,
16
+ and favoring right-values
17
+
18
+ left: The left Mapping object.
19
+ right: The right (favored) Mapping object.
20
+
21
+ NOTE: This is not commutative (merge(a,b) != merge(b,a)).
22
+ """
23
+ merged = {}
24
+
25
+ left_keys = frozenset(left)
26
+ right_keys = frozenset(right)
27
+
28
+ # Items only in the left Mapping
29
+ for key in left_keys - right_keys:
30
+ merged[key] = left[key]
31
+
32
+ # Items only in the right Mapping
33
+ for key in right_keys - left_keys:
34
+ merged[key] = right[key]
35
+
36
+ # in both
37
+ for key in left_keys & right_keys:
38
+ left_value = left[key]
39
+ right_value = right[key]
40
+
41
+ if (isinstance(left_value, Mapping) and
42
+ isinstance(right_value, Mapping)): # recursive merge
43
+ merged[key] = merge(left_value, right_value)
44
+ else: # overwrite with right value
45
+ merged[key] = right_value
46
+
47
+ return merged