plexus-python-pulse-common 1.1.75__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.
@@ -0,0 +1,9 @@
1
+ __all__ = [
2
+ "escape_sql_like",
3
+ ]
4
+
5
+
6
+ def escape_sql_like(s: str | None) -> str:
7
+ if s is None:
8
+ raise ValueError("input string cannot be None")
9
+ return s.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")
@@ -0,0 +1,401 @@
1
+ import dataclasses
2
+ import datetime
3
+ import re
4
+ from collections.abc import Callable
5
+
6
+ import pyparsing as pp
7
+ from plexus.common.utils.dtutils import basic_format, dt_format, dt_parse
8
+ from plexus.common.utils.funcutils import singleton
9
+
10
+ __all__ = [
11
+ "hex_string_pattern",
12
+ "hex_string_parser",
13
+ "snake_case_pattern",
14
+ "snake_case_parser",
15
+ "kebab_case_pattern",
16
+ "kebab_case_parser",
17
+ "dot_case_pattern",
18
+ "dot_case_parser",
19
+ "uuid_pattern",
20
+ "uuid_parser",
21
+ "strict_relpath_pattern",
22
+ "strict_relpath_parser",
23
+ "strict_abspath_pattern",
24
+ "strict_abspath_parser",
25
+ "strict_fragmented_relpath_pattern",
26
+ "strict_fragmented_relpath_parser",
27
+ "strict_fragmented_abspath_pattern",
28
+ "strict_fragmented_abspath_parser",
29
+ "email_address_pattern",
30
+ "email_address_parser",
31
+ "semver_pattern",
32
+ "semver_parser",
33
+ "colon_tag_pattern",
34
+ "colon_tag_parser",
35
+ "slash_tag_pattern",
36
+ "slash_tag_parser",
37
+ "topic_pattern",
38
+ "topic_parser",
39
+ "vin_code_chars",
40
+ "vin_code_pattern",
41
+ "vin_code_parser",
42
+ "to_colon_tag",
43
+ "to_slash_tag",
44
+ "UserName",
45
+ "UserEmail",
46
+ "VehicleName",
47
+ "BagName",
48
+ "parse_user_name",
49
+ "parse_user_email",
50
+ "parse_vehicle_name",
51
+ "parse_bag_name",
52
+ ]
53
+
54
+
55
+ def token_check(cond: Callable[[str], bool]) -> Callable[[pp.ParseResults], bool]:
56
+ def cond_func(results: pp.ParseResults) -> bool:
57
+ token, *_ = results
58
+ return cond(token)
59
+
60
+ return cond_func
61
+
62
+
63
+ def token_reparse(elem: pp.ParserElement, negate: bool = False) -> Callable[[pp.ParseResults], bool]:
64
+ def cond_func(results: pp.ParseResults) -> bool:
65
+ token, *_ = results
66
+ try:
67
+ elem.parse_string(token, parse_all=True)
68
+ return not negate
69
+ except pp.ParseException:
70
+ return negate
71
+
72
+ return cond_func
73
+
74
+
75
+ def make_string_pattern(pattern: re.Pattern[str]) -> re.Pattern[str]:
76
+ return re.compile(rf"^{pattern.pattern}$")
77
+
78
+
79
+ def make_string_parser(element: pp.ParserElement) -> pp.ParserElement:
80
+ return pp.Combine(pp.StringStart() + element + pp.StringEnd())
81
+
82
+
83
+ underscore_token: pp.ParserElement = pp.Char("_")
84
+ hyphen_token: pp.ParserElement = pp.Char("-")
85
+ period_token: pp.ParserElement = pp.Char(".")
86
+ colon_token: pp.ParserElement = pp.Char(":")
87
+ slash_token: pp.ParserElement = pp.Char("/")
88
+ plus_token: pp.ParserElement = pp.Char("+")
89
+ sharp_token: pp.ParserElement = pp.Char("#")
90
+
91
+ lower_regexp: re.Pattern[str] = re.compile(r"[a-z]")
92
+ upper_regexp: re.Pattern[str] = re.compile(r"[A-Z]")
93
+ digit_regexp: re.Pattern[str] = re.compile(r"[0-9]")
94
+ lower_digit_regexp: re.Pattern[str] = re.compile(r"[a-z0-9]")
95
+ upper_digit_regexp: re.Pattern[str] = re.compile(r"[A-Z0-9]")
96
+ alpha_digit_regexp: re.Pattern[str] = re.compile(r"[a-zA-Z0-9]")
97
+ hex_digit_regexp: re.Pattern[str] = re.compile(r"[a-f0-9]")
98
+
99
+ lowers_regexp: re.Pattern[str] = re.compile(rf"{lower_regexp.pattern}+")
100
+ uppers_regexp: re.Pattern[str] = re.compile(rf"{upper_regexp.pattern}+")
101
+ digits_regexp: re.Pattern[str] = re.compile(rf"{digit_regexp.pattern}+")
102
+ lower_digits_regexp: re.Pattern[str] = re.compile(rf"{lower_digit_regexp.pattern}+")
103
+ upper_digits_regexp: re.Pattern[str] = re.compile(rf"{upper_digit_regexp.pattern}+")
104
+ alpha_digits_regexp: re.Pattern[str] = re.compile(rf"{alpha_digit_regexp.pattern}+")
105
+ hex_digits_regexp: re.Pattern[str] = re.compile(rf"{hex_digit_regexp.pattern}+")
106
+ lower_identifier_regexp: re.Pattern[str] = re.compile(rf"{lower_regexp.pattern}{lower_digit_regexp.pattern}*")
107
+ upper_identifier_regexp: re.Pattern[str] = re.compile(rf"{upper_regexp.pattern}{upper_digit_regexp.pattern}*")
108
+ strict_chars_regexp: re.Pattern[str] = re.compile(rf"({alpha_digit_regexp.pattern}|[._-])+")
109
+
110
+ lower_element: pp.ParserElement = pp.Regex(lower_regexp.pattern)
111
+ upper_element: pp.ParserElement = pp.Regex(upper_regexp.pattern)
112
+ digit_element: pp.ParserElement = pp.Regex(digit_regexp.pattern)
113
+ lower_digit_element: pp.ParserElement = pp.Regex(lower_digit_regexp.pattern)
114
+ upper_digit_element: pp.ParserElement = pp.Regex(upper_digit_regexp.pattern)
115
+ alpha_digit_element: pp.ParserElement = pp.Regex(alpha_digit_regexp.pattern)
116
+ hex_digit_element: pp.ParserElement = pp.Regex(hex_digit_regexp.pattern)
117
+
118
+ lowers_element: pp.ParserElement = pp.Regex(lowers_regexp.pattern)
119
+ uppers_element: pp.ParserElement = pp.Regex(uppers_regexp.pattern)
120
+ digits_element: pp.ParserElement = pp.Regex(digits_regexp.pattern)
121
+ lower_digits_element: pp.ParserElement = pp.Regex(lower_digits_regexp.pattern)
122
+ upper_digits_element: pp.ParserElement = pp.Regex(upper_digits_regexp.pattern)
123
+ alpha_digits_element: pp.ParserElement = pp.Regex(alpha_digits_regexp.pattern)
124
+ hex_digits_element: pp.ParserElement = pp.Regex(hex_digits_regexp.pattern)
125
+ lower_identifier_element: pp.ParserElement = pp.Regex(lower_identifier_regexp.pattern)
126
+ upper_identifier_element: pp.ParserElement = pp.Regex(upper_identifier_regexp.pattern)
127
+ strict_chars_element: pp.ParserElement = pp.Regex(strict_chars_regexp.pattern)
128
+
129
+ basic_datetime_regexp: re.Pattern[str] = re.compile(r"\d{8}T\d{6}")
130
+ extended_datetime_regexp: re.Pattern[str] = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}")
131
+
132
+ basic_datetime_element: pp.ParserElement = pp.Regex(basic_datetime_regexp.pattern)
133
+ extended_datetime_element: pp.ParserElement = pp.Regex(extended_datetime_regexp.pattern)
134
+
135
+ positive_number_regexp: re.Pattern[str] = re.compile(rf"[1-9]{digit_regexp.pattern}*")
136
+ number_regexp: re.Pattern[str] = re.compile(rf"0|({positive_number_regexp.pattern})")
137
+
138
+ positive_number_element: pp.ParserElement = pp.Regex(positive_number_regexp.pattern)
139
+ number_element: pp.ParserElement = pp.Regex(number_regexp.pattern)
140
+
141
+ snake_case_regexp: re.Pattern[str] = re.compile(
142
+ rf"{lower_digits_regexp.pattern}(?:_{lower_digits_regexp.pattern})*")
143
+ kebab_case_regexp: re.Pattern[str] = re.compile(
144
+ rf"{lower_digits_regexp.pattern}(?:-{lower_digits_regexp.pattern})*")
145
+ dot_case_regexp: re.Pattern[str] = re.compile(
146
+ rf"{lower_digits_regexp.pattern}(?:\.{lower_digits_regexp.pattern})*")
147
+
148
+ snake_case_element: pp.ParserElement = pp.Combine(
149
+ lower_digits_element + (underscore_token + lower_digits_element)[...])
150
+ kebab_case_element: pp.ParserElement = pp.Combine(
151
+ lower_digits_element + (hyphen_token + lower_digits_element)[...])
152
+ dot_case_element: pp.ParserElement = pp.Combine(
153
+ lower_digits_element + (period_token + lower_digits_element)[...])
154
+
155
+ uuid_regexp: re.Pattern[str] = re.compile(
156
+ rf"{hex_digit_regexp.pattern}{{8}}(-{hex_digit_regexp.pattern}{{4}}){{3}}-{hex_digit_regexp.pattern}{{12}}")
157
+ uuid_element: pp.ParserElement = pp.Regex(uuid_regexp.pattern)
158
+
159
+ strict_relpath_regexp: re.Pattern[str] = re.compile(
160
+ rf"(?!.*(^|/)\.+($|/))(?:{strict_chars_regexp.pattern}/)*(?:{strict_chars_regexp.pattern})?")
161
+ strict_abspath_regexp: re.Pattern[str] = re.compile(
162
+ rf"(?!.*(^|/)\.+($|/))/(?:{strict_chars_regexp.pattern}/)*(?:{strict_chars_regexp.pattern})?")
163
+ strict_fragmented_relpath_regexp: re.Pattern[str] = re.compile(
164
+ rf"(?!.*(^|/)\.+($|/))(?:{strict_chars_regexp.pattern}/)*({strict_chars_regexp.pattern})(?:#({strict_relpath_regexp.pattern}))?")
165
+ strict_fragmented_abspath_regexp: re.Pattern[str] = re.compile(
166
+ rf"(?!.*(^|/)\.+($|/))/(?:{strict_chars_regexp.pattern}/)*({strict_chars_regexp.pattern})(?:#({strict_relpath_regexp.pattern}))?")
167
+
168
+ strict_path_chars_element = strict_chars_element.copy()
169
+ strict_path_chars_element.add_condition(token_reparse(period_token[1, ...], negate=True),
170
+ message="cannot be pure dots")
171
+
172
+ strict_relpath_element: pp.ParserElement = pp.Combine(
173
+ (strict_path_chars_element + slash_token)[...] + strict_path_chars_element[0, 1])
174
+ strict_abspath_element: pp.ParserElement = pp.Combine(
175
+ slash_token + (strict_path_chars_element + slash_token)[...] + strict_path_chars_element[0, 1])
176
+ strict_fragmented_relpath_element: pp.ParserElement = pp.Combine(
177
+ (strict_path_chars_element + slash_token)[...] + strict_path_chars_element +
178
+ (sharp_token + strict_relpath_element)[0, 1])
179
+ strict_fragmented_abspath_element: pp.ParserElement = pp.Combine(
180
+ slash_token + (strict_path_chars_element + slash_token)[...] + strict_path_chars_element +
181
+ (sharp_token + strict_relpath_element)[0, 1])
182
+
183
+ email_address_regexp: re.Pattern[str] = re.compile(
184
+ rf"(({lower_digit_regexp.pattern}|[_-])+)(?:\.({lower_digit_regexp.pattern}|[_-])+)*@(?:{kebab_case_regexp.pattern}\.)+({lower_digit_regexp.pattern}{{2,63}})")
185
+ email_address_element: pp.ParserElement = pp.Regex(email_address_regexp.pattern)
186
+
187
+ semver_regexp: re.Pattern[str] = re.compile(
188
+ rf"({number_regexp.pattern})\.({number_regexp.pattern})\.({number_regexp.pattern})"
189
+ rf"(?:-{alpha_digits_regexp.pattern}(?:\.{alpha_digits_regexp.pattern})*)?"
190
+ rf"(?:\+{alpha_digits_regexp.pattern}(?:\.{alpha_digits_regexp.pattern})*)?")
191
+ semver_element: pp.ParserElement = pp.Regex(semver_regexp.pattern)
192
+
193
+ colon_tag_regexp: re.Pattern[str] = re.compile(rf"{snake_case_regexp.pattern}(?::{snake_case_regexp.pattern})*")
194
+ colon_tag_element: pp.ParserElement = pp.Combine(snake_case_element + (colon_token + snake_case_element)[...])
195
+
196
+ slash_tag_regexp: re.Pattern[str] = re.compile(rf"{snake_case_regexp.pattern}(?:/{snake_case_regexp.pattern})*")
197
+ slash_tag_element: pp.ParserElement = pp.Combine(snake_case_element + (slash_token + snake_case_element)[...])
198
+
199
+ topic_regexp: re.Pattern[str] = re.compile(rf"(?:/{snake_case_regexp.pattern})+")
200
+ topic_element: pp.ParserElement = pp.Combine((slash_token + snake_case_element)[1, ...])
201
+
202
+ vin_code_chars: str = "0123456789ABCDEFGHJKLMNPRSTUVWXYZ"
203
+
204
+ vin_code_regexp: re.Pattern[str] = re.compile(rf"[{vin_code_chars}]{{17}}")
205
+ vin_code_element: pp.ParserElement = pp.Regex(vin_code_regexp.pattern)
206
+
207
+ hex_string_pattern = make_string_pattern(hex_digits_regexp)
208
+ hex_string_parser = make_string_parser(hex_digits_element)
209
+
210
+ snake_case_pattern = make_string_pattern(snake_case_regexp)
211
+ snake_case_parser = make_string_parser(snake_case_element)
212
+ kebab_case_pattern = make_string_pattern(kebab_case_regexp)
213
+ kebab_case_parser = make_string_parser(kebab_case_element)
214
+ dot_case_pattern = make_string_pattern(dot_case_regexp)
215
+ dot_case_parser = make_string_parser(dot_case_element)
216
+
217
+ uuid_pattern = make_string_pattern(uuid_regexp)
218
+ uuid_parser = make_string_parser(uuid_element)
219
+
220
+ strict_relpath_pattern = make_string_pattern(strict_relpath_regexp)
221
+ strict_relpath_parser = make_string_parser(strict_relpath_element)
222
+ strict_abspath_pattern = make_string_pattern(strict_abspath_regexp)
223
+ strict_abspath_parser = make_string_parser(strict_abspath_element)
224
+ strict_fragmented_relpath_pattern = make_string_pattern(strict_fragmented_relpath_regexp)
225
+ strict_fragmented_relpath_parser = make_string_parser(strict_fragmented_relpath_element)
226
+ strict_fragmented_abspath_pattern = make_string_pattern(strict_fragmented_abspath_regexp)
227
+ strict_fragmented_abspath_parser = make_string_parser(strict_fragmented_abspath_element)
228
+
229
+ email_address_pattern = make_string_pattern(email_address_regexp)
230
+ email_address_parser = make_string_parser(email_address_element)
231
+
232
+ semver_pattern = make_string_pattern(semver_regexp)
233
+ semver_parser = make_string_parser(semver_element)
234
+
235
+ colon_tag_pattern = make_string_pattern(colon_tag_regexp)
236
+ colon_tag_parser = make_string_parser(colon_tag_element)
237
+ slash_tag_pattern = make_string_pattern(slash_tag_regexp)
238
+ slash_tag_parser = make_string_parser(slash_tag_element)
239
+ topic_pattern = make_string_pattern(topic_regexp)
240
+ topic_parser = make_string_parser(topic_element)
241
+ vin_code_pattern = make_string_pattern(vin_code_regexp)
242
+ vin_code_parser = make_string_parser(vin_code_element)
243
+
244
+
245
+ def to_colon_tag(tag: str) -> str:
246
+ return tag.replace("/", ":")
247
+
248
+
249
+ def to_slash_tag(tag: str) -> str:
250
+ return tag.replace(":", "/")
251
+
252
+
253
+ @dataclasses.dataclass(frozen=True, eq=True)
254
+ class UserName(object):
255
+ first_name: str
256
+ last_name: str
257
+ sn: int = 0
258
+
259
+ def __str__(self) -> str:
260
+ if self.sn == 0:
261
+ return f"{self.first_name}.{self.last_name}"
262
+ return f"{self.first_name}{self.sn}.{self.last_name}"
263
+
264
+
265
+ @dataclasses.dataclass(frozen=True, eq=True)
266
+ class UserEmail(object):
267
+ user_name: UserName
268
+ domain: str
269
+
270
+ def __str__(self) -> str:
271
+ return f"{str(self.user_name)}@{self.domain}"
272
+
273
+
274
+ @dataclasses.dataclass(frozen=True, eq=True)
275
+ class VehicleName(object):
276
+ brand: str
277
+ alias: str
278
+ code: str | None = None
279
+ vin: str | None = None
280
+
281
+ def __str__(self) -> str:
282
+ if self.code and self.vin:
283
+ return f"{self.brand}_{self.alias}_{self.code}_V{self.vin}"
284
+ if self.code:
285
+ return f"{self.brand}_{self.alias}_{self.code}"
286
+ if self.vin:
287
+ return f"{self.brand}_{self.alias}_V{self.vin}"
288
+ return f"{self.brand}_{self.alias}"
289
+
290
+
291
+ @dataclasses.dataclass(frozen=True, eq=True)
292
+ class BagName(object):
293
+ vehicle_name: VehicleName
294
+ record_dt: datetime.datetime
295
+ record_sn: int
296
+
297
+ def __str__(self) -> str:
298
+ return f"{dt_format(self.record_dt, basic_format())}-{self.vehicle_name}-{self.record_sn}.bag"
299
+
300
+
301
+ @singleton
302
+ def get_user_name_element() -> pp.ParserElement:
303
+ return pp.Combine(lowers_element("first_name") +
304
+ positive_number_element("sn")[0, 1] +
305
+ period_token +
306
+ lowers_element("last_name"))
307
+
308
+
309
+ @singleton
310
+ def get_user_name_parser() -> pp.ParserElement:
311
+ return make_string_parser(get_user_name_element())
312
+
313
+
314
+ @singleton
315
+ def get_user_email_element() -> pp.ParserElement:
316
+ return pp.Combine(get_user_name_element()("user_name") +
317
+ pp.Char("@") +
318
+ pp.Combine((kebab_case_element + period_token)[1, ...] + lowers_element)("domain"))
319
+
320
+
321
+ @singleton
322
+ def get_user_email_parser() -> pp.ParserElement:
323
+ return make_string_parser(get_user_email_element())
324
+
325
+
326
+ @singleton
327
+ def get_vehicle_name_element() -> pp.ParserElement:
328
+ return pp.Combine(lower_identifier_element("brand") +
329
+ (underscore_token +
330
+ pp.Combine(lower_identifier_element +
331
+ (underscore_token + lower_identifier_element)[...])("alias")) +
332
+ (underscore_token + digits_element("code"))[0, 1] +
333
+ (underscore_token + pp.Char("V") + vin_code_element("vin"))[0, 1])
334
+
335
+
336
+ @singleton
337
+ def get_vehicle_name_parser() -> pp.ParserElement:
338
+ return make_string_parser(get_vehicle_name_element())
339
+
340
+
341
+ @singleton
342
+ def get_bag_name_element() -> pp.ParserElement:
343
+ return pp.Combine(basic_datetime_element("record_dt") +
344
+ (hyphen_token +
345
+ get_vehicle_name_element()("vehicle_name")) +
346
+ (hyphen_token + number_element("record_sn")) +
347
+ (period_token + pp.Literal("bag"))[0, 1])
348
+
349
+
350
+ @singleton
351
+ def get_bag_name_parser() -> pp.ParserElement:
352
+ return make_string_parser(get_bag_name_element())
353
+
354
+
355
+ def parse_user_name(s: str) -> UserName | None:
356
+ user_name_match = get_user_name_parser().parse_string(s, parse_all=True)
357
+
358
+ return UserName(
359
+ user_name_match.get("first_name"),
360
+ user_name_match.get("last_name"),
361
+ int(user_name_match.get("sn", 0)),
362
+ )
363
+
364
+
365
+ def parse_user_email(s: str) -> UserEmail | None:
366
+ user_email_match = get_user_email_parser().parse_string(s, parse_all=True)
367
+
368
+ return UserEmail(
369
+ UserName(
370
+ user_email_match.get("user_name").get("first_name"),
371
+ user_email_match.get("user_name").get("last_name"),
372
+ int(user_email_match.get("user_name").get("sn", 0)),
373
+ ),
374
+ user_email_match.get("domain"),
375
+ )
376
+
377
+
378
+ def parse_vehicle_name(s: str) -> VehicleName | None:
379
+ vehicle_name_match = get_vehicle_name_parser().parse_string(s, parse_all=True)
380
+
381
+ return VehicleName(
382
+ vehicle_name_match.get("brand"),
383
+ vehicle_name_match.get("alias"),
384
+ vehicle_name_match.get("code"),
385
+ vehicle_name_match.get("vin"),
386
+ )
387
+
388
+
389
+ def parse_bag_name(s: str) -> BagName | None:
390
+ bag_name_match = get_bag_name_parser().parse_string(s, parse_all=True)
391
+
392
+ return BagName(
393
+ VehicleName(
394
+ bag_name_match.get("vehicle_name").get("brand"),
395
+ bag_name_match.get("vehicle_name").get("alias"),
396
+ bag_name_match.get("vehicle_name").get("code"),
397
+ bag_name_match.get("vehicle_name").get("vin"),
398
+ ),
399
+ dt_parse(bag_name_match.get("record_dt"), basic_format()),
400
+ int(bag_name_match.get("record_sn")),
401
+ )