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,173 @@
1
+ # -*- coding: utf-8 -*-
2
+ import string
3
+
4
+ from ...token.base import Tokenizer, Token, Scanner
5
+
6
+
7
+ class EnvironToken(Token):
8
+ """A parsed environ variable name and value
9
+
10
+ Fields each instance should have:
11
+
12
+ * `.name` - the variable name
13
+ * `.value` - the variable value
14
+ * `.start` - the offset this token's name started
15
+ * `.stop` - the offset this token's value stopped
16
+ """
17
+ def __init__(self, tokenizer, name, value, start, stop):
18
+ super().__init__(tokenizer, start, stop)
19
+
20
+ self.name = name
21
+ self.value = value
22
+
23
+
24
+ class EnvironTokenizer(Tokenizer):
25
+ """Parses an environment file buffer and returns the key/value pairs as
26
+ EnvironToken instances
27
+
28
+ An environment file is a file that contains NAME=VALUE pairs. It ignores
29
+ all comments starting with the number sign (#)
30
+
31
+ .. Example:
32
+ with open("<ENVIRON-FILE-PATH>") as fp:
33
+ ts = EnvironTokenizer(fp, os.environ)
34
+ for t in ts:
35
+ print(t.name, t.value)
36
+ """
37
+ token_class = EnvironToken
38
+
39
+ def __init__(self, buffer, environ=None):
40
+ """
41
+ :param buffer: io.IOBase|str
42
+ :param environ: Mapping
43
+ """
44
+ super().__init__(Scanner(buffer))
45
+ self.environ = environ
46
+
47
+ def next(self):
48
+ """Get the next environment variable
49
+
50
+ :returns: EnvironToken
51
+ """
52
+ token = None
53
+ name = ""
54
+ value = ""
55
+
56
+ buffer = self.buffer
57
+ alphanum = string.ascii_letters + string.digits + "_"
58
+
59
+ # get the variable name
60
+ while True:
61
+ start = buffer.tell()
62
+ ch = buffer.read(1)
63
+
64
+ if ch == "":
65
+ # we've exhausted the buffer
66
+ break
67
+
68
+ elif ch == "#":
69
+ # read to the end of the line and discard
70
+ buffer.readline()
71
+
72
+ elif ch in string.whitespace:
73
+ buffer.read_thru(whitespace=True)
74
+
75
+ elif ch in string.ascii_letters:
76
+ name = ch + buffer.read_thru(chars=alphanum)
77
+
78
+ # ignore export statements
79
+ if name == "export":
80
+ buffer.read_thru(whitespace=True)
81
+ name = ""
82
+
83
+ else:
84
+ break
85
+
86
+ else:
87
+ raise ValueError(
88
+ f"Not sure what to do with char [{ch}]"
89
+ " in environ variable name"
90
+ )
91
+
92
+ if name:
93
+ equal_sign = buffer.read(1)
94
+ if equal_sign != "=":
95
+ raise ValueError(
96
+ f"Equal sign [=] was expected but got [{equal_sign}]"
97
+ )
98
+
99
+ sentinel = ""
100
+ multiline = False
101
+
102
+ while True:
103
+ ch = buffer.read(1)
104
+
105
+ if ch == "":
106
+ # we've exhausted the buffer
107
+ break
108
+
109
+ elif ch == "\\":
110
+ multiline = True
111
+ ch = buffer.read(1)
112
+
113
+ if ch in string.whitespace:
114
+ buffer.read_thru(whitespace=True)
115
+ if value.endswith(" "):
116
+ ch = ""
117
+
118
+ else:
119
+ ch = " "
120
+
121
+ value += ch
122
+
123
+ elif ch == "\"" or ch == "'":
124
+ if sentinel == ch:
125
+ sentinel = ""
126
+
127
+ else:
128
+ sentinel = ch
129
+
130
+ elif ch == "$":
131
+ ch = buffer.read(1)
132
+ if ch == "{":
133
+ variable_name = buffer.read_to(delim="}")
134
+ buffer.read_thru(delim="}")
135
+
136
+ else:
137
+ variable_name = ch + buffer.read_thru(chars=alphanum)
138
+
139
+ if self.environ:
140
+ value += self.environ.get(variable_name, "")
141
+
142
+ elif ch == "#":
143
+ if sentinel:
144
+ value += ch
145
+
146
+ else:
147
+ # ignore the rest of the line since it's a comment
148
+ buffer.readline()
149
+
150
+ if multiline:
151
+ buffer.read_thru(whitespace=True)
152
+ multiline = False
153
+
154
+ else:
155
+ break
156
+
157
+ elif ch == "\n":
158
+ # we are done grabbing the value
159
+ break
160
+
161
+ else:
162
+ value += ch
163
+
164
+ token = self.token_class(
165
+ self,
166
+ name,
167
+ value,
168
+ start,
169
+ buffer.tell()
170
+ )
171
+
172
+ return token
173
+
@@ -0,0 +1,256 @@
1
+ # -*- coding: utf-8 -*-
2
+ from configparser import ConfigParser, ExtendedInterpolation
3
+ import re
4
+
5
+ from ..compat import *
6
+
7
+
8
+ class ConfigInterpolation(ExtendedInterpolation):
9
+ """Internal class used by Config
10
+
11
+ https://github.com/python/cpython/blob/3.11/Lib/configparser.py#L361
12
+ """
13
+ def before_set_dict(self, parser, section, option, value):
14
+ if parser.path.endswith(".toml"):
15
+ values = []
16
+ for key, val in value.items():
17
+ val = self.before_set_value(parser, section, key, val)
18
+ values.append(f"{key} = {val}")
19
+
20
+ return "{" + ", ".join(values) + "}"
21
+
22
+ else:
23
+ return value
24
+
25
+ def before_get_dict(self, parser, section, option, value, defaults):
26
+ if parser.path.endswith(".toml"):
27
+ d = {}
28
+ regex = r"([a-zA-Z0-9_-]+)\s*=\s*([\"\[\{])"
29
+ for m in re.finditer(regex, value):
30
+ key = m.group(1)
31
+ sentinel = value[m.regs[2][0]:m.regs[2][1]]
32
+ start = m.regs[2][0]
33
+ stop = start + 1
34
+ while value[stop] != sentinel:
35
+ if value[stop] == "\\":
36
+ # skip escaped characters
37
+ stop += 1
38
+ stop += 1
39
+ stop += 1
40
+
41
+ d[key] = self.before_get(
42
+ parser,
43
+ section,
44
+ key,
45
+ value[start:stop],
46
+ defaults
47
+ )
48
+
49
+ return d
50
+
51
+ else:
52
+ return value
53
+
54
+ def before_get_str(self, parser, section, option, value, defaults):
55
+ if parser.path.endswith(".toml"):
56
+ return value.strip("\"")
57
+
58
+ else:
59
+ return value
60
+
61
+ def before_set_str(self, parser, section, option, value):
62
+ if parser.path.endswith(".toml"):
63
+ return f"\"{value}\""
64
+
65
+ else:
66
+ return value
67
+
68
+ def before_set_list(self, parser, section, option, value):
69
+ if parser.path.endswith(".toml"):
70
+ values = []
71
+ for val in value:
72
+ val = self.before_set_value(parser, section, option, val)
73
+ values.append(f" {val}")
74
+
75
+ return "[\n" + ",\n".join(values) + "\n]"
76
+
77
+ else:
78
+ return value
79
+
80
+ def before_get_list(self, parser, section, option, value, defaults):
81
+ if parser.path.endswith(".toml"):
82
+ l = []
83
+ cv = value.strip("[]").strip()
84
+ while cv:
85
+ sentinel = cv[0]
86
+ start = 0
87
+ stop = start + 1
88
+ while cv[stop] != sentinel:
89
+ if cv[stop] == "\\":
90
+ # skip escaped characters
91
+ stop += 1
92
+ stop += 1
93
+ stop += 1
94
+
95
+ l.append(self.before_get(
96
+ parser,
97
+ section,
98
+ option,
99
+ cv[start:stop],
100
+ defaults
101
+ ))
102
+
103
+ cv = cv[stop:].strip(",").strip()
104
+
105
+ return l
106
+
107
+ else:
108
+ return value
109
+
110
+ def before_set_value(self, parser, section, option, value):
111
+ if value is not None:
112
+ if isinstance(value, Mapping):
113
+ value = self.before_set_dict(
114
+ parser,
115
+ section,
116
+ option,
117
+ value
118
+ )
119
+
120
+ elif isinstance(value, str):
121
+ value = self.before_set_str(
122
+ parser,
123
+ section,
124
+ option,
125
+ value
126
+ )
127
+
128
+ elif isinstance(value, Sequence):
129
+ value = self.before_set_list(
130
+ parser,
131
+ section,
132
+ option,
133
+ value
134
+ )
135
+
136
+ else:
137
+ value = str(value)
138
+
139
+ return value
140
+
141
+ def optionxform(self, parser, optionstr):
142
+ if parser.path.endswith(".toml"):
143
+ return optionstr
144
+
145
+ else:
146
+ return optionstr.lower()
147
+
148
+ def before_write(self, parser, section, option, value):
149
+ return super().before_write(parser, section, option, value)
150
+
151
+ def before_get(self, parser, section, option, value, defaults):
152
+ if value.startswith("{"):
153
+ return self.before_get_dict(
154
+ parser,
155
+ section,
156
+ option,
157
+ value,
158
+ defaults
159
+ )
160
+
161
+ elif value.startswith("["):
162
+ return self.before_get_list(
163
+ parser,
164
+ section,
165
+ option,
166
+ value,
167
+ defaults
168
+ )
169
+
170
+ elif value.startswith("\""):
171
+ return value.strip("\"")
172
+
173
+ else:
174
+ return super().before_get(parser, section, option, value, defaults)
175
+
176
+ def before_set(self, parser, section, option, value):
177
+ return super().before_set(parser, section, option, value)
178
+
179
+ def before_read(self, parser, section, option, value):
180
+ return super().before_read(parser, section, option, value)
181
+
182
+
183
+ class Config(ConfigParser):
184
+ """Parse a configuration file
185
+
186
+ :Example:
187
+ c = Config("<PATH-TO-CONFIG-FILE>")
188
+ c["<SECTION-NAME>"]["<VALUE>"]
189
+
190
+ https://docs.python.org/3/library/configparser.html
191
+ """
192
+ def __init__(self, path, **kwargs):
193
+ from ..path import Filepath # avoid circular dependency
194
+ self.path = Filepath(path)
195
+ super().__init__(
196
+ interpolation=ConfigInterpolation()
197
+ )
198
+
199
+ if self.path.is_file():
200
+ self.read(path)
201
+
202
+ def optionxform(self, optionstr):
203
+ """Overrides parent to allow customization of the option value"""
204
+ return self._interpolation.optionxform(self, optionstr)
205
+
206
+ def read_dict(self, dictionary, source='<dict>'):
207
+ """Overrides parent to allow customization of the raw value
208
+
209
+ I had to copy this whole method body just to change one line because it
210
+ just casts value to str instead of allowing the value to be manipulated
211
+ in ._interpolation.before_set(). Sigh
212
+ """
213
+ elements_added = set()
214
+ for section, keys in dictionary.items():
215
+ section = str(section)
216
+ try:
217
+ self.add_section(section)
218
+
219
+ except (DuplicateSectionError, ValueError):
220
+ if self._strict and section in elements_added:
221
+ raise
222
+
223
+ elements_added.add(section)
224
+ for key, value in keys.items():
225
+ key = self.optionxform(str(key))
226
+
227
+ # ADDED BY JAY
228
+ value = self._interpolation.before_set_value(
229
+ self,
230
+ section,
231
+ key,
232
+ value
233
+ )
234
+
235
+ if self._strict and (section, key) in elements_added:
236
+ raise DuplicateOptionError(section, key, source)
237
+
238
+ elements_added.add((section, key))
239
+ self.set(section, key, value)
240
+
241
+ def write(self):
242
+ with self.path.open("w") as fp:
243
+ super().write(fp)
244
+
245
+ def _write_section(self, fp, section_name, section_items, delimiter):
246
+ """Overrides parent to ignore empty sections"""
247
+ if section_items:
248
+ super()._write_section(fp, section_name, section_items, delimiter)
249
+
250
+ def jsonable(self):
251
+ d = {}
252
+ for k in self:
253
+ d[k] = dict(self[k])
254
+
255
+ return d
256
+