vendor/CMF/1.6.3/CMFCore

view FSPythonScript.py @ 2:4c712d7bd1d7

Added tag 1.6.3 for changeset 1babb9d61518
author Georges Racinet on purity.racinet.fr <georges@racinet.fr>
date Fri, 09 Sep 2011 12:44:00 +0200
parents
children
line source
1 ##############################################################################
2 #
3 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
4 #
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE.
11 #
12 ##############################################################################
13 """ Customizable Python scripts that come from the filesystem.
15 $Id$
16 """
18 import new
20 from AccessControl import ClassSecurityInfo
21 from AccessControl import getSecurityManager
22 from ComputedAttribute import ComputedAttribute
23 from Globals import DTMLFile
24 from Globals import InitializeClass
25 from OFS.Cache import Cacheable
26 from Products.PythonScripts.PythonScript import PythonScript
27 from Shared.DC.Scripts.Script import Script
29 from DirectoryView import registerFileExtension
30 from DirectoryView import registerMetaType
31 from FSObject import FSObject
32 from permissions import FTPAccess
33 from permissions import View
34 from permissions import ViewManagementScreens
35 from utils import _dtmldir
36 from utils import expandpath
38 _marker = []
41 class bad_func_code:
42 co_varnames = ()
43 co_argcount = 0
46 class FSPythonScript (FSObject, Script):
47 """FSPythonScripts act like Python Scripts but are not directly
48 modifiable from the management interface."""
50 meta_type = 'Filesystem Script (Python)'
51 _params = _body = ''
52 _v_f = None
53 _proxy_roles = ()
55 _owner = None # Unowned
57 manage_options=(
58 (
59 {'label':'Customize', 'action':'manage_main'},
60 {'label':'Test',
61 'action':'ZScriptHTML_tryForm',
62 'help': ('PythonScripts', 'PythonScript_test.stx')},
63 )
64 + Cacheable.manage_options
65 )
67 # Use declarative security
68 security = ClassSecurityInfo()
69 security.declareObjectProtected(View)
70 security.declareProtected(View, 'index_html',)
71 # Prevent the bindings from being edited TTW
72 security.declarePrivate('ZBindings_edit','ZBindingsHTML_editForm',
73 'ZBindingsHTML_editAction')
75 security.declareProtected(ViewManagementScreens, 'manage_main')
76 manage_main = DTMLFile('custpy', _dtmldir)
78 def _createZODBClone(self):
79 """Create a ZODB (editable) equivalent of this object."""
80 obj = PythonScript(self.getId())
81 obj.write(self.read())
82 return obj
84 def _readFile(self, reparse):
85 """Read the data from the filesystem.
87 Read the file (indicated by exandpath(self._filepath), and parse the
88 data if necessary.
89 """
90 fp = expandpath(self._filepath)
91 file = open(fp, 'rU')
92 try: data = file.read()
93 finally: file.close()
94 if reparse:
95 self._write(data, reparse)
97 def _validateProxy(self, roles=None):
98 pass
100 def __render_with_namespace__(self, namespace):
101 '''Calls the script.'''
102 self._updateFromFS()
103 return Script.__render_with_namespace__(self, namespace)
105 def __call__(self, *args, **kw):
106 '''Calls the script.'''
107 self._updateFromFS()
108 return Script.__call__(self, *args, **kw)
110 #### The following is mainly taken from PythonScript.py ###
112 def _exec(self, bound_names, args, kw):
113 """Call a Python Script
115 Calling a Python Script is an actual function invocation.
116 """
117 # do caching
118 keyset = None
119 if self.ZCacheable_isCachingEnabled():
120 # Prepare a cache key.
121 keyset = kw.copy()
122 asgns = self.getBindingAssignments()
123 name_context = asgns.getAssignedName('name_context', None)
124 if name_context:
125 keyset[name_context] = self.aq_parent.getPhysicalPath()
126 name_subpath = asgns.getAssignedName('name_subpath', None)
127 if name_subpath:
128 keyset[name_subpath] = self._getTraverseSubpath()
129 # Note: perhaps we should cache based on name_ns also.
130 keyset['*'] = args
131 result = self.ZCacheable_get(keywords=keyset, default=_marker)
132 if result is not _marker:
133 # Got a cached value.
134 return result
136 # Prepare the function.
137 f = self._v_f
138 if f is None:
139 # The script has errors.
140 __traceback_supplement__ = (
141 FSPythonScriptTracebackSupplement, self, 0)
142 raise RuntimeError, '%s has errors.' % self._filepath
144 # Updating func_globals directly is not thread safe here.
145 # In normal PythonScripts, every thread has its own
146 # copy of the function. But in FSPythonScripts
147 # there is only one copy. So here's another way.
148 new_globals = f.func_globals.copy()
149 new_globals['__traceback_supplement__'] = (
150 FSPythonScriptTracebackSupplement, self)
151 new_globals['__file__'] = self._filepath
152 if bound_names:
153 new_globals.update(bound_names)
154 if f.func_defaults:
155 f = new.function(f.func_code, new_globals, f.func_name,
156 f.func_defaults)
157 else:
158 f = new.function(f.func_code, new_globals, f.func_name)
160 # Execute the function in a new security context.
161 security=getSecurityManager()
162 security.addContext(self)
163 try:
164 result = f(*args, **kw)
165 if keyset is not None:
166 # Store the result in the cache.
167 self.ZCacheable_set(result, keywords=keyset)
168 return result
169 finally:
170 security.removeContext(self)
172 security.declareProtected(ViewManagementScreens, 'getModTime')
173 # getModTime defined in FSObject
175 security.declareProtected(ViewManagementScreens, 'ZScriptHTML_tryForm')
176 # ZScriptHTML_tryForm defined in Shared.DC.Scripts.Script.Script
178 def ZScriptHTML_tryParams(self):
179 """Parameters to test the script with."""
180 param_names = []
181 for name in self._params.split(','):
182 name = name.strip()
183 if name and name[0] != '*':
184 param_names.append( name.split('=', 1)[0] )
185 return param_names
187 security.declareProtected(ViewManagementScreens, 'read')
188 def read(self):
189 self._updateFromFS()
190 return self._source
192 security.declareProtected(ViewManagementScreens, 'document_src')
193 def document_src(self, REQUEST=None, RESPONSE=None):
194 """Return unprocessed document source."""
196 if RESPONSE is not None:
197 RESPONSE.setHeader('Content-Type', 'text/plain')
198 return self._source
200 security.declareProtected(ViewManagementScreens, 'PrincipiaSearchSource')
201 def PrincipiaSearchSource(self):
202 "Support for searching - the document's contents are searched."
203 return "%s\n%s" % (self._params, self._body)
205 security.declareProtected(ViewManagementScreens, 'params')
206 def params(self): return self._params
208 security.declareProtected(ViewManagementScreens, 'manage_haveProxy')
209 manage_haveProxy = PythonScript.manage_haveProxy.im_func
211 security.declareProtected(ViewManagementScreens, 'body')
212 def body(self): return self._body
214 security.declareProtected(ViewManagementScreens, 'get_size')
215 def get_size(self): return len(self.read())
217 security.declareProtected(FTPAccess, 'manage_FTPget')
218 def manage_FTPget(self):
219 "Get source for FTP download"
220 self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/plain')
221 return self.read()
223 def _write(self, text, compile):
224 '''
225 Parses the source, storing the body, params, title, bindings,
226 and source in self. If compile is set, compiles the
227 function.
228 '''
229 ps = PythonScript(self.id)
230 ps.write(text)
231 if compile:
232 ps._makeFunction(1)
233 self._v_f = f = ps._v_f
234 if f is not None:
235 self.func_code = f.func_code
236 self.func_defaults = f.func_defaults
237 else:
238 # There were errors in the compile.
239 # No signature.
240 self.func_code = bad_func_code()
241 self.func_defaults = None
242 self._body = ps._body
243 self._params = ps._params
244 self.title = ps.title
245 self._setupBindings(ps.getBindingAssignments().getAssignedNames())
246 self._source = ps.read() # Find out what the script sees.
248 def func_defaults(self):
249 # This ensures func_code and func_defaults are
250 # set when the code hasn't been compiled yet,
251 # just in time for mapply(). Truly odd, but so is mapply(). :P
252 self._updateFromFS()
253 return self.__dict__.get('func_defaults', None)
254 func_defaults = ComputedAttribute(func_defaults, 1)
256 def func_code(self):
257 # See func_defaults.
258 self._updateFromFS()
259 return self.__dict__.get('func_code', None)
260 func_code = ComputedAttribute(func_code, 1)
262 def title(self):
263 # See func_defaults.
264 self._updateFromFS()
265 return self.__dict__.get('title', None)
266 title = ComputedAttribute(title, 1)
268 def getBindingAssignments(self):
269 # Override of the version in Bindings.py.
270 # This version ensures that bindings get loaded on demand.
271 if not hasattr(self, '_bind_names'):
272 # Set a default first to avoid recursion
273 self._setupBindings()
274 # Now do it for real
275 self._updateFromFS()
276 return self._bind_names
278 InitializeClass(FSPythonScript)
281 class FSPythonScriptTracebackSupplement:
282 """Implementation of ITracebackSupplement
284 Makes script-specific info available in exception tracebacks.
285 """
286 def __init__(self, script, line=-1):
287 self.object = script
288 # If line is set to -1, it means to use tb_lineno.
289 self.line = line
292 registerFileExtension('py', FSPythonScript)
293 registerMetaType('Script (Python)', FSPythonScript)