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