You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
4.4 KiB

4 years ago
  1. #!/usr/bin/env python
  2. #
  3. # Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
  4. # Copyright (c) 2008-2016 California Institute of Technology.
  5. # Copyright (c) 2016-2018 The Uncertainty Quantification Foundation.
  6. # License: 3-clause BSD. The full license text is available at:
  7. # - https://github.com/uqfoundation/dill/blob/master/LICENSE
  8. __all__ = ['parent', 'reference', 'at', 'parents', 'children']
  9. import gc
  10. import sys
  11. from ._dill import _proxy_helper as reference
  12. from ._dill import _locate_object as at
  13. def parent(obj, objtype, ignore=()):
  14. """
  15. >>> listiter = iter([4,5,6,7])
  16. >>> obj = parent(listiter, list)
  17. >>> obj == [4,5,6,7] # actually 'is', but don't have handle any longer
  18. True
  19. NOTE: objtype can be a single type (e.g. int or list) or a tuple of types.
  20. WARNING: if obj is a sequence (e.g. list), may produce unexpected results.
  21. Parent finds *one* parent (e.g. the last member of the sequence).
  22. """
  23. depth = 1 #XXX: always looking for the parent (only, right?)
  24. chain = parents(obj, objtype, depth, ignore)
  25. parent = chain.pop()
  26. if parent is obj:
  27. return None
  28. return parent
  29. def parents(obj, objtype, depth=1, ignore=()): #XXX: objtype=object ?
  30. """Find the chain of referents for obj. Chain will end with obj.
  31. objtype: an object type or tuple of types to search for
  32. depth: search depth (e.g. depth=2 is 'grandparents')
  33. ignore: an object or tuple of objects to ignore in the search
  34. """
  35. edge_func = gc.get_referents # looking for refs, not back_refs
  36. predicate = lambda x: isinstance(x, objtype) # looking for parent type
  37. #if objtype is None: predicate = lambda x: True #XXX: in obj.mro() ?
  38. ignore = (ignore,) if not hasattr(ignore, '__len__') else ignore
  39. ignore = (id(obj) for obj in ignore)
  40. chain = find_chain(obj, predicate, edge_func, depth)[::-1]
  41. #XXX: should pop off obj... ?
  42. return chain
  43. def children(obj, objtype, depth=1, ignore=()): #XXX: objtype=object ?
  44. """Find the chain of referrers for obj. Chain will start with obj.
  45. objtype: an object type or tuple of types to search for
  46. depth: search depth (e.g. depth=2 is 'grandchildren')
  47. ignore: an object or tuple of objects to ignore in the search
  48. NOTE: a common thing to ignore is all globals, 'ignore=(globals(),)'
  49. NOTE: repeated calls may yield different results, as python stores
  50. the last value in the special variable '_'; thus, it is often good
  51. to execute something to replace '_' (e.g. >>> 1+1).
  52. """
  53. edge_func = gc.get_referrers # looking for back_refs, not refs
  54. predicate = lambda x: isinstance(x, objtype) # looking for child type
  55. #if objtype is None: predicate = lambda x: True #XXX: in obj.mro() ?
  56. ignore = (ignore,) if not hasattr(ignore, '__len__') else ignore
  57. ignore = (id(obj) for obj in ignore)
  58. chain = find_chain(obj, predicate, edge_func, depth, ignore)
  59. #XXX: should pop off obj... ?
  60. return chain
  61. # more generic helper function (cut-n-paste from objgraph)
  62. # Source at http://mg.pov.lt/objgraph/
  63. # Copyright (c) 2008-2010 Marius Gedminas <marius@pov.lt>
  64. # Copyright (c) 2010 Stefano Rivera <stefano@rivera.za.net>
  65. # Released under the MIT licence (see objgraph/objgrah.py)
  66. def find_chain(obj, predicate, edge_func, max_depth=20, extra_ignore=()):
  67. queue = [obj]
  68. depth = {id(obj): 0}
  69. parent = {id(obj): None}
  70. ignore = set(extra_ignore)
  71. ignore.add(id(extra_ignore))
  72. ignore.add(id(queue))
  73. ignore.add(id(depth))
  74. ignore.add(id(parent))
  75. ignore.add(id(ignore))
  76. ignore.add(id(sys._getframe())) # this function
  77. ignore.add(id(sys._getframe(1))) # find_chain/find_backref_chain, likely
  78. gc.collect()
  79. while queue:
  80. target = queue.pop(0)
  81. if predicate(target):
  82. chain = [target]
  83. while parent[id(target)] is not None:
  84. target = parent[id(target)]
  85. chain.append(target)
  86. return chain
  87. tdepth = depth[id(target)]
  88. if tdepth < max_depth:
  89. referrers = edge_func(target)
  90. ignore.add(id(referrers))
  91. for source in referrers:
  92. if id(source) in ignore:
  93. continue
  94. if id(source) not in depth:
  95. depth[id(source)] = tdepth + 1
  96. parent[id(source)] = target
  97. queue.append(source)
  98. return [obj] # not found
  99. # backward compatability
  100. refobject = at
  101. # EOF