Tries to find a pathway to access item from source.
(item, source, depth=1, queue_limit=1000000, found_limit=1000000)
| 6 | return next(itertools.islice(iterable, n, None), default) |
| 7 | |
| 8 | def find(item, source, depth=1, queue_limit=1000000, found_limit=1000000): |
| 9 | "Tries to find a pathway to access item from source." |
| 10 | assert depth > 0, 'Cannot find item in source!' |
| 11 | candidates = collections.deque([(source, 'source', 1)]) |
| 12 | locations = {id(source)} |
| 13 | while candidates: |
| 14 | if len(candidates) > queue_limit or len(locations) > found_limit: |
| 15 | break |
| 16 | source, path, level = candidates.pop() |
| 17 | # Search container. |
| 18 | try: |
| 19 | iterator = iter(source) |
| 20 | except TypeError: |
| 21 | pass |
| 22 | else: |
| 23 | for key, value in enumerate(iterator): |
| 24 | if isinstance(source, dict): |
| 25 | key, value = value, source[value] |
| 26 | addr = id(value) |
| 27 | if addr not in locations: |
| 28 | try: |
| 29 | assert source[key] is value |
| 30 | except (AssertionError, KeyError, TypeError): |
| 31 | attr_path = 'nth({}, {})'.format(path, key) |
| 32 | else: |
| 33 | attr_path = '{}[{!r}]'.format(path, key) |
| 34 | if value is item: |
| 35 | return attr_path |
| 36 | if level < depth: |
| 37 | candidates.appendleft((value, attr_path, level + 1)) |
| 38 | locations.add(addr) |
| 39 | # Search attributes. |
| 40 | for name in dir(source): |
| 41 | try: |
| 42 | attr = getattr(source, name) |
| 43 | except AttributeError: |
| 44 | pass |
| 45 | else: |
| 46 | addr = id(attr) |
| 47 | if addr not in locations: |
| 48 | attr_path = '{}.{}'.format(path, name) |
| 49 | if attr is item: |
| 50 | return attr_path |
| 51 | if level < depth: |
| 52 | candidates.appendleft((attr, attr_path, level + 1)) |
| 53 | locations.add(addr) |