datatypes 0.31.0__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.
- datatypes/__init__.py +169 -0
- datatypes/collections/__init__.py +25 -0
- datatypes/collections/container.py +248 -0
- datatypes/collections/mapping.py +1226 -0
- datatypes/collections/sequence.py +311 -0
- datatypes/command.py +596 -0
- datatypes/compat.py +206 -0
- datatypes/config/__init__.py +13 -0
- datatypes/config/environ/__init__.py +33 -0
- datatypes/config/environ/base.py +332 -0
- datatypes/config/environ/token.py +173 -0
- datatypes/config/parser.py +256 -0
- datatypes/config/settings.py +352 -0
- datatypes/copy.py +151 -0
- datatypes/csv.py +547 -0
- datatypes/datetime.py +1243 -0
- datatypes/decorators/__init__.py +31 -0
- datatypes/decorators/base.py +449 -0
- datatypes/decorators/descriptor.py +490 -0
- datatypes/decorators/misc.py +152 -0
- datatypes/email.py +439 -0
- datatypes/enum.py +176 -0
- datatypes/event.py +299 -0
- datatypes/exception.py +13 -0
- datatypes/filetypes/__init__.py +10 -0
- datatypes/filetypes/html.py +540 -0
- datatypes/filetypes/toml.py +275 -0
- datatypes/http.py +1443 -0
- datatypes/logging.py +475 -0
- datatypes/number.py +586 -0
- datatypes/parse.py +362 -0
- datatypes/path.py +4358 -0
- datatypes/profile.py +246 -0
- datatypes/reflection/__init__.py +24 -0
- datatypes/reflection/inspect.py +3475 -0
- datatypes/reflection/other.py +843 -0
- datatypes/reflection/path.py +639 -0
- datatypes/server.py +642 -0
- datatypes/service.py +212 -0
- datatypes/ssh.py +812 -0
- datatypes/string.py +1632 -0
- datatypes/token/__init__.py +16 -0
- datatypes/token/abnf.py +1696 -0
- datatypes/token/base.py +921 -0
- datatypes/token/word.py +259 -0
- datatypes/typing.py +126 -0
- datatypes/url.py +988 -0
- datatypes/utils.py +179 -0
- datatypes-0.31.0.dist-info/METADATA +38 -0
- datatypes-0.31.0.dist-info/RECORD +53 -0
- datatypes-0.31.0.dist-info/WHEEL +5 -0
- datatypes-0.31.0.dist-info/licenses/LICENSE.txt +21 -0
- datatypes-0.31.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from collections import ChainMap
|
|
3
|
+
|
|
4
|
+
from ..collections.mapping import Namespace
|
|
5
|
+
from .environ import Environ
|
|
6
|
+
from .parser import Config
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Settings(Namespace):
|
|
10
|
+
"""A settings object that can source values locally, from the environment,
|
|
11
|
+
or from a config file.
|
|
12
|
+
|
|
13
|
+
The order of precedence for values is:
|
|
14
|
+
|
|
15
|
+
1. Locally
|
|
16
|
+
2. Environment
|
|
17
|
+
3. Configuration file
|
|
18
|
+
4. Environ.namespace
|
|
19
|
+
5. Config.path.fileroot
|
|
20
|
+
|
|
21
|
+
:example:
|
|
22
|
+
environ = Environ("<PREFIX>")
|
|
23
|
+
config = Config("<CONFIG-FILE-PATH>")
|
|
24
|
+
local = {
|
|
25
|
+
"foo": 1,
|
|
26
|
+
"bar: 2,
|
|
27
|
+
}
|
|
28
|
+
s = Settings(local, environ, config)
|
|
29
|
+
s.foo # 1
|
|
30
|
+
s["foo"] # 1
|
|
31
|
+
|
|
32
|
+
This extends Namespace so both attribute (.foo) and item (["foo"]) access
|
|
33
|
+
are supported.
|
|
34
|
+
|
|
35
|
+
Retrieve an Environ instance by requesting its prefix namespace:
|
|
36
|
+
|
|
37
|
+
s = Settings(prefix="FOOBAR_")
|
|
38
|
+
s.foobar # Environ instance
|
|
39
|
+
|
|
40
|
+
Or request a Config instance by its fileroot:
|
|
41
|
+
|
|
42
|
+
s = Settings(config="/some/path/foobar.ini")
|
|
43
|
+
s.foobar # Config instance
|
|
44
|
+
"""
|
|
45
|
+
environ_class = Environ
|
|
46
|
+
|
|
47
|
+
config_class = Config
|
|
48
|
+
|
|
49
|
+
def __init__(self, data=None, environ=None, config=None, **kwargs):
|
|
50
|
+
"""
|
|
51
|
+
:param data: Mapping, the local data
|
|
52
|
+
:param environ: Environ, environment data
|
|
53
|
+
:param config: Config, configuration file data
|
|
54
|
+
:param **kwargs:
|
|
55
|
+
* prefix: str, this will create an Environ instance with this
|
|
56
|
+
prefix (eg, prefix="FOO_" would create an Environ("FOO_"))
|
|
57
|
+
"""
|
|
58
|
+
super().__init__(self.get_init_data(data, **kwargs))
|
|
59
|
+
|
|
60
|
+
self.__dict__["config"] = self.get_init_config(config, **kwargs)
|
|
61
|
+
self.__dict__["environ"] = self.get_init_environ(environ, **kwargs)
|
|
62
|
+
|
|
63
|
+
def get_init_environ(self, environ, **kwargs):
|
|
64
|
+
"""Returns the instance that will be used for environ lookups
|
|
65
|
+
|
|
66
|
+
:param environ: Environ
|
|
67
|
+
:returns: Environ
|
|
68
|
+
"""
|
|
69
|
+
if environ is None:
|
|
70
|
+
if self.__module__ != __name__:
|
|
71
|
+
environ = self.environ_class(
|
|
72
|
+
kwargs.get("prefix", self.__module__)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
else:
|
|
76
|
+
environ = self.environ_class(
|
|
77
|
+
kwargs.get("prefix", "")
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return environ
|
|
81
|
+
|
|
82
|
+
def get_init_config(self, config, **kwargs):
|
|
83
|
+
"""Returns the instance that will be used for config lookups
|
|
84
|
+
|
|
85
|
+
:param config: Config|str, if a str it's assumed to be a path to a
|
|
86
|
+
configuration file
|
|
87
|
+
:returns: Config
|
|
88
|
+
"""
|
|
89
|
+
if isinstance(config, str):
|
|
90
|
+
config = self.config_class(config)
|
|
91
|
+
|
|
92
|
+
return config or {}
|
|
93
|
+
|
|
94
|
+
def get_init_data(self, data, **kwargs):
|
|
95
|
+
"""Returns the data that will be passed to the parent as the local
|
|
96
|
+
data
|
|
97
|
+
|
|
98
|
+
:param data: Mapping
|
|
99
|
+
:returns: Mapping
|
|
100
|
+
"""
|
|
101
|
+
return data or {}
|
|
102
|
+
|
|
103
|
+
def _get_value(self, backend, k):
|
|
104
|
+
"""Internal method. Used to check the chainmap for an item value
|
|
105
|
+
and also an attribute
|
|
106
|
+
|
|
107
|
+
:param backend: Environ|Config
|
|
108
|
+
:param k: str
|
|
109
|
+
:returns: Any
|
|
110
|
+
:raises: KeyError
|
|
111
|
+
"""
|
|
112
|
+
return backend[k]
|
|
113
|
+
|
|
114
|
+
def get_environ_value(self, k):
|
|
115
|
+
"""Given a key k, attempt to get the value from the environment
|
|
116
|
+
|
|
117
|
+
:param k: str, the key we're looking for
|
|
118
|
+
:returns: Any
|
|
119
|
+
"""
|
|
120
|
+
return self._get_value(self.environ, k)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_config_value(self, k):
|
|
124
|
+
"""Given a key k, attempt to get the value from the config
|
|
125
|
+
|
|
126
|
+
:param k: str, the key we're looking for
|
|
127
|
+
:returns: Any
|
|
128
|
+
"""
|
|
129
|
+
return self._get_value(self.config, k)
|
|
130
|
+
|
|
131
|
+
def get_environ(self, k):
|
|
132
|
+
"""Internal method called from .__getitem__ that checks to see if the
|
|
133
|
+
key k is actually the Environ instance's namespace
|
|
134
|
+
|
|
135
|
+
:param k: str, the key that wasn't found anywhere else
|
|
136
|
+
:returns: Environ
|
|
137
|
+
"""
|
|
138
|
+
if self.environ is not None:
|
|
139
|
+
namespace = getattr(self.environ, "namespace", "")
|
|
140
|
+
if namespace and namespace.lower().startswith(k.lower()):
|
|
141
|
+
return self.environ
|
|
142
|
+
|
|
143
|
+
def get_config(self, k):
|
|
144
|
+
"""Internal method called from .__getitem__ that checks to see if the
|
|
145
|
+
key k is actually the config file's fileroot
|
|
146
|
+
|
|
147
|
+
:param k: str, the key that wasn't found anywhere else
|
|
148
|
+
:returns: Config
|
|
149
|
+
"""
|
|
150
|
+
if self.config is not None:
|
|
151
|
+
path = getattr(self.config, "path", "")
|
|
152
|
+
if path and path.fileroot == k:
|
|
153
|
+
return self.config
|
|
154
|
+
|
|
155
|
+
def __getitem__(self, k):
|
|
156
|
+
# I'd love to use a collections.ChainMap here but because there are
|
|
157
|
+
# customization methods for each type of settings they need to know
|
|
158
|
+
# where the value came from
|
|
159
|
+
try:
|
|
160
|
+
return super().__getitem__(k)
|
|
161
|
+
|
|
162
|
+
except KeyError:
|
|
163
|
+
try:
|
|
164
|
+
return self.get_environ_value(k)
|
|
165
|
+
|
|
166
|
+
except KeyError:
|
|
167
|
+
try:
|
|
168
|
+
return self.get_config_value(k)
|
|
169
|
+
|
|
170
|
+
except KeyError:
|
|
171
|
+
environ = self.get_environ(k)
|
|
172
|
+
if environ is not None:
|
|
173
|
+
return environ
|
|
174
|
+
|
|
175
|
+
else:
|
|
176
|
+
config = self.get_config(k)
|
|
177
|
+
if config is not None:
|
|
178
|
+
return config
|
|
179
|
+
|
|
180
|
+
raise
|
|
181
|
+
|
|
182
|
+
def get(self, k, default=None):
|
|
183
|
+
try:
|
|
184
|
+
return self.__getitem__(k)
|
|
185
|
+
|
|
186
|
+
except KeyError:
|
|
187
|
+
return default
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class MultiSettings(Settings):
|
|
191
|
+
"""Similar to Settings but makes it easier to query multiple configuration
|
|
192
|
+
files or environ prefixes
|
|
193
|
+
|
|
194
|
+
Sometimes there might be multiple of the same value with different
|
|
195
|
+
prefixes (eg FOO_BOO=1, BAR_BOO=2), and self.BOO is first match wins, so
|
|
196
|
+
it depends on order. If you need the sub value you have to give more
|
|
197
|
+
information (eg, s.BAR_BOO will return 2)
|
|
198
|
+
|
|
199
|
+
I have a tendency for different packages to have their own environment
|
|
200
|
+
or settings singleton, then dependent projects will also have a settings
|
|
201
|
+
singleton, so this allows these dependent projects to give their settings
|
|
202
|
+
singleton access to everything
|
|
203
|
+
|
|
204
|
+
:Example:
|
|
205
|
+
from datatypes import MultiSettings
|
|
206
|
+
from <SOME-PACKAGE> import settings as external_settings
|
|
207
|
+
from <SOME-OTHER-PACKAGE> import environ as external_environ
|
|
208
|
+
|
|
209
|
+
class Settings(MultiSettings):
|
|
210
|
+
def __init__(self):
|
|
211
|
+
super().__init__(
|
|
212
|
+
prefixes=["FOO_", "BAR_"],
|
|
213
|
+
settings=[external_settings],
|
|
214
|
+
environs=[external_environ],
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# create the singleton for this project that will have access to
|
|
218
|
+
# all the external configuration also
|
|
219
|
+
settings = Settings()
|
|
220
|
+
"""
|
|
221
|
+
def __init__(self, data=None, **kwargs):
|
|
222
|
+
"""
|
|
223
|
+
:param data: dict, the local data
|
|
224
|
+
:param **kwargs:
|
|
225
|
+
* prefix: str, the environment prefix, this is merged with
|
|
226
|
+
prefixes but this value takes precedence
|
|
227
|
+
* prefixes: list[str], the environment prefixes
|
|
228
|
+
* configs: list[str], the configuration files
|
|
229
|
+
* settings: list[Settings], any settings to add to this instance
|
|
230
|
+
* environs: list[Environ], any environs to add to this instance
|
|
231
|
+
* configs: list[Config], any configuration to add to this instance
|
|
232
|
+
"""
|
|
233
|
+
super().__init__(data, **kwargs)
|
|
234
|
+
|
|
235
|
+
self.__dict__["settings"] = self.get_init_settings(**kwargs)
|
|
236
|
+
|
|
237
|
+
def get_init_environ(self, environ, **kwargs):
|
|
238
|
+
prefixes = []
|
|
239
|
+
|
|
240
|
+
if environ is not None:
|
|
241
|
+
prefixes.append(environ)
|
|
242
|
+
|
|
243
|
+
if environs := kwargs.get("environs", []):
|
|
244
|
+
prefixes.extend(environs)
|
|
245
|
+
|
|
246
|
+
if p := kwargs.get("prefix", ""):
|
|
247
|
+
prefixes.append(p)
|
|
248
|
+
|
|
249
|
+
if ps := kwargs.get("prefixes", []):
|
|
250
|
+
prefixes.extend(ps)
|
|
251
|
+
|
|
252
|
+
maps = []
|
|
253
|
+
for prefix in prefixes:
|
|
254
|
+
if isinstance(prefix, Environ):
|
|
255
|
+
maps.append(super().get_init_environ(prefix))
|
|
256
|
+
|
|
257
|
+
else:
|
|
258
|
+
maps.append(super().get_init_environ(None, prefix=prefix))
|
|
259
|
+
|
|
260
|
+
return ChainMap(*maps)
|
|
261
|
+
|
|
262
|
+
def get_init_config(self, config, **kwargs):
|
|
263
|
+
configs = []
|
|
264
|
+
|
|
265
|
+
if config:
|
|
266
|
+
configs.append(config)
|
|
267
|
+
|
|
268
|
+
if cs := kwargs.get("configs", []):
|
|
269
|
+
configs.extend(cs)
|
|
270
|
+
|
|
271
|
+
maps = []
|
|
272
|
+
for c in configs:
|
|
273
|
+
maps.append(super().get_init_config(c))
|
|
274
|
+
|
|
275
|
+
return ChainMap(*maps)
|
|
276
|
+
|
|
277
|
+
def get_init_settings(self, **kwargs):
|
|
278
|
+
maps = kwargs.get("settings", [])
|
|
279
|
+
return ChainMap(*maps)
|
|
280
|
+
|
|
281
|
+
def add_environ(self, environ):
|
|
282
|
+
"""Add an Environ instance to the environs pool
|
|
283
|
+
|
|
284
|
+
:param environ: Environ, the instance to add to the end of the pool
|
|
285
|
+
"""
|
|
286
|
+
self.__dict__["environ"] = ChainMap(*self.environ.maps, environ)
|
|
287
|
+
|
|
288
|
+
def add_config(self, config):
|
|
289
|
+
"""Add a Config instance to the end of the configs pool
|
|
290
|
+
|
|
291
|
+
:param config: Config
|
|
292
|
+
"""
|
|
293
|
+
self.__dict__["config"] = ChainMap(*self.config.maps, config)
|
|
294
|
+
|
|
295
|
+
def add_settings(self, settings):
|
|
296
|
+
"""Add a Settings instance to the end of the settings pool
|
|
297
|
+
|
|
298
|
+
:param settings: Settings
|
|
299
|
+
"""
|
|
300
|
+
self.__dict__["settings"] = ChainMap(*self.settings.maps, settings)
|
|
301
|
+
|
|
302
|
+
def get_environ(self, k):
|
|
303
|
+
k = k.lower()
|
|
304
|
+
for environ in self.environ.maps:
|
|
305
|
+
namespace = getattr(environ, "namespace", "")
|
|
306
|
+
if namespace and namespace.lower().startswith(k):
|
|
307
|
+
return environ
|
|
308
|
+
|
|
309
|
+
def get_config(self, k):
|
|
310
|
+
for config in self.config.maps:
|
|
311
|
+
path = getattr(config, "path", "")
|
|
312
|
+
if path and path.fileroot == k:
|
|
313
|
+
return config
|
|
314
|
+
|
|
315
|
+
def _get_value(self, chainmap, k):
|
|
316
|
+
"""Internal method. Used to check the chainmap for an item value
|
|
317
|
+
and also an attribute"""
|
|
318
|
+
try:
|
|
319
|
+
return super()._get_value(chainmap, k)
|
|
320
|
+
|
|
321
|
+
except KeyError:
|
|
322
|
+
for m in chainmap.maps:
|
|
323
|
+
try:
|
|
324
|
+
return getattr(m, k)
|
|
325
|
+
|
|
326
|
+
except AttributeError:
|
|
327
|
+
pass
|
|
328
|
+
|
|
329
|
+
raise
|
|
330
|
+
|
|
331
|
+
def get_settings_value(self, k):
|
|
332
|
+
"""Given a key k, attempt to get the value from any sub settings
|
|
333
|
+
instances
|
|
334
|
+
|
|
335
|
+
:param k: str, the key we're looking for
|
|
336
|
+
:returns: Any
|
|
337
|
+
"""
|
|
338
|
+
return self._get_value(self.settings, k)
|
|
339
|
+
|
|
340
|
+
def __getitem__(self, k):
|
|
341
|
+
try:
|
|
342
|
+
return super().__getitem__(k)
|
|
343
|
+
|
|
344
|
+
except KeyError:
|
|
345
|
+
try:
|
|
346
|
+
return self.get_settings_value(k)
|
|
347
|
+
|
|
348
|
+
except KeyError:
|
|
349
|
+
pass
|
|
350
|
+
|
|
351
|
+
raise
|
|
352
|
+
|
datatypes/copy.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from __future__ import unicode_literals, division, print_function, absolute_import
|
|
3
|
+
import copy
|
|
4
|
+
from io import IOBase
|
|
5
|
+
|
|
6
|
+
from .compat import *
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Deepcopy(object):
|
|
10
|
+
"""deep copy an object trying to anticipate and handle any errors
|
|
11
|
+
|
|
12
|
+
The original version of this class comes from endpoints.utils and is used in
|
|
13
|
+
the http.Http.__deepcopy__() method.
|
|
14
|
+
|
|
15
|
+
You would traditionally use this in a class by adding these two methods:
|
|
16
|
+
|
|
17
|
+
def copy(self):
|
|
18
|
+
# handy wrapper around the deepcopy stuff
|
|
19
|
+
return copy.deepcopy(self)
|
|
20
|
+
|
|
21
|
+
def __deepcopy__(self, memodict=None):
|
|
22
|
+
dc = Deepcopy()
|
|
23
|
+
return dc.copy(self, memodict)
|
|
24
|
+
|
|
25
|
+
This follows the rules of pickle for creating a new instance, so you can use
|
|
26
|
+
methods like __getnewargs_ex__ to get the arguments for creating a new instance
|
|
27
|
+
and __getstate__() and __setstate__() to handle migrating data from the old instance
|
|
28
|
+
to the new instance
|
|
29
|
+
|
|
30
|
+
https://docs.python.org/3/library/copy.html
|
|
31
|
+
"""
|
|
32
|
+
def __init__(self, **kwargs):
|
|
33
|
+
"""
|
|
34
|
+
:param **kwargs:
|
|
35
|
+
ignore_keys: set, if you don't want to include certain keys in the copy
|
|
36
|
+
then you can pass them in this named argument
|
|
37
|
+
ignore_private: boolean, if you don't want to copy any properties that
|
|
38
|
+
begin with an underscore (eg, _foo) then set this to True
|
|
39
|
+
memodict: the memodict used in deepcopy
|
|
40
|
+
"""
|
|
41
|
+
self.ignore_keys = set(kwargs.pop("ignore_keys", []))
|
|
42
|
+
self.memodict = kwargs.pop("memodict", kwargs.pop("memo", {}))
|
|
43
|
+
self.ignore_private = kwargs.pop("ignore_private", False)
|
|
44
|
+
|
|
45
|
+
def copy(self, val, memodict=None, **kwargs):
|
|
46
|
+
if memodict:
|
|
47
|
+
memo = dict(self.memodict)
|
|
48
|
+
memo.update(memodict or {})
|
|
49
|
+
else:
|
|
50
|
+
memo = self.memodict
|
|
51
|
+
|
|
52
|
+
ignore_keys = kwargs.get("ignore_keys", self.ignore_keys)
|
|
53
|
+
ignore_private = kwargs.get("ignore_keys", self.ignore_private)
|
|
54
|
+
|
|
55
|
+
if hasattr(val, "__dict__"):
|
|
56
|
+
instance = self.create_instance(val)
|
|
57
|
+
d = self.get_state(val)
|
|
58
|
+
|
|
59
|
+
instance_d = {}
|
|
60
|
+
for k in d.keys():
|
|
61
|
+
if k in ignore_keys:
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
if ignore_private and k.startswith("_"):
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
if k in memo:
|
|
68
|
+
instance_d[k] = memo[k]
|
|
69
|
+
|
|
70
|
+
else:
|
|
71
|
+
instance_d[k] = self._copy(d[k], memo)
|
|
72
|
+
|
|
73
|
+
self.set_state(instance, instance_d)
|
|
74
|
+
|
|
75
|
+
else:
|
|
76
|
+
instance = self._copy(val, memo)
|
|
77
|
+
|
|
78
|
+
return instance
|
|
79
|
+
|
|
80
|
+
def _copy(cls, val, memodict):
|
|
81
|
+
ret = val
|
|
82
|
+
# try a deepcopy
|
|
83
|
+
try:
|
|
84
|
+
ret = copy.deepcopy(val, memodict)
|
|
85
|
+
|
|
86
|
+
except (AttributeError, TypeError) as e:
|
|
87
|
+
# if the deepcopy failed a shallow copy is better than nothing
|
|
88
|
+
try:
|
|
89
|
+
ret = copy.copy(val)
|
|
90
|
+
|
|
91
|
+
except (TypeError, RuntimeError):
|
|
92
|
+
# we can do nothing so just pass it straight through
|
|
93
|
+
# RuntimeError is because py3 can throw a RecursionError, py2
|
|
94
|
+
# will throw a TypeError
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
return ret
|
|
98
|
+
|
|
99
|
+
def create_instance(self, val):
|
|
100
|
+
"""If the passed in .copy val is a class, this will create a new instance
|
|
101
|
+
of val using the pickle rules:
|
|
102
|
+
|
|
103
|
+
py3: https://docs.python.org/3/library/pickle.html#module-pickle
|
|
104
|
+
py2: https://docs.python.org/2/library/pickle.html#module-pickle
|
|
105
|
+
|
|
106
|
+
This will try for py3 methods first and then fall back to py2 rules
|
|
107
|
+
"""
|
|
108
|
+
instance_class = self.get_instance_type(val)
|
|
109
|
+
args, kwargs = self.get_instance_args(val)
|
|
110
|
+
return instance_class(*args, **kwargs)
|
|
111
|
+
|
|
112
|
+
def get_instance_type(self, val):
|
|
113
|
+
"""get the class/type of val"""
|
|
114
|
+
return type(val)
|
|
115
|
+
|
|
116
|
+
def get_instance_args(self, val):
|
|
117
|
+
"""get the arguments that val would need to be instantiated again"""
|
|
118
|
+
args = ()
|
|
119
|
+
kwargs = {}
|
|
120
|
+
|
|
121
|
+
if hasattr(val, "__getnewargs_ex__"):
|
|
122
|
+
args, kwargs = val.__getnewargs_ex__()
|
|
123
|
+
|
|
124
|
+
elif hasattr(val, "__getnewargs__"):
|
|
125
|
+
args = val.__getnewargs__()
|
|
126
|
+
|
|
127
|
+
elif hasattr(val, "__getinitargs__"):
|
|
128
|
+
args = val.__getinitargs__()
|
|
129
|
+
|
|
130
|
+
return args, kwargs
|
|
131
|
+
|
|
132
|
+
def set_state(self, val, state):
|
|
133
|
+
"""Populate val with state (the contents of a previous version of val's
|
|
134
|
+
__dict__ property)"""
|
|
135
|
+
if hasattr(val, "__setstate__"):
|
|
136
|
+
val.__setstate__(state)
|
|
137
|
+
|
|
138
|
+
else:
|
|
139
|
+
for k, v in state.items():
|
|
140
|
+
setattr(val, k, v)
|
|
141
|
+
|
|
142
|
+
def get_state(self, val):
|
|
143
|
+
"""Get the state (the contents of val.__dict__) of val as a dict"""
|
|
144
|
+
if hasattr(val, "__getstate__"):
|
|
145
|
+
d = val.__getstate__()
|
|
146
|
+
|
|
147
|
+
else:
|
|
148
|
+
d = val.__dict__
|
|
149
|
+
|
|
150
|
+
return d
|
|
151
|
+
|