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