results.py 26 KB

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