import gc
import inspect


def find_backref_chain(obj, predicate, max_depth=20, extra_ignore=()):
	"""Find a shortest chain of references leading to obj.

	The start of the chain will be some object that matches your predicate.

	``max_depth`` limits the search depth.

	``extra_ignore`` can be a list of object IDs to exclude those objects from
	your search.

	Example:

		>>> find_backref_chain(obj, inspect.ismodule)
		[<module ...>, ..., obj]

	Returns None if such a chain could not be found.
	"""
	queue = [obj]
	depth = {id(obj): 0}
	parent = {id(obj): None}
	ignore = set(extra_ignore)
	ignore.add(id(extra_ignore))
	ignore.add(id(queue))
	ignore.add(id(depth))
	ignore.add(id(parent))
	ignore.add(id(ignore))
	gc.collect()
	while queue:
		target = queue.pop(0)
		if predicate(target):
			chain = [target]
			while parent[id(target)] is not None:
				target = parent[id(target)]
				chain.append(target)
			return chain
		tdepth = depth[id(target)]
		if tdepth < max_depth:
			referrers = gc.get_referrers(target)
			ignore.add(id(referrers))
			for source in referrers:
				if inspect.isframe(source) or id(source) in ignore:
					continue
				if id(source) not in depth:
					depth[id(source)] = tdepth + 1
					parent[id(source)] = target
					queue.append(source)
	return None