[kupfer: 27/31] puid: Use a context manager for proper recursion blocking



commit 2e6738701ca27dc66b21eeed2c3f4592a9d50170
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date:   Thu Dec 31 00:49:07 2009 +0100

    puid: Use a context manager for proper recursion blocking
    
    When seeking objects in the catalog, we must block recursion since we
    can't in general re-enter a source we are already visiting. To do this
    properly we use a context manager that tracks a "stack" of the places
    we are currently visiting.

 kupfer/puid.py |   45 +++++++++++++++++++++++++++++++++++++--------
 1 files changed, 37 insertions(+), 8 deletions(-)
---
diff --git a/kupfer/puid.py b/kupfer/puid.py
index 9636fa6..d29e6ce 100644
--- a/kupfer/puid.py
+++ b/kupfer/puid.py
@@ -2,6 +2,10 @@
 Persistent Globally Unique Indentifiers for KupferObjects.
 """
 
+from __future__ import with_statement
+
+import contextlib
+
 try:
 	import cPickle as pickle
 except ImportError:
@@ -34,22 +38,47 @@ def is_reference(puid):
 	"Return True if @puid is a reference-type ID"
 	return not isinstance(puid, SerializedObject)
 
-def _find_obj_in_catalog(puid, catalog, excluding=None):
+# A Context manager to block recursion when seeking inside a
+# catalog; we have a stack (@_excluding) of the sources we
+# are visiting, and nested context with the _exclusion
+# context manager
+
+_excluding = []
+ contextlib contextmanager
+def _exclusion(src):
+	try:
+		_excluding.append(src)
+		yield
+	finally:
+		_excluding.pop()
+
+def _is_currently_excluding(src):
+	return src is not None and src in _excluding
+
+def _find_obj_in_catalog(puid, catalog):
 	if puid.startswith(qfurl.QFURL_SCHEME):
 		qfu = qfurl.qfurl(url=puid)
 		return qfu.resolve_in_catalog(catalog)
 	for src in catalog:
-		if excluding is not None and src == excluding:
+		if _is_currently_excluding(src):
 			continue
-		for obj in src.get_leaves():
-			if repr(obj) == puid:
-				return obj
+		with _exclusion(src):
+			for obj in src.get_leaves():
+				if repr(obj) == puid:
+					return obj
 	return None
 
 def resolve_unique_id(puid, excluding=None):
 	"""
-	Resolve unique id @puid inside @catalog
+	Resolve unique id @puid
+
+	The caller (if a Source) should pass itself as @excluding,
+	so that recursion into itself is avoided.
 	"""
+	if excluding is not None:
+		with _exclusion(excluding):
+			return resolve_unique_id(puid, None)
+
 	if puid is None:
 		return None
 	if isinstance(puid, SerializedObject):
@@ -59,12 +88,12 @@ def resolve_unique_id(puid, excluding=None):
 			pretty.print_debug(__name__, type(exc).__name__, exc)
 			return None
 	sc = data.GetSourceController()
-	obj = _find_obj_in_catalog(puid, sc._pre_root, excluding=excluding)
+	obj = _find_obj_in_catalog(puid, sc._pre_root)
 	if obj is not None:
 		pretty.print_debug(__name__, "Resolving %s to %s" % (puid, obj))
 		return obj
 	other_sources = set(sc.sources) - set(sc._pre_root)
-	obj = _find_obj_in_catalog(puid, other_sources, excluding=excluding)
+	obj = _find_obj_in_catalog(puid, other_sources)
 	pretty.print_debug(__name__, "Resolving %s to %s" % (puid, obj))
 	return obj
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]