1111from plone .registry .record import Record
1212from plone .registry .recordsproxy import RecordsProxy
1313from plone .registry .recordsproxy import RecordsProxyCollection
14+ from zope .component import getUtility
1415from zope .component import queryAdapter
1516from zope .event import notify
17+ from zope .globalrequest import getRequest
1618from zope .interface import implementer
1719from zope .schema import getFieldNames
1820from zope .schema import getFieldsInOrder
1921
2022import re
2123import warnings
2224
25+ _CACHE_MARKER = object ()
26+
27+
28+ def _get_request_cache ():
29+ """Return the per-request value cache dict, or None if no request."""
30+ request = getRequest ()
31+ if request is None :
32+ return None
33+ cache = getattr (request , "_plone_registry_cache" , None )
34+ if cache is None :
35+ cache = {}
36+ request ._plone_registry_cache = cache
37+ return cache
38+
39+
40+ def get_registry ():
41+ """Get the registry utility, cached per-request.
42+
43+ Falls back to getUtility(IRegistry) when no request is active.
44+ """
45+ request = getRequest ()
46+ if request is not None :
47+ registry = getattr (request , "_plone_registry" , None )
48+ if registry is not None :
49+ return registry
50+ registry = getUtility (IRegistry )
51+ if request is not None :
52+ request ._plone_registry = registry
53+ return registry
54+
2355
2456@implementer (IRegistry )
2557class Registry (Persistent ):
@@ -31,20 +63,40 @@ def __init__(self):
3163 # Basic value access API
3264
3365 def __getitem__ (self , name ):
34- # Fetch straight from records._values to avoid loading the field
35- # as a separate persistent object
36- return self .records ._values [name ]
66+ cache = _get_request_cache ()
67+ if cache is not None :
68+ value = cache .get (name , _CACHE_MARKER )
69+ if value is not _CACHE_MARKER :
70+ return value
71+ value = self .records ._values [name ]
72+ if cache is not None :
73+ cache [name ] = value
74+ return value
3775
3876 def get (self , name , default = None ):
39- # Fetch straight from records._values to avoid loading the field
40- # as a separate persistent object
41- return self .records ._values .get (name , default )
77+ cache = _get_request_cache ()
78+ if cache is not None :
79+ value = cache .get (name , _CACHE_MARKER )
80+ if value is not _CACHE_MARKER :
81+ return value
82+ value = self .records ._values .get (name , _CACHE_MARKER )
83+ if value is _CACHE_MARKER :
84+ return default
85+ if cache is not None :
86+ cache [name ] = value
87+ return value
4288
4389 def __setitem__ (self , name , value ):
4490 # make sure we get the Record class' validation
4591 self .records [name ].value = value
92+ cache = _get_request_cache ()
93+ if cache is not None :
94+ cache [name ] = value
4695
4796 def __contains__ (self , name ):
97+ cache = _get_request_cache ()
98+ if cache is not None and name in cache :
99+ return True
48100 return name in self .records ._values
49101
50102 # Records - make this a property so that it's readonly
@@ -65,6 +117,14 @@ def forInterface(self, interface, check=True, omit=(), prefix=None, factory=None
65117 if not prefix .endswith ("." ):
66118 prefix += "."
67119
120+ # Try per-request proxy cache
121+ cache = _get_request_cache ()
122+ cache_key = ("__proxy__" , interface .__identifier__ , prefix , omit )
123+ if cache is not None :
124+ proxy = cache .get (cache_key )
125+ if proxy is not None :
126+ return proxy
127+
68128 if check :
69129 for name in getFieldNames (interface ):
70130 if name not in omit and prefix + name not in self :
@@ -76,7 +136,12 @@ def forInterface(self, interface, check=True, omit=(), prefix=None, factory=None
76136 if factory is None :
77137 factory = RecordsProxy
78138
79- return factory (self , interface , omitted = omit , prefix = prefix )
139+ proxy = factory (self , interface , omitted = omit , prefix = prefix )
140+
141+ if cache is not None :
142+ cache [cache_key ] = proxy
143+
144+ return proxy
80145
81146 def registerInterface (self , interface , omit = (), prefix = None ):
82147 if prefix is None :
0 commit comments