Back to index

plone3  3.1.7
FSPythonScript.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
00004 #
00005 # This software is subject to the provisions of the Zope Public License,
00006 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
00007 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00008 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00009 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00010 # FOR A PARTICULAR PURPOSE.
00011 #
00012 ##############################################################################
00013 """ Customizable Python scripts that come from the filesystem.
00014 
00015 $Id: FSPythonScript.py 76464 2007-06-07 15:38:16Z tseaver $
00016 """
00017 
00018 from difflib import unified_diff
00019 import new
00020 
00021 from AccessControl import ClassSecurityInfo
00022 from AccessControl import getSecurityManager
00023 from ComputedAttribute import ComputedAttribute
00024 from Globals import DTMLFile
00025 from Globals import InitializeClass
00026 from OFS.Cache import Cacheable
00027 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
00028 from Products.PythonScripts.PythonScript import PythonScript
00029 from Shared.DC.Scripts.Script import Script
00030 
00031 from DirectoryView import registerFileExtension
00032 from DirectoryView import registerMetaType
00033 from FSObject import FSObject
00034 from permissions import FTPAccess
00035 from permissions import View
00036 from permissions import ViewManagementScreens
00037 from utils import _dtmldir
00038 
00039 _marker = object()
00040 
00041 
00042 class bad_func_code:
00043 
00044     co_varnames = ()
00045     co_argcount = 0
00046 
00047 
00048 class CustomizedPythonScript(PythonScript):
00049 
00050     """ Subclass which captures the "source" version's text.
00051     """
00052 
00053     #meta_type = 'Customized Python Script'  #(need permissions here)
00054 
00055     security = ClassSecurityInfo()
00056 
00057     def __init__(self, id, text):
00058         self._setId(id)
00059         self.write(text)
00060         self.original_source = text
00061 
00062     security.declareProtected(ViewManagementScreens, 'getDiff')
00063     def getDiff(self):
00064         """ Return a diff of the current source with the original source.
00065         """
00066         return unified_diff( self.original_source.splitlines()
00067                            , self.read().splitlines()
00068                            , 'original'
00069                            , 'modified'
00070                            , ''
00071                            , ''
00072                            , lineterm=""
00073                            )
00074 
00075     security.declareProtected(ViewManagementScreens, 'manage_showDiff')
00076     manage_showDiff = PageTemplateFile('www/cpsDiff.pt', globals())
00077 
00078     manage_options = (PythonScript.manage_options[:1]
00079                     + ({'label': 'Diff', 'action': 'manage_showDiff'},)
00080                     + PythonScript.manage_options[1:]
00081                      )
00082 
00083 InitializeClass(CustomizedPythonScript)
00084 
00085 
00086 class FSPythonScript(FSObject, Script):
00087 
00088     """FSPythonScripts act like Python Scripts but are not directly
00089     modifiable from the management interface."""
00090 
00091     meta_type = 'Filesystem Script (Python)'
00092     _params = _body = ''
00093     _v_f = None
00094     _proxy_roles = ()
00095     _owner = None  # Unowned
00096 
00097     manage_options = ({'label':'Customize', 'action':'manage_main'},
00098                       {'label':'Test', 'action':'ZScriptHTML_tryForm',
00099                        'help': ('PythonScripts', 'PythonScript_test.stx')},
00100                      )
00101 
00102     security = ClassSecurityInfo()
00103     security.declareObjectProtected(View)
00104 
00105     security.declareProtected(ViewManagementScreens, 'manage_main')
00106     manage_main = DTMLFile('custpy', _dtmldir)
00107 
00108     security.declareProtected(View, 'index_html',)
00109     # Prevent the bindings from being edited TTW
00110     security.declarePrivate('ZBindings_edit','ZBindingsHTML_editForm',
00111                             'ZBindingsHTML_editAction')
00112 
00113     def _createZODBClone(self):
00114         """Create a ZODB (editable) equivalent of this object."""
00115         return CustomizedPythonScript(self.getId(), self.read())
00116 
00117     def _readFile(self, reparse):
00118         """Read the data from the filesystem.
00119         """
00120         file = open(self._filepath, 'rU')
00121         try:
00122             data = file.read()
00123         finally:
00124             file.close()
00125 
00126         if reparse:
00127             self._write(data, reparse)
00128 
00129     def _validateProxy(self, roles=None):
00130         pass
00131 
00132     def __render_with_namespace__(self, namespace):
00133         '''Calls the script.'''
00134         self._updateFromFS()
00135         return Script.__render_with_namespace__(self, namespace)
00136 
00137     def __call__(self, *args, **kw):
00138         '''Calls the script.'''
00139         self._updateFromFS()
00140         return Script.__call__(self, *args, **kw)
00141 
00142     #### The following is mainly taken from PythonScript.py ###
00143 
00144     def _exec(self, bound_names, args, kw):
00145         """Call a Python Script
00146 
00147         Calling a Python Script is an actual function invocation.
00148         """
00149         # do caching
00150         keyset = None
00151         if self.ZCacheable_isCachingEnabled():
00152             # Prepare a cache key.
00153             keyset = kw.copy()
00154             asgns = self.getBindingAssignments()
00155             name_context = asgns.getAssignedName('name_context', None)
00156             if name_context:
00157                 keyset[name_context] = self.aq_parent.getPhysicalPath()
00158             name_subpath = asgns.getAssignedName('name_subpath', None)
00159             if name_subpath:
00160                 keyset[name_subpath] = self._getTraverseSubpath()
00161             # Note: perhaps we should cache based on name_ns also.
00162             keyset['*'] = args
00163             result = self.ZCacheable_get(keywords=keyset, default=_marker)
00164             if result is not _marker:
00165                 # Got a cached value.
00166                 return result
00167 
00168         # Prepare the function.
00169         f = self._v_f
00170         if f is None:
00171             # The script has errors.
00172             __traceback_supplement__ = (
00173                 FSPythonScriptTracebackSupplement, self, 0)
00174             raise RuntimeError, '%s has errors.' % self._filepath
00175 
00176         # Updating func_globals directly is not thread safe here.
00177         # In normal PythonScripts, every thread has its own
00178         # copy of the function.  But in FSPythonScripts
00179         # there is only one copy.  So here's another way.
00180         new_globals = f.func_globals.copy()
00181         new_globals['__traceback_supplement__'] = (
00182             FSPythonScriptTracebackSupplement, self)
00183         new_globals['__file__'] = self._filepath
00184         if bound_names:
00185             new_globals.update(bound_names)
00186         if f.func_defaults:
00187             f = new.function(f.func_code, new_globals, f.func_name,
00188                              f.func_defaults)
00189         else:
00190             f = new.function(f.func_code, new_globals, f.func_name)
00191 
00192         # Execute the function in a new security context.
00193         security=getSecurityManager()
00194         security.addContext(self)
00195         try:
00196             result = f(*args, **kw)
00197             if keyset is not None:
00198                 # Store the result in the cache.
00199                 self.ZCacheable_set(result, keywords=keyset)
00200             return result
00201         finally:
00202             security.removeContext(self)
00203 
00204     security.declareProtected(ViewManagementScreens, 'getModTime')
00205     # getModTime defined in FSObject
00206 
00207     security.declareProtected(ViewManagementScreens, 'ZScriptHTML_tryForm')
00208     # ZScriptHTML_tryForm defined in Shared.DC.Scripts.Script.Script
00209 
00210     def ZScriptHTML_tryParams(self):
00211         """Parameters to test the script with."""
00212         param_names = []
00213         for name in self._params.split(','):
00214             name = name.strip()
00215             if name and name[0] != '*':
00216                 param_names.append( name.split('=', 1)[0] )
00217         return param_names
00218 
00219     security.declareProtected(ViewManagementScreens, 'read')
00220     def read(self):
00221         self._updateFromFS()
00222         return self._source
00223 
00224     security.declareProtected(ViewManagementScreens, 'document_src')
00225     def document_src(self, REQUEST=None, RESPONSE=None):
00226         """Return unprocessed document source."""
00227 
00228         if RESPONSE is not None:
00229             RESPONSE.setHeader('Content-Type', 'text/plain')
00230         return self._source
00231 
00232     security.declareProtected(ViewManagementScreens, 'PrincipiaSearchSource')
00233     def PrincipiaSearchSource(self):
00234         "Support for searching - the document's contents are searched."
00235         return "%s\n%s" % (self._params, self._body)
00236 
00237     security.declareProtected(ViewManagementScreens, 'params')
00238     def params(self): return self._params
00239 
00240     security.declareProtected(ViewManagementScreens, 'manage_haveProxy')
00241     manage_haveProxy = PythonScript.manage_haveProxy.im_func
00242 
00243     security.declareProtected(ViewManagementScreens, 'body')
00244     def body(self): return self._body
00245 
00246     security.declareProtected(ViewManagementScreens, 'get_size')
00247     def get_size(self): return len(self.read())
00248 
00249     security.declareProtected(FTPAccess, 'manage_FTPget')
00250     def manage_FTPget(self):
00251         "Get source for FTP download"
00252         self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/plain')
00253         return self.read()
00254 
00255     def _write(self, text, compile):
00256         '''
00257         Parses the source, storing the body, params, title, bindings,
00258         and source in self.  If compile is set, compiles the
00259         function.
00260         '''
00261         ps = PythonScript(self.id)
00262         ps.write(text)
00263         if compile:
00264             ps._makeFunction(1)
00265             self._v_f = f = ps._v_f
00266             if f is not None:
00267                 self.func_code = f.func_code
00268                 self.func_defaults = f.func_defaults
00269             else:
00270                 # There were errors in the compile.
00271                 # No signature.
00272                 self.func_code = bad_func_code()
00273                 self.func_defaults = None
00274         self._body = ps._body
00275         self._params = ps._params
00276         self.title = ps.title
00277         self._setupBindings(ps.getBindingAssignments().getAssignedNames())
00278         self._source = ps.read()  # Find out what the script sees.
00279 
00280     def func_defaults(self):
00281         # This ensures func_code and func_defaults are
00282         # set when the code hasn't been compiled yet,
00283         # just in time for mapply().  Truly odd, but so is mapply(). :P
00284         self._updateFromFS()
00285         return self.__dict__.get('func_defaults', None)
00286     func_defaults = ComputedAttribute(func_defaults, 1)
00287 
00288     def func_code(self):
00289         # See func_defaults.
00290         self._updateFromFS()
00291         return self.__dict__.get('func_code', None)
00292     func_code = ComputedAttribute(func_code, 1)
00293 
00294     def title(self):
00295         # See func_defaults.
00296         self._updateFromFS()
00297         return self.__dict__.get('title', None)
00298     title = ComputedAttribute(title, 1)
00299 
00300     def getBindingAssignments(self):
00301         # Override of the version in Bindings.py.
00302         # This version ensures that bindings get loaded on demand.
00303         if not hasattr(self, '_bind_names'):
00304             # Set a default first to avoid recursion
00305             self._setupBindings()
00306             # Now do it for real
00307             self._updateFromFS()
00308         return self._bind_names
00309 
00310 InitializeClass(FSPythonScript)
00311 
00312 
00313 class FSPythonScriptTracebackSupplement:
00314 
00315     """Implementation of ITracebackSupplement
00316 
00317     Makes script-specific info available in exception tracebacks.
00318     """
00319 
00320     def __init__(self, script, line=-1):
00321         self.object = script
00322         # If line is set to -1, it means to use tb_lineno.
00323         self.line = line
00324 
00325 
00326 registerFileExtension('py', FSPythonScript)
00327 registerMetaType('Script (Python)', FSPythonScript)