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
datatypes/compat.py ADDED
@@ -0,0 +1,206 @@
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import unicode_literals, division, print_function, absolute_import
3
+ import sys
4
+
5
+ try:
6
+ import cPickle as pickle
7
+ except ImportError:
8
+ import pickle
9
+
10
+ # shamelessly ripped from https://github.com/kennethreitz/requests/blob/master/requests/compat.py
11
+ # Syntax sugar.
12
+ _ver = sys.version_info
13
+ is_py2 = _ver[0] == 2
14
+ is_py3 = _ver[0] == 3
15
+
16
+ if is_py2:
17
+ basestring = basestring
18
+ unicode = unicode
19
+ range = xrange # range is now always an iterator
20
+ input = raw_input
21
+ cmp = cmp
22
+
23
+ import Queue as queue
24
+
25
+ # using _* makes __all__ not export this automatically, so you have to
26
+ # explicitly import this (from datatypes.compat import _thread), the reason
27
+ # is because the thread module is lowkey deprecated in favor of the
28
+ # threading module
29
+ #
30
+ # https://stackoverflow.com/questions/1141047/why-was-the-thread-module-renamed-to-thread-in-python-3-x
31
+ #
32
+ # I've chosen to keep the _thread syntax to prod me into finding a better
33
+ # way to do something, but I still have access to it explicitely if I need it
34
+ import thread as _thread
35
+
36
+ try:
37
+ from cStringIO import StringIO
38
+ except ImportError:
39
+ from StringIO import StringIO
40
+
41
+ from SimpleHTTPServer import SimpleHTTPRequestHandler
42
+ from BaseHTTPServer import (
43
+ BaseHTTPRequestHandler,
44
+ HTTPServer,
45
+ )
46
+ import SocketServer as socketserver
47
+ import Cookie as cookies
48
+ import __builtin__ as builtins
49
+
50
+ from urllib import urlencode
51
+ from urllib2 import (
52
+ Request,
53
+ urlopen,
54
+ URLError,
55
+ HTTPError,
56
+ )
57
+ import urlparse as parse
58
+
59
+ import itertools
60
+ zip = itertools.izip
61
+ zip_longest = itertools.izip_longest
62
+
63
+ from collections import (
64
+ Iterable,
65
+ Mapping,
66
+ Callable,
67
+ MutableSequence,
68
+ Sequence,
69
+ Collection,
70
+ )
71
+
72
+
73
+ from HTMLParser import HTMLParser
74
+ #unescape = HTMLParser().unescape
75
+
76
+ import cgi
77
+ class html(object):
78
+ @classmethod
79
+ def escape(cls, *args, **kwargs):
80
+ return cgi.escape(*args, **kwargs)
81
+
82
+ @classmethod
83
+ def unescape(cls, *args, **kwargs):
84
+ return HTMLParser().unescape(*args, **kwargs)
85
+
86
+ # NOTE -- getargspace isn't a full mapping of getfullargspec
87
+ from inspect import getargspec as getfullargspec
88
+
89
+
90
+ exec("""def reraise(exception_class, e, traceback=None):
91
+ try:
92
+ raise exception_class, e, traceback
93
+ finally:
94
+ traceback = None
95
+ """)
96
+
97
+
98
+ else:
99
+ basestring = (str, bytes)
100
+ unicode = str
101
+ long = int
102
+ input = input
103
+
104
+ import queue
105
+ import _thread
106
+ from io import StringIO
107
+ from http.server import (
108
+ HTTPServer,
109
+ SimpleHTTPRequestHandler,
110
+ BaseHTTPRequestHandler,
111
+ )
112
+ import socketserver
113
+ from http import cookies
114
+ import builtins
115
+
116
+ from functools import reduce # removed from common functions
117
+ from html.parser import HTMLParser
118
+ from urllib import parse
119
+ from urllib.parse import urlencode
120
+ from urllib.request import Request, urlopen
121
+ from urllib.error import URLError, HTTPError
122
+ try:
123
+ import html
124
+ #from html import unescape
125
+ except ImportError:
126
+ import cgi
127
+ class html(object):
128
+ @classmethod
129
+ def escape(cls, *args, **kwargs):
130
+ return cgi.escape(*args, **kwargs)
131
+
132
+ @classmethod
133
+ def unescape(cls, *args, **kwargs):
134
+ return HTMLParser().unescape(*args, **kwargs)
135
+ #unescape = HTMLParser.unescape
136
+
137
+ from inspect import getfullargspec
138
+ from itertools import zip_longest
139
+
140
+ from collections.abc import (
141
+ Iterable,
142
+ Mapping,
143
+ Callable,
144
+ MutableSequence,
145
+ Sequence,
146
+ Collection,
147
+ )
148
+
149
+
150
+ # ripped from six https://github.com/benjaminp/six
151
+ def reraise(exception_class, e, traceback=None):
152
+ """the 3 params correspond to the return value of sys.exc_info()
153
+
154
+ https://docs.python.org/3/library/sys.html#sys.exc_info
155
+
156
+ :param exception_class: BaseException, the class of the exception to
157
+ reraise
158
+ :param e: BaseException instance, the actual exception instance
159
+ :param traceback: traceback, the stack trace
160
+ """
161
+ try:
162
+ e = exception_class("" if e is None else e)
163
+ if e.__traceback__ is not traceback:
164
+ raise e.with_traceback(traceback)
165
+ raise e
166
+ finally:
167
+ e = None
168
+ traceback = None
169
+
170
+
171
+ # py3 has no cmp function for some strange reason
172
+ # https://codegolf.stackexchange.com/a/49779
173
+ def cmp(a, b):
174
+ return (a > b) - (a < b)
175
+
176
+
177
+ Str = unicode if is_py2 else str
178
+ Bytes = str if is_py2 else bytes
179
+
180
+
181
+ # TODO using reraise
182
+
183
+ # if py_2:
184
+ # #raise error_info[0].__class__, error_info[0], error_info[1][2]
185
+ # reraise(*error_info)
186
+ # #raise error_info[0].__class__, error_info[1], error_info[2]
187
+ #
188
+ # elif py_3:
189
+ # #e, exc_info = error_info
190
+ # #et, ei, tb = exc_info
191
+ #
192
+ # reraise(*error_info)
193
+ # #et, ei, tb = error_info
194
+ # #raise ei.with_traceback(tb)
195
+
196
+
197
+ # if not error_info:
198
+ # exc_info = sys.exc_info()
199
+ # #raise e.__class__, e, exc_info[2]
200
+ # #self.error_info = (e, exc_info)
201
+ # self.error_info = exc_info
202
+ #
203
+ # if error_info:
204
+ #
205
+ # reraise(*error_info)
206
+
@@ -0,0 +1,13 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .environ.base import (
4
+ Environ,
5
+ )
6
+ from .parser import (
7
+ Config,
8
+ )
9
+ from .settings import (
10
+ Settings,
11
+ MultiSettings,
12
+ )
13
+
@@ -0,0 +1,33 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ import tempfile
4
+
5
+ from .base import Environ
6
+
7
+
8
+ ###############################################################################
9
+ # Actual environment configuration that is used throughout the package
10
+ ###############################################################################
11
+ environ = Environ("DATATYPES_")
12
+
13
+ environ.setdefault("ENCODING", "UTF-8")
14
+ """For things that need encoding, this will be the default encoding if nothing
15
+ else is passed in"""
16
+
17
+
18
+ environ.setdefault("ENCODING_ERRORS", "replace")
19
+ """For things that need encoding, this will handle any errors"""
20
+
21
+
22
+ # 2021-1-15 - we place our cache by default into another directory because I
23
+ # have problems running some commands like touch on the base temp directory on
24
+ # MacOS on Python3, no idea why but I couldn't get around it
25
+ environ.setdefault(
26
+ "CACHE_DIR",
27
+ os.path.join(tempfile.gettempdir(), environ.namespace.lower().rstrip("_"))
28
+ )
29
+ """the default caching directory for things that need a cache folder"""
30
+
31
+
32
+ environ.setdefault("USER_AGENT", "")
33
+
@@ -0,0 +1,332 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ from pathlib import Path
4
+
5
+ from ...compat import *
6
+
7
+
8
+ class Environ(Mapping):
9
+ """Create an Environ namespace instance
10
+
11
+ you would usually create this like this:
12
+
13
+ environ = Environ("PREFIX_")
14
+
15
+ Then you can access any environment variables with that prefix from the
16
+ `environ` instance.
17
+
18
+ :Example:
19
+ # in your environment
20
+ export PREFIX_FOOBAR=1
21
+ export PREFIX_CHE="this is the che value"
22
+
23
+ # in your python code
24
+ environ = Environ("PREFIX_")
25
+
26
+ print(environ.FOOBAR) # "1"
27
+ print(environ.CHE) # this is the che value
28
+
29
+ # by default, all environ values are string, but you can set defaults
30
+ # and set the type
31
+ environ.setdefault("BAZ", 1000, type=int)
32
+
33
+ print(environ.BAZ, type(environ.BAZ)) # 1000 <class 'int'>
34
+ """
35
+ @classmethod
36
+ def find_namespace(cls, prefix):
37
+ namespace = ""
38
+ if prefix:
39
+ namespace = prefix.split(".", maxsplit=1)[0].upper()
40
+ if not namespace.endswith("_"):
41
+ namespace += "_"
42
+ return namespace
43
+
44
+ def __init__(self, namespace="", environ=None):
45
+ """
46
+ :param namespace: str, usually __name__ from the calling module but can
47
+ also be "PREFIX_" or something like that
48
+ :param environ: Mapping|None, the environment you want this instance
49
+ to wrap, it defaults to os.environ if a mapping isn't passed in
50
+ """
51
+ self.__dict__["namespace"] = self.find_namespace(namespace)
52
+ self.__dict__["defaults"] = {}
53
+ if environ is None:
54
+ self.__dict__["environ"] = os.environ
55
+
56
+ else:
57
+ self.__dict__["environ"] = environ
58
+
59
+ def setdefault(self, key, value, type=None, override=False):
60
+ """Set a default value to be returned if there isn't a value set in
61
+ the environment
62
+
63
+ NOTE -- this works differently than dict.setdefault because it doesn't
64
+ actually set the value into the environment
65
+
66
+ :param key: str
67
+ :param value: Any
68
+ :param type: type, this will be used to normalize the actual
69
+ environment value if it exists also
70
+ :param override: bool, sometimes when setting default configuration
71
+ you might want to change the default value, this allows you to
72
+ do that. If True then any previously set default value will be
73
+ overridden
74
+ """
75
+ ek = self.ekey(key)
76
+ if ek not in self.defaults or override:
77
+ self.defaults[ek] = {
78
+ "value": value,
79
+ "type": type,
80
+ }
81
+
82
+ def set(self, key, value):
83
+ self.__setattr__(key, value)
84
+ #os.environ[self.key(key)] = value
85
+
86
+ def __setattr__(self, key, value):
87
+ self.__setitem__(key, value)
88
+
89
+ def __setitem__(self, key, value):
90
+ self.environ[self.key(key)] = value
91
+
92
+ def nset(self, key, values):
93
+ """Given a list of values, this will set key_* where * is
94
+ 1 -> len(values)
95
+
96
+ :param key: string, the key that will be added to the environment as
97
+ key_*
98
+ :param values: list, a list of values
99
+ """
100
+ self.ndelete(key)
101
+
102
+ for i, v in enumerate(values, 1):
103
+ k = "{}_{}".format(key, i)
104
+ self.set(k, v)
105
+
106
+ def delete(self, key):
107
+ """remove key from the environment"""
108
+ self.__delattr__(key)
109
+ #os.environ.pop(self.key(key), None)
110
+
111
+ def __delitem__(self, key):
112
+ #os.environ.pop(self.key(key), None)
113
+ self.environ.pop(self.key(key), None)
114
+
115
+ def ndelete(self, key):
116
+ """remove all key_* from the environment"""
117
+ for k in self.nkeys(key):
118
+ #os.environ.pop(k)
119
+ self.environ.pop(k, None)
120
+
121
+ def get(self, key, default=None):
122
+ """get a value for key from the environment
123
+
124
+ :param key: string, this will be normalized using the key() method
125
+ :returns: mixed, the value in the environment of key, or default if
126
+ key is not in the environment
127
+ """
128
+ try:
129
+ return self[key]
130
+
131
+ except KeyError:
132
+ return default
133
+ #return os.environ.get(self.key(key), default)
134
+
135
+ def pop(self, key, *defaults):
136
+ try:
137
+ return self.__getitem__(key)
138
+
139
+ except KeyError:
140
+ if defaults:
141
+ return defaults[0]
142
+
143
+ else:
144
+ raise
145
+
146
+ def __getitem__(self, key):
147
+ ek = self.ekey(key)
148
+ k = self.key(key)
149
+ try:
150
+ #return os.environ[k]
151
+ v = self.environ[k]
152
+
153
+ except KeyError:
154
+ v = self.defaults[ek]["value"]
155
+
156
+ if ek in self.defaults:
157
+ if self.defaults[ek]["type"]:
158
+ v = self.defaults[ek]["type"](v)
159
+
160
+ return v
161
+
162
+ def __getattr__(self, key):
163
+ try:
164
+ return self.__getitem__(key)
165
+
166
+ except KeyError as e:
167
+ raise AttributeError(key) from e
168
+
169
+ def nget(self, key):
170
+ """this will look for key, and key_N (where
171
+ N is 1 to infinity) in the environment
172
+
173
+ The number checks (eg *_1, *_2) go in order, so you can't do *_1, *_3,
174
+ because it will fail on missing *_2 and move on, so make sure your
175
+ number suffixes are in order (eg, 1, 2, 3, ...)
176
+
177
+ :param key: string, the name of the environment variables
178
+ :returns: generator, the found values for key and key_* variants
179
+ """
180
+ for nkey in self.nkeys(key):
181
+ #yield os.environ[nkey]
182
+ yield self.environ[nkey]
183
+
184
+ def keys(self):
185
+ """yields all the keys of the given namespace currently in the
186
+ environment"""
187
+ for k, _ in self.items():
188
+ yield k
189
+
190
+ def values(self):
191
+ """yields all the keys of the given namespace currently in the
192
+ environment"""
193
+ for _, v in self.items():
194
+ yield v
195
+
196
+ def items(self):
197
+ #for k, v in os.environ.items():
198
+ seen = set()
199
+ for k, v in self.environ.items():
200
+ if k.startswith(self.namespace):
201
+ ek = self.ekey(k)
202
+ seen.add(ek)
203
+ yield ek, v
204
+
205
+ for ek, d in self.defaults.items():
206
+ if ek not in seen:
207
+ yield ek, d["value"]
208
+
209
+ def __iter__(self):
210
+ return self.keys()
211
+
212
+ def key(self, key):
213
+ """normalizes key to have the namespace
214
+
215
+ :Example:
216
+ environ = Environ("FOO_")
217
+ k = environ.key("BAR")
218
+ print(k) # FOO_BAR
219
+ """
220
+ if self.namespace and not key.startswith(self.namespace):
221
+ key = self.namespace + key
222
+ return key
223
+
224
+ def ekey(self, key):
225
+ """Given a full namespaced key return the key name without the
226
+ namespace
227
+
228
+ :Example:
229
+ environ = Environ("FOO_")
230
+ k = environ.ekey("FOO_BAR")
231
+ print(k) # BAR
232
+ """
233
+ return key.replace(self.namespace, "")
234
+
235
+ def nkey(self, key, n):
236
+ """internal method. Helper method for nkeys"""
237
+ k = self.key(key)
238
+ for fmt in ["{key}_{n}", "{key}{n}"]:
239
+ nkey = fmt.format(key=k, n=n)
240
+ if nkey in self.environ:
241
+ #if nkey in os.environ:
242
+ return nkey
243
+
244
+ raise KeyError("Environ {} has no {} key".format(key, n))
245
+
246
+ def nkeys(self, key):
247
+ """This returns the actual environment variable names from * -> *_N
248
+
249
+ :param key: str, the name of the environment variables
250
+ :returns: generator[str], the found environment names
251
+ """
252
+ k = self.key(key)
253
+ #if k in os.environ:
254
+ if k in self.environ:
255
+ yield k
256
+
257
+ # now try importing _1 -> _N prefixes
258
+ n = 0
259
+ while True:
260
+ try:
261
+ yield self.nkey(key, n)
262
+
263
+ except KeyError:
264
+ # 0 is a special case, so if it fails we keep going
265
+ if n:
266
+ # we are done because we missed an n value
267
+ break
268
+
269
+ finally:
270
+ n += 1
271
+
272
+ def paths(self, key, sep=None):
273
+ """splits the value at key by the path separator and yields each
274
+ individual path part
275
+
276
+ splits each value using the path separator
277
+
278
+ :param key: str, the key
279
+ :param sep: str, the value to split on, defaults to the OS's underlying
280
+ path separator
281
+ :returns: generator, each part of the value found at key
282
+ """
283
+ for paths in self.nget(key):
284
+ for p in self.split_value(paths, sep=sep):
285
+ yield p
286
+
287
+ def split_value(self, value, sep=None):
288
+ """Split value using sep, sep defaults to the os path separator
289
+
290
+ :param value: str, the value to split
291
+ :param sep: str, the value to split on, defaults to the OS's underlying
292
+ path separator
293
+ :returns: list[str]
294
+ """
295
+ sep = sep or os.pathsep
296
+ return value.split(sep)
297
+
298
+ def has(self, key):
299
+ """Return True if key is in the environment
300
+
301
+ :param k: str
302
+ :returns: bool
303
+ """
304
+ return (
305
+ self.key(key) in self.environ
306
+ or self.ekey(key) in self.defaults
307
+ )
308
+
309
+ def __contains__(self, key):
310
+ return self.has(key)
311
+
312
+ def __len__(self):
313
+ return len(list(self.keys()))
314
+
315
+ def load_path(self, path):
316
+ """Load the environment file found at `path` into this instance
317
+
318
+ :param path: str|Path
319
+ :returns: int, how many variables were loaded from the file
320
+ """
321
+ from .token import EnvironTokenizer # avoid circular dependency
322
+
323
+ count = 0
324
+ path = Path(path)
325
+ with path.open() as fp:
326
+ ts = EnvironTokenizer(fp, self)
327
+ for t in ts:
328
+ self.set(t.name, t.value)
329
+ count += 1
330
+
331
+ return count
332
+