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.
Files changed (53) hide show
  1. datatypes/__init__.py +169 -0
  2. datatypes/collections/__init__.py +25 -0
  3. datatypes/collections/container.py +248 -0
  4. datatypes/collections/mapping.py +1226 -0
  5. datatypes/collections/sequence.py +311 -0
  6. datatypes/command.py +596 -0
  7. datatypes/compat.py +206 -0
  8. datatypes/config/__init__.py +13 -0
  9. datatypes/config/environ/__init__.py +33 -0
  10. datatypes/config/environ/base.py +332 -0
  11. datatypes/config/environ/token.py +173 -0
  12. datatypes/config/parser.py +256 -0
  13. datatypes/config/settings.py +352 -0
  14. datatypes/copy.py +151 -0
  15. datatypes/csv.py +547 -0
  16. datatypes/datetime.py +1243 -0
  17. datatypes/decorators/__init__.py +31 -0
  18. datatypes/decorators/base.py +449 -0
  19. datatypes/decorators/descriptor.py +490 -0
  20. datatypes/decorators/misc.py +152 -0
  21. datatypes/email.py +439 -0
  22. datatypes/enum.py +176 -0
  23. datatypes/event.py +299 -0
  24. datatypes/exception.py +13 -0
  25. datatypes/filetypes/__init__.py +10 -0
  26. datatypes/filetypes/html.py +540 -0
  27. datatypes/filetypes/toml.py +275 -0
  28. datatypes/http.py +1443 -0
  29. datatypes/logging.py +475 -0
  30. datatypes/number.py +586 -0
  31. datatypes/parse.py +362 -0
  32. datatypes/path.py +4358 -0
  33. datatypes/profile.py +246 -0
  34. datatypes/reflection/__init__.py +24 -0
  35. datatypes/reflection/inspect.py +3475 -0
  36. datatypes/reflection/other.py +843 -0
  37. datatypes/reflection/path.py +639 -0
  38. datatypes/server.py +642 -0
  39. datatypes/service.py +212 -0
  40. datatypes/ssh.py +812 -0
  41. datatypes/string.py +1632 -0
  42. datatypes/token/__init__.py +16 -0
  43. datatypes/token/abnf.py +1696 -0
  44. datatypes/token/base.py +921 -0
  45. datatypes/token/word.py +259 -0
  46. datatypes/typing.py +126 -0
  47. datatypes/url.py +988 -0
  48. datatypes/utils.py +179 -0
  49. datatypes-0.31.0.dist-info/METADATA +38 -0
  50. datatypes-0.31.0.dist-info/RECORD +53 -0
  51. datatypes-0.31.0.dist-info/WHEEL +5 -0
  52. datatypes-0.31.0.dist-info/licenses/LICENSE.txt +21 -0
  53. 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
+