branch | dev-tf |
changeset 427 | 63284c98cdc1 |
parent 329 | 1482a4b86075 |
426:2022e4da3ad9 | 427:63284c98cdc1 |
---|---|
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
10 # FOR A PARTICULAR PURPOSE. |
10 # FOR A PARTICULAR PURPOSE. |
11 # |
11 # |
12 |
12 |
13 __docformat__ = 'restructuredtext' |
13 """PyAMS_utils.traversing module |
14 |
|
15 This module provides a custom Pyramid "namespace" traverser: using "++name++" URLs allows |
|
16 to traverse URLs based on custom traversing adapters. |
|
17 |
|
18 It also provides a "get_parent" function, which returns a parent object of given object providing |
|
19 a given interface. |
|
20 """ |
|
14 |
21 |
15 from pyramid.compat import decode_path_info, is_nonstr_iter |
22 from pyramid.compat import decode_path_info, is_nonstr_iter |
16 from pyramid.exceptions import NotFound, URLDecodeError |
23 from pyramid.exceptions import NotFound, URLDecodeError |
17 from pyramid.interfaces import VH_ROOT_KEY |
24 from pyramid.interfaces import VH_ROOT_KEY |
18 from pyramid.location import lineage |
25 from pyramid.location import lineage |
26 from pyams_utils.adapter import ContextAdapter, adapter_config |
33 from pyams_utils.adapter import ContextAdapter, adapter_config |
27 from pyams_utils.interfaces.traversing import IPathElements |
34 from pyams_utils.interfaces.traversing import IPathElements |
28 from pyams_utils.registry import query_utility |
35 from pyams_utils.registry import query_utility |
29 |
36 |
30 |
37 |
38 __docformat__ = 'restructuredtext' |
|
39 |
|
40 |
|
31 class NamespaceTraverser(ResourceTreeTraverser): |
41 class NamespaceTraverser(ResourceTreeTraverser): |
32 """Custom traverser handling views and namespaces |
42 """Custom traverser handling views and namespaces |
33 |
43 |
34 This is an upgraded version of native Pyramid traverser. |
44 This is an upgraded version of native Pyramid traverser. |
35 It adds: |
45 It adds: |
39 |
49 |
40 PLUS_SELECTOR = '+' |
50 PLUS_SELECTOR = '+' |
41 NAMESPACE_SELECTOR = PLUS_SELECTOR * 2 |
51 NAMESPACE_SELECTOR = PLUS_SELECTOR * 2 |
42 |
52 |
43 def __call__(self, request): |
53 def __call__(self, request): |
44 |
54 # pylint: disable=too-many-locals,too-many-branches,too-many-statements |
45 environ = request.environ |
55 environ = request.environ |
46 matchdict = request.matchdict |
56 matchdict = request.matchdict |
47 |
57 |
48 if matchdict is not None: |
58 if matchdict is not None: |
49 path = matchdict.get('traverse', slash) or slash |
59 path = matchdict.get('traverse', slash) or slash |
66 # empty if mounted under a path in mod_wsgi, for example |
76 # empty if mounted under a path in mod_wsgi, for example |
67 path = request.path_info or slash |
77 path = request.path_info or slash |
68 except KeyError: |
78 except KeyError: |
69 # if environ['PATH_INFO'] is just not there |
79 # if environ['PATH_INFO'] is just not there |
70 path = slash |
80 path = slash |
71 except UnicodeDecodeError as e: |
81 except UnicodeDecodeError as exc: |
72 raise URLDecodeError(e.encoding, e.object, e.start, e.end, e.reason) |
82 raise URLDecodeError(exc.encoding, exc.object, exc.start, exc.end, exc.reason) |
73 |
83 |
74 if VH_ROOT_KEY in environ: |
84 if VH_ROOT_KEY in environ: |
75 # HTTP_X_VHM_ROOT |
85 # HTTP_X_VHM_ROOT |
76 vroot_path = decode_path_info(environ[VH_ROOT_KEY]) |
86 vroot_path = decode_path_info(environ[VH_ROOT_KEY]) |
77 vroot_tuple = split_path_info(vroot_path) |
87 vroot_tuple = split_path_info(vroot_path) |
81 vroot_tuple = () |
91 vroot_tuple = () |
82 vpath = path |
92 vpath = path |
83 vroot_idx = -1 |
93 vroot_idx = -1 |
84 |
94 |
85 root = self.root |
95 root = self.root |
86 ob = vroot = root |
96 obj = vroot = root |
87 |
97 |
88 request.registry.notify(BeforeTraverseEvent(root, request)) |
98 request.registry.notify(BeforeTraverseEvent(root, request)) |
89 |
99 |
90 if vpath == slash: |
100 if vpath == slash: |
91 # invariant: vpath must not be empty |
101 # invariant: vpath must not be empty |
102 ns_selector = self.NAMESPACE_SELECTOR |
112 ns_selector = self.NAMESPACE_SELECTOR |
103 view_selector = self.VIEW_SELECTOR |
113 view_selector = self.VIEW_SELECTOR |
104 vpath_tuple = split_path_info(vpath) |
114 vpath_tuple = split_path_info(vpath) |
105 |
115 |
106 for segment in vpath_tuple: |
116 for segment in vpath_tuple: |
107 if ob is not root: |
117 if obj is not root: |
108 request.registry.notify(BeforeTraverseEvent(ob, request)) |
118 request.registry.notify(BeforeTraverseEvent(obj, request)) |
109 |
119 |
110 if segment == plus_selector: |
120 if segment == plus_selector: |
111 # check for custom namespace called '+' |
121 # check for custom namespace called '+' |
112 # currently this namespace is used in PyAMS_default_theme package to get direct access to a given |
122 # currently this namespace is used in PyAMS_default_theme package to get |
113 # content |
123 # direct access to a given content |
114 registry = get_current_registry() |
124 registry = get_current_registry() |
115 traverser = registry.queryMultiAdapter((ob, request), ITraversable, '+') |
125 traverser = registry.queryMultiAdapter((obj, request), ITraversable, '+') |
116 if traverser is None: |
126 if traverser is None: |
117 raise NotFound() |
127 raise NotFound() |
118 try: |
128 try: |
119 ob = traverser.traverse(vpath_tuple[vroot_idx + i + 2], vpath_tuple[vroot_idx + i + 3:]) |
129 obj = traverser.traverse(vpath_tuple[vroot_idx + i + 2], |
130 vpath_tuple[vroot_idx + i + 3:]) |
|
120 except IndexError: |
131 except IndexError: |
121 # the "+" namespace traverser is waiting for additional elements from input URL |
132 # the "+" namespace traverser is waiting for additional elements from |
122 # so a "+" URL not followed by something else is just an error! |
133 # input URL so a "+" URL not followed by something else is just an error! |
123 raise NotFound() |
134 raise NotFound() |
124 else: |
135 else: |
125 i += 1 |
136 i += 1 |
126 return { |
137 return { |
127 'context': ob, |
138 'context': obj, |
128 'view_name': ''.join(vpath_tuple[vroot_idx + i + 2:]), |
139 'view_name': ''.join(vpath_tuple[vroot_idx + i + 2:]), |
129 'subpath': vpath_tuple[i + 2:], |
140 'subpath': vpath_tuple[i + 2:], |
130 'traversed': vpath_tuple[:vroot_idx + i + 2], |
141 'traversed': vpath_tuple[:vroot_idx + i + 2], |
131 'virtual_root': vroot, |
142 'virtual_root': vroot, |
132 'virtual_root_path': vroot_tuple, |
143 'virtual_root_path': vroot_tuple, |
133 'root': root |
144 'root': root |
134 } |
145 } |
135 |
146 |
136 elif segment[:2] == ns_selector: |
147 elif segment[:2] == ns_selector: |
137 # check for namespace prefixed by '++' |
148 # check for namespace prefixed by '++' |
138 # when a namespace is detected, named "ITraversable" multi-adapters are searched for |
149 # when a namespace is detected, named "ITraversable" multi-adapters are |
139 # context and request, or for context, sequentially; a NotFound exception is raised if traverser |
150 # searched for context and request, or for context, sequentially; a NotFound |
140 # can't be found, otherwise it's "traverse" method is called to get new context |
151 # exception is raised if traverser can't be found, otherwise it's "traverse" |
141 ns, name = segment[2:].split(ns_selector, 1) |
152 # method is called to get new context |
153 nss, name = segment[2:].split(ns_selector, 1) |
|
142 registry = get_current_registry() |
154 registry = get_current_registry() |
143 traverser = registry.queryMultiAdapter((ob, request), ITraversable, ns) |
155 traverser = registry.queryMultiAdapter((obj, request), ITraversable, nss) |
144 if traverser is None: |
156 if traverser is None: |
145 traverser = registry.queryAdapter(ob, ITraversable, ns) |
157 traverser = registry.queryAdapter(obj, ITraversable, nss) |
146 if traverser is None: |
158 if traverser is None: |
147 raise NotFound() |
159 raise NotFound() |
148 ob = traverser.traverse(name, vpath_tuple[vroot_idx + i + 1:]) |
160 obj = traverser.traverse(name, vpath_tuple[vroot_idx + i + 1:]) |
149 i += 1 |
161 i += 1 |
150 continue |
162 continue |
151 |
163 |
152 elif segment[:2] == view_selector: |
164 elif segment[:2] == view_selector: |
153 # check for view name prefixed by '@@' |
165 # check for view name prefixed by '@@' |
154 return { |
166 return { |
155 'context': ob, |
167 'context': obj, |
156 'view_name': segment[2:], |
168 'view_name': segment[2:], |
157 'subpath': vpath_tuple[i + 1:], |
169 'subpath': vpath_tuple[i + 1:], |
158 'traversed': vpath_tuple[:vroot_idx + i + 1], |
170 'traversed': vpath_tuple[:vroot_idx + i + 1], |
159 'virtual_root': vroot, |
171 'virtual_root': vroot, |
160 'virtual_root_path': vroot_tuple, |
172 'virtual_root_path': vroot_tuple, |
161 'root': root |
173 'root': root |
162 } |
174 } |
163 |
175 |
164 try: |
176 try: |
165 getitem = ob.__getitem__ |
177 getitem = obj.__getitem__ |
166 except AttributeError: |
178 except AttributeError: |
167 return { |
179 return { |
168 'context': ob, |
180 'context': obj, |
169 'view_name': segment, |
181 'view_name': segment, |
170 'subpath': vpath_tuple[i + 1:], |
182 'subpath': vpath_tuple[i + 1:], |
171 'traversed': vpath_tuple[:vroot_idx + i + 1], |
183 'traversed': vpath_tuple[:vroot_idx + i + 1], |
172 'virtual_root': vroot, |
184 'virtual_root': vroot, |
173 'virtual_root_path': vroot_tuple, |
185 'virtual_root_path': vroot_tuple, |
174 'root': root |
186 'root': root |
175 } |
187 } |
176 |
188 |
177 try: |
189 try: |
178 next = getitem(segment) |
190 next_item = getitem(segment) |
179 except KeyError: |
191 except KeyError: |
180 return { |
192 return { |
181 'context': ob, |
193 'context': obj, |
182 'view_name': segment, |
194 'view_name': segment, |
183 'subpath': vpath_tuple[i + 1:], |
195 'subpath': vpath_tuple[i + 1:], |
184 'traversed': vpath_tuple[:vroot_idx + i + 1], |
196 'traversed': vpath_tuple[:vroot_idx + i + 1], |
185 'virtual_root': vroot, |
197 'virtual_root': vroot, |
186 'virtual_root_path': vroot_tuple, |
198 'virtual_root_path': vroot_tuple, |
187 'root': root |
199 'root': root |
188 } |
200 } |
189 if i == vroot_idx: |
201 if i == vroot_idx: |
190 vroot = next |
202 vroot = next_item |
191 ob = next |
203 obj = next_item |
192 i += 1 |
204 i += 1 |
193 |
205 |
194 if ob is not root: |
206 if obj is not root: |
195 request.registry.notify(BeforeTraverseEvent(ob, request)) |
207 request.registry.notify(BeforeTraverseEvent(obj, request)) |
196 |
208 |
197 return { |
209 return { |
198 'context': ob, |
210 'context': obj, |
199 'view_name': empty, |
211 'view_name': empty, |
200 'subpath': subpath, |
212 'subpath': subpath, |
201 'traversed': vpath_tuple, |
213 'traversed': vpath_tuple, |
202 'virtual_root': vroot, |
214 'virtual_root': vroot, |
203 'virtual_root_path': vroot_tuple, |
215 'virtual_root_path': vroot_tuple, |
208 def get_parent(context, interface=Interface, allow_context=True, condition=None): |
220 def get_parent(context, interface=Interface, allow_context=True, condition=None): |
209 """Get first parent of the context that implements given interface |
221 """Get first parent of the context that implements given interface |
210 |
222 |
211 :param object context: base element |
223 :param object context: base element |
212 :param Interface interface: the interface that parend should implement |
224 :param Interface interface: the interface that parend should implement |
213 :param boolean allow_context: if 'True' (the default), traversing is done starting with context; otherwise, |
225 :param boolean allow_context: if 'True' (the default), traversing is done starting with |
214 traversing is done starting from context's parent |
226 context; otherwise, traversing is done starting from context's parent |
215 :param callable condition: an optional function that should return a 'True' result when called with parent |
227 :param callable condition: an optional function that should return a 'True' result when |
216 as first argument |
228 called with parent as first argument |
217 """ |
229 """ |
218 if allow_context: |
230 if allow_context: |
219 parent = context |
231 parent = context |
220 else: |
232 else: |
221 parent = getattr(context, '__parent__', None) |
233 parent = getattr(context, '__parent__', None) |
236 be able to search object based on a given parent |
248 be able to search object based on a given parent |
237 """ |
249 """ |
238 |
250 |
239 @property |
251 @property |
240 def parents(self): |
252 def parents(self): |
253 """Get list of parents OIDs""" |
|
241 intids = query_utility(IIntIds) |
254 intids = query_utility(IIntIds) |
242 if intids is None: |
255 if intids is None: |
243 return [] |
256 return [] |
244 return [intids.register(parent) for parent in lineage(self.context)] |
257 return [intids.register(parent) for parent in lineage(self.context)] |