]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/pip/_vendor/pyparsing/results.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / pip / _vendor / pyparsing / results.py
1 # results.py
2 from collections.abc import (
3 MutableMapping,
4 Mapping,
5 MutableSequence,
6 Iterator,
7 Sequence,
8 Container,
9 )
10 import pprint
11 from typing import Tuple, Any, Dict, Set, List
12
13 str_type: Tuple[type, ...] = (str, bytes)
14 _generator_type = type((_ for _ in ()))
15
16
17 class _ParseResultsWithOffset:
18 tup: Tuple["ParseResults", int]
19 __slots__ = ["tup"]
20
21 def __init__(self, p1: "ParseResults", p2: int):
22 self.tup: Tuple[ParseResults, int] = (p1, p2)
23
24 def __getitem__(self, i):
25 return self.tup[i]
26
27 def __getstate__(self):
28 return self.tup
29
30 def __setstate__(self, *args):
31 self.tup = args[0]
32
33
34 class ParseResults:
35 """Structured parse results, to provide multiple means of access to
36 the parsed data:
37
38 - as a list (``len(results)``)
39 - by list index (``results[0], results[1]``, etc.)
40 - by attribute (``results.<results_name>`` - see :class:`ParserElement.set_results_name`)
41
42 Example::
43
44 integer = Word(nums)
45 date_str = (integer.set_results_name("year") + '/'
46 + integer.set_results_name("month") + '/'
47 + integer.set_results_name("day"))
48 # equivalent form:
49 # date_str = (integer("year") + '/'
50 # + integer("month") + '/'
51 # + integer("day"))
52
53 # parse_string returns a ParseResults object
54 result = date_str.parse_string("1999/12/31")
55
56 def test(s, fn=repr):
57 print(f"{s} -> {fn(eval(s))}")
58 test("list(result)")
59 test("result[0]")
60 test("result['month']")
61 test("result.day")
62 test("'month' in result")
63 test("'minutes' in result")
64 test("result.dump()", str)
65
66 prints::
67
68 list(result) -> ['1999', '/', '12', '/', '31']
69 result[0] -> '1999'
70 result['month'] -> '12'
71 result.day -> '31'
72 'month' in result -> True
73 'minutes' in result -> False
74 result.dump() -> ['1999', '/', '12', '/', '31']
75 - day: '31'
76 - month: '12'
77 - year: '1999'
78 """
79
80 _null_values: Tuple[Any, ...] = (None, [], ())
81
82 _name: str
83 _parent: "ParseResults"
84 _all_names: Set[str]
85 _modal: bool
86 _toklist: List[Any]
87 _tokdict: Dict[str, Any]
88
89 __slots__ = (
90 "_name",
91 "_parent",
92 "_all_names",
93 "_modal",
94 "_toklist",
95 "_tokdict",
96 )
97
98 class List(list):
99 """
100 Simple wrapper class to distinguish parsed list results that should be preserved
101 as actual Python lists, instead of being converted to :class:`ParseResults`::
102
103 LBRACK, RBRACK = map(pp.Suppress, "[]")
104 element = pp.Forward()
105 item = ppc.integer
106 element_list = LBRACK + pp.DelimitedList(element) + RBRACK
107
108 # add parse actions to convert from ParseResults to actual Python collection types
109 def as_python_list(t):
110 return pp.ParseResults.List(t.as_list())
111 element_list.add_parse_action(as_python_list)
112
113 element <<= item | element_list
114
115 element.run_tests('''
116 100
117 [2,3,4]
118 [[2, 1],3,4]
119 [(2, 1),3,4]
120 (2,3,4)
121 ''', post_parse=lambda s, r: (r[0], type(r[0])))
122
123 prints::
124
125 100
126 (100, <class 'int'>)
127
128 [2,3,4]
129 ([2, 3, 4], <class 'list'>)
130
131 [[2, 1],3,4]
132 ([[2, 1], 3, 4], <class 'list'>)
133
134 (Used internally by :class:`Group` when `aslist=True`.)
135 """
136
137 def __new__(cls, contained=None):
138 if contained is None:
139 contained = []
140
141 if not isinstance(contained, list):
142 raise TypeError(
143 f"{cls.__name__} may only be constructed with a list, not {type(contained).__name__}"
144 )
145
146 return list.__new__(cls)
147
148 def __new__(cls, toklist=None, name=None, **kwargs):
149 if isinstance(toklist, ParseResults):
150 return toklist
151 self = object.__new__(cls)
152 self._name = None
153 self._parent = None
154 self._all_names = set()
155
156 if toklist is None:
157 self._toklist = []
158 elif isinstance(toklist, (list, _generator_type)):
159 self._toklist = (
160 [toklist[:]]
161 if isinstance(toklist, ParseResults.List)
162 else list(toklist)
163 )
164 else:
165 self._toklist = [toklist]
166 self._tokdict = dict()
167 return self
168
169 # Performance tuning: we construct a *lot* of these, so keep this
170 # constructor as small and fast as possible
171 def __init__(
172 self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance
173 ):
174 self._tokdict: Dict[str, _ParseResultsWithOffset]
175 self._modal = modal
176 if name is not None and name != "":
177 if isinstance(name, int):
178 name = str(name)
179 if not modal:
180 self._all_names = {name}
181 self._name = name
182 if toklist not in self._null_values:
183 if isinstance(toklist, (str_type, type)):
184 toklist = [toklist]
185 if asList:
186 if isinstance(toklist, ParseResults):
187 self[name] = _ParseResultsWithOffset(
188 ParseResults(toklist._toklist), 0
189 )
190 else:
191 self[name] = _ParseResultsWithOffset(
192 ParseResults(toklist[0]), 0
193 )
194 self[name]._name = name
195 else:
196 try:
197 self[name] = toklist[0]
198 except (KeyError, TypeError, IndexError):
199 if toklist is not self:
200 self[name] = toklist
201 else:
202 self._name = name
203
204 def __getitem__(self, i):
205 if isinstance(i, (int, slice)):
206 return self._toklist[i]
207 else:
208 if i not in self._all_names:
209 return self._tokdict[i][-1][0]
210 else:
211 return ParseResults([v[0] for v in self._tokdict[i]])
212
213 def __setitem__(self, k, v, isinstance=isinstance):
214 if isinstance(v, _ParseResultsWithOffset):
215 self._tokdict[k] = self._tokdict.get(k, list()) + [v]
216 sub = v[0]
217 elif isinstance(k, (int, slice)):
218 self._toklist[k] = v
219 sub = v
220 else:
221 self._tokdict[k] = self._tokdict.get(k, list()) + [
222 _ParseResultsWithOffset(v, 0)
223 ]
224 sub = v
225 if isinstance(sub, ParseResults):
226 sub._parent = self
227
228 def __delitem__(self, i):
229 if isinstance(i, (int, slice)):
230 mylen = len(self._toklist)
231 del self._toklist[i]
232
233 # convert int to slice
234 if isinstance(i, int):
235 if i < 0:
236 i += mylen
237 i = slice(i, i + 1)
238 # get removed indices
239 removed = list(range(*i.indices(mylen)))
240 removed.reverse()
241 # fixup indices in token dictionary
242 for name, occurrences in self._tokdict.items():
243 for j in removed:
244 for k, (value, position) in enumerate(occurrences):
245 occurrences[k] = _ParseResultsWithOffset(
246 value, position - (position > j)
247 )
248 else:
249 del self._tokdict[i]
250
251 def __contains__(self, k) -> bool:
252 return k in self._tokdict
253
254 def __len__(self) -> int:
255 return len(self._toklist)
256
257 def __bool__(self) -> bool:
258 return not not (self._toklist or self._tokdict)
259
260 def __iter__(self) -> Iterator:
261 return iter(self._toklist)
262
263 def __reversed__(self) -> Iterator:
264 return iter(self._toklist[::-1])
265
266 def keys(self):
267 return iter(self._tokdict)
268
269 def values(self):
270 return (self[k] for k in self.keys())
271
272 def items(self):
273 return ((k, self[k]) for k in self.keys())
274
275 def haskeys(self) -> bool:
276 """
277 Since ``keys()`` returns an iterator, this method is helpful in bypassing
278 code that looks for the existence of any defined results names."""
279 return not not self._tokdict
280
281 def pop(self, *args, **kwargs):
282 """
283 Removes and returns item at specified index (default= ``last``).
284 Supports both ``list`` and ``dict`` semantics for ``pop()``. If
285 passed no argument or an integer argument, it will use ``list``
286 semantics and pop tokens from the list of parsed tokens. If passed
287 a non-integer argument (most likely a string), it will use ``dict``
288 semantics and pop the corresponding value from any defined results
289 names. A second default return value argument is supported, just as in
290 ``dict.pop()``.
291
292 Example::
293
294 numlist = Word(nums)[...]
295 print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321']
296
297 def remove_first(tokens):
298 tokens.pop(0)
299 numlist.add_parse_action(remove_first)
300 print(numlist.parse_string("0 123 321")) # -> ['123', '321']
301
302 label = Word(alphas)
303 patt = label("LABEL") + Word(nums)[1, ...]
304 print(patt.parse_string("AAB 123 321").dump())
305
306 # Use pop() in a parse action to remove named result (note that corresponding value is not
307 # removed from list form of results)
308 def remove_LABEL(tokens):
309 tokens.pop("LABEL")
310 return tokens
311 patt.add_parse_action(remove_LABEL)
312 print(patt.parse_string("AAB 123 321").dump())
313
314 prints::
315
316 ['AAB', '123', '321']
317 - LABEL: 'AAB'
318
319 ['AAB', '123', '321']
320 """
321 if not args:
322 args = [-1]
323 for k, v in kwargs.items():
324 if k == "default":
325 args = (args[0], v)
326 else:
327 raise TypeError(f"pop() got an unexpected keyword argument {k!r}")
328 if isinstance(args[0], int) or len(args) == 1 or args[0] in self:
329 index = args[0]
330 ret = self[index]
331 del self[index]
332 return ret
333 else:
334 defaultvalue = args[1]
335 return defaultvalue
336
337 def get(self, key, default_value=None):
338 """
339 Returns named result matching the given key, or if there is no
340 such name, then returns the given ``default_value`` or ``None`` if no
341 ``default_value`` is specified.
342
343 Similar to ``dict.get()``.
344
345 Example::
346
347 integer = Word(nums)
348 date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
349
350 result = date_str.parse_string("1999/12/31")
351 print(result.get("year")) # -> '1999'
352 print(result.get("hour", "not specified")) # -> 'not specified'
353 print(result.get("hour")) # -> None
354 """
355 if key in self:
356 return self[key]
357 else:
358 return default_value
359
360 def insert(self, index, ins_string):
361 """
362 Inserts new element at location index in the list of parsed tokens.
363
364 Similar to ``list.insert()``.
365
366 Example::
367
368 numlist = Word(nums)[...]
369 print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321']
370
371 # use a parse action to insert the parse location in the front of the parsed results
372 def insert_locn(locn, tokens):
373 tokens.insert(0, locn)
374 numlist.add_parse_action(insert_locn)
375 print(numlist.parse_string("0 123 321")) # -> [0, '0', '123', '321']
376 """
377 self._toklist.insert(index, ins_string)
378 # fixup indices in token dictionary
379 for name, occurrences in self._tokdict.items():
380 for k, (value, position) in enumerate(occurrences):
381 occurrences[k] = _ParseResultsWithOffset(
382 value, position + (position > index)
383 )
384
385 def append(self, item):
386 """
387 Add single element to end of ``ParseResults`` list of elements.
388
389 Example::
390
391 numlist = Word(nums)[...]
392 print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321']
393
394 # use a parse action to compute the sum of the parsed integers, and add it to the end
395 def append_sum(tokens):
396 tokens.append(sum(map(int, tokens)))
397 numlist.add_parse_action(append_sum)
398 print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321', 444]
399 """
400 self._toklist.append(item)
401
402 def extend(self, itemseq):
403 """
404 Add sequence of elements to end of ``ParseResults`` list of elements.
405
406 Example::
407
408 patt = Word(alphas)[1, ...]
409
410 # use a parse action to append the reverse of the matched strings, to make a palindrome
411 def make_palindrome(tokens):
412 tokens.extend(reversed([t[::-1] for t in tokens]))
413 return ''.join(tokens)
414 patt.add_parse_action(make_palindrome)
415 print(patt.parse_string("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
416 """
417 if isinstance(itemseq, ParseResults):
418 self.__iadd__(itemseq)
419 else:
420 self._toklist.extend(itemseq)
421
422 def clear(self):
423 """
424 Clear all elements and results names.
425 """
426 del self._toklist[:]
427 self._tokdict.clear()
428
429 def __getattr__(self, name):
430 try:
431 return self[name]
432 except KeyError:
433 if name.startswith("__"):
434 raise AttributeError(name)
435 return ""
436
437 def __add__(self, other: "ParseResults") -> "ParseResults":
438 ret = self.copy()
439 ret += other
440 return ret
441
442 def __iadd__(self, other: "ParseResults") -> "ParseResults":
443 if not other:
444 return self
445
446 if other._tokdict:
447 offset = len(self._toklist)
448 addoffset = lambda a: offset if a < 0 else a + offset
449 otheritems = other._tokdict.items()
450 otherdictitems = [
451 (k, _ParseResultsWithOffset(v[0], addoffset(v[1])))
452 for k, vlist in otheritems
453 for v in vlist
454 ]
455 for k, v in otherdictitems:
456 self[k] = v
457 if isinstance(v[0], ParseResults):
458 v[0]._parent = self
459
460 self._toklist += other._toklist
461 self._all_names |= other._all_names
462 return self
463
464 def __radd__(self, other) -> "ParseResults":
465 if isinstance(other, int) and other == 0:
466 # useful for merging many ParseResults using sum() builtin
467 return self.copy()
468 else:
469 # this may raise a TypeError - so be it
470 return other + self
471
472 def __repr__(self) -> str:
473 return f"{type(self).__name__}({self._toklist!r}, {self.as_dict()})"
474
475 def __str__(self) -> str:
476 return (
477 "["
478 + ", ".join(
479 [
480 str(i) if isinstance(i, ParseResults) else repr(i)
481 for i in self._toklist
482 ]
483 )
484 + "]"
485 )
486
487 def _asStringList(self, sep=""):
488 out = []
489 for item in self._toklist:
490 if out and sep:
491 out.append(sep)
492 if isinstance(item, ParseResults):
493 out += item._asStringList()
494 else:
495 out.append(str(item))
496 return out
497
498 def as_list(self) -> list:
499 """
500 Returns the parse results as a nested list of matching tokens, all converted to strings.
501
502 Example::
503
504 patt = Word(alphas)[1, ...]
505 result = patt.parse_string("sldkj lsdkj sldkj")
506 # even though the result prints in string-like form, it is actually a pyparsing ParseResults
507 print(type(result), result) # -> <class 'pyparsing.ParseResults'> ['sldkj', 'lsdkj', 'sldkj']
508
509 # Use as_list() to create an actual list
510 result_list = result.as_list()
511 print(type(result_list), result_list) # -> <class 'list'> ['sldkj', 'lsdkj', 'sldkj']
512 """
513 return [
514 res.as_list() if isinstance(res, ParseResults) else res
515 for res in self._toklist
516 ]
517
518 def as_dict(self) -> dict:
519 """
520 Returns the named parse results as a nested dictionary.
521
522 Example::
523
524 integer = Word(nums)
525 date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
526
527 result = date_str.parse_string('12/31/1999')
528 print(type(result), repr(result)) # -> <class 'pyparsing.ParseResults'> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
529
530 result_dict = result.as_dict()
531 print(type(result_dict), repr(result_dict)) # -> <class 'dict'> {'day': '1999', 'year': '12', 'month': '31'}
532
533 # even though a ParseResults supports dict-like access, sometime you just need to have a dict
534 import json
535 print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
536 print(json.dumps(result.as_dict())) # -> {"month": "31", "day": "1999", "year": "12"}
537 """
538
539 def to_item(obj):
540 if isinstance(obj, ParseResults):
541 return obj.as_dict() if obj.haskeys() else [to_item(v) for v in obj]
542 else:
543 return obj
544
545 return dict((k, to_item(v)) for k, v in self.items())
546
547 def copy(self) -> "ParseResults":
548 """
549 Returns a new shallow copy of a :class:`ParseResults` object. `ParseResults`
550 items contained within the source are shared with the copy. Use
551 :class:`ParseResults.deepcopy()` to create a copy with its own separate
552 content values.
553 """
554 ret = ParseResults(self._toklist)
555 ret._tokdict = self._tokdict.copy()
556 ret._parent = self._parent
557 ret._all_names |= self._all_names
558 ret._name = self._name
559 return ret
560
561 def deepcopy(self) -> "ParseResults":
562 """
563 Returns a new deep copy of a :class:`ParseResults` object.
564 """
565 ret = self.copy()
566 # replace values with copies if they are of known mutable types
567 for i, obj in enumerate(self._toklist):
568 if isinstance(obj, ParseResults):
569 self._toklist[i] = obj.deepcopy()
570 elif isinstance(obj, (str, bytes)):
571 pass
572 elif isinstance(obj, MutableMapping):
573 self._toklist[i] = dest = type(obj)()
574 for k, v in obj.items():
575 dest[k] = v.deepcopy() if isinstance(v, ParseResults) else v
576 elif isinstance(obj, Container):
577 self._toklist[i] = type(obj)(
578 v.deepcopy() if isinstance(v, ParseResults) else v for v in obj
579 )
580 return ret
581
582 def get_name(self):
583 r"""
584 Returns the results name for this token expression. Useful when several
585 different expressions might match at a particular location.
586
587 Example::
588
589 integer = Word(nums)
590 ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
591 house_number_expr = Suppress('#') + Word(nums, alphanums)
592 user_data = (Group(house_number_expr)("house_number")
593 | Group(ssn_expr)("ssn")
594 | Group(integer)("age"))
595 user_info = user_data[1, ...]
596
597 result = user_info.parse_string("22 111-22-3333 #221B")
598 for item in result:
599 print(item.get_name(), ':', item[0])
600
601 prints::
602
603 age : 22
604 ssn : 111-22-3333
605 house_number : 221B
606 """
607 if self._name:
608 return self._name
609 elif self._parent:
610 par: "ParseResults" = self._parent
611 parent_tokdict_items = par._tokdict.items()
612 return next(
613 (
614 k
615 for k, vlist in parent_tokdict_items
616 for v, loc in vlist
617 if v is self
618 ),
619 None,
620 )
621 elif (
622 len(self) == 1
623 and len(self._tokdict) == 1
624 and next(iter(self._tokdict.values()))[0][1] in (0, -1)
625 ):
626 return next(iter(self._tokdict.keys()))
627 else:
628 return None
629
630 def dump(self, indent="", full=True, include_list=True, _depth=0) -> str:
631 """
632 Diagnostic method for listing out the contents of
633 a :class:`ParseResults`. Accepts an optional ``indent`` argument so
634 that this string can be embedded in a nested display of other data.
635
636 Example::
637
638 integer = Word(nums)
639 date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
640
641 result = date_str.parse_string('1999/12/31')
642 print(result.dump())
643
644 prints::
645
646 ['1999', '/', '12', '/', '31']
647 - day: '31'
648 - month: '12'
649 - year: '1999'
650 """
651 out = []
652 NL = "\n"
653 out.append(indent + str(self.as_list()) if include_list else "")
654
655 if full:
656 if self.haskeys():
657 items = sorted((str(k), v) for k, v in self.items())
658 for k, v in items:
659 if out:
660 out.append(NL)
661 out.append(f"{indent}{(' ' * _depth)}- {k}: ")
662 if isinstance(v, ParseResults):
663 if v:
664 out.append(
665 v.dump(
666 indent=indent,
667 full=full,
668 include_list=include_list,
669 _depth=_depth + 1,
670 )
671 )
672 else:
673 out.append(str(v))
674 else:
675 out.append(repr(v))
676 if any(isinstance(vv, ParseResults) for vv in self):
677 v = self
678 for i, vv in enumerate(v):
679 if isinstance(vv, ParseResults):
680 out.append(
681 "\n{}{}[{}]:\n{}{}{}".format(
682 indent,
683 (" " * (_depth)),
684 i,
685 indent,
686 (" " * (_depth + 1)),
687 vv.dump(
688 indent=indent,
689 full=full,
690 include_list=include_list,
691 _depth=_depth + 1,
692 ),
693 )
694 )
695 else:
696 out.append(
697 "\n%s%s[%d]:\n%s%s%s"
698 % (
699 indent,
700 (" " * (_depth)),
701 i,
702 indent,
703 (" " * (_depth + 1)),
704 str(vv),
705 )
706 )
707
708 return "".join(out)
709
710 def pprint(self, *args, **kwargs):
711 """
712 Pretty-printer for parsed results as a list, using the
713 `pprint <https://docs.python.org/3/library/pprint.html>`_ module.
714 Accepts additional positional or keyword args as defined for
715 `pprint.pprint <https://docs.python.org/3/library/pprint.html#pprint.pprint>`_ .
716
717 Example::
718
719 ident = Word(alphas, alphanums)
720 num = Word(nums)
721 func = Forward()
722 term = ident | num | Group('(' + func + ')')
723 func <<= ident + Group(Optional(DelimitedList(term)))
724 result = func.parse_string("fna a,b,(fnb c,d,200),100")
725 result.pprint(width=40)
726
727 prints::
728
729 ['fna',
730 ['a',
731 'b',
732 ['(', 'fnb', ['c', 'd', '200'], ')'],
733 '100']]
734 """
735 pprint.pprint(self.as_list(), *args, **kwargs)
736
737 # add support for pickle protocol
738 def __getstate__(self):
739 return (
740 self._toklist,
741 (
742 self._tokdict.copy(),
743 None,
744 self._all_names,
745 self._name,
746 ),
747 )
748
749 def __setstate__(self, state):
750 self._toklist, (self._tokdict, par, inAccumNames, self._name) = state
751 self._all_names = set(inAccumNames)
752 self._parent = None
753
754 def __getnewargs__(self):
755 return self._toklist, self._name
756
757 def __dir__(self):
758 return dir(type(self)) + list(self.keys())
759
760 @classmethod
761 def from_dict(cls, other, name=None) -> "ParseResults":
762 """
763 Helper classmethod to construct a ``ParseResults`` from a ``dict``, preserving the
764 name-value relations as results names. If an optional ``name`` argument is
765 given, a nested ``ParseResults`` will be returned.
766 """
767
768 def is_iterable(obj):
769 try:
770 iter(obj)
771 except Exception:
772 return False
773 # str's are iterable, but in pyparsing, we don't want to iterate over them
774 else:
775 return not isinstance(obj, str_type)
776
777 ret = cls([])
778 for k, v in other.items():
779 if isinstance(v, Mapping):
780 ret += cls.from_dict(v, name=k)
781 else:
782 ret += cls([v], name=k, asList=is_iterable(v))
783 if name is not None:
784 ret = cls([ret], name=name)
785 return ret
786
787 asList = as_list
788 """Deprecated - use :class:`as_list`"""
789 asDict = as_dict
790 """Deprecated - use :class:`as_dict`"""
791 getName = get_name
792 """Deprecated - use :class:`get_name`"""
793
794
795 MutableMapping.register(ParseResults)
796 MutableSequence.register(ParseResults)