[zopeorg-checkins] CVS: Products/PythonMethod - CHANGES.txt:1.1 COPYRIGHT.txt:1.1 Guarded.py:1.1 PythonMethod.py:1.1 README.txt:1.1 __init__.py:1.1 version.txt:1.1

Sidnei da Silva sidnei at x3ng.com.br
Fri May 30 11:17:47 EDT 2003


Update of /cvs-zopeorg/Products/PythonMethod
In directory cvs.zope.org:/tmp/cvs-serv19195/PythonMethod

Added Files:
	CHANGES.txt COPYRIGHT.txt Guarded.py PythonMethod.py 
	README.txt __init__.py version.txt 
Log Message:
Adding products needed for migration of NZO

=== Added File Products/PythonMethod/CHANGES.txt ===
1999-12-13  Evan Simpson <evan at 4-am.com>

	* Version 0.1.7
	* Nested functions and lambdas are now supported, with full safety.
	* You can access all of the dtml-var format functions through a builtin
	dictionary called special_formats (eg: special_formats['html-quote']).
	* Handing off to Digital Creations for inclusion in CVS.
	* Packaged with packProduct script, which excludes parent directories
	and .pyc files.  Makes for a smaller package, and doesn't step on
	ownership/permissions of lib/python/Products path elements.

1999-12-01  Evan Simpson <evan at 4-am.com>

	* Added COPYRIGHT.txt, making Wide Open Source licence (BSD-style)
	explicit. (Mike Goldman provided the text, I provided the silly name).
	* Jeff Rush donated a PrincipiaSearchSource method, so that 
	PythonMethod objects can be zcataloged to the same degree
	as DTML Methods.
	* Also from Jeff Rush, a document_src method, so that the source of
	PythonMethods can be viewed via a "View Source" link if desired.
	* If a PM has a 'traverse_subpath' parameter, you can now directly
	traverse it.  The elements of the subpath will then be put into a list
	in 'traverse_subpath'. (thanks to Anthony Baxter)

1999-11-11  Evan Simpson <evan at 4-am.com>

	* Version 0.1.6
	* Fix to builtins messed up DTML Methods, so I re-fixed it.

1999-11-05  Evan Simpson <evan at 4-am.com>

	* Version 0.1.5
	* Killed *%#&$@ weird bug in which having 'add' documents in 'www'
	subdirectory prevented rename, paste, or import of existing
	PythonMethods! See use of '_www'.
	* Range, test, and several other Zope 'builtins' had an unbound 'self'
	argument unless called on _, but that's fixed.
	* Safe multiplication was utterly broken (thanks to the guard); now
	it works.  Is anyone using the safe version??

1999-10-18  Evan Simpson <evan at 4-am.com>

	* Eliminated bug which delayed stringification of printed values.

1999-10-08  Evan Simpson <evan at 4-am.com>

	* Version 0.1.4
	* Fixed mis-design noticed by Michel Pelletier, and refactored
	MakeFunction.  Now both kinds of Python Method have the bugfix
	from 0.1.3, and shouldn't provoke a transaction when called.

1999-10-07  Evan Simpson <evan at 4-am.com>

	* Version 0.1.3
	* Fixed parameter bug with 'self' and no defaults

1999-09-24  Evan Simpson <evan at 4-am.com>

        * Version 0.1.2
        * Added WebDAV/FTP access code donated by Michel Pelletier
        * Made parameters part of WebDAV/FTP text
        * Eliminated initialization of globals to None
        * Added 'global_exists' global function instead
        * Killed bug with unused parameters
        * Put switch in Guarded.py to allow both regular and
        dangerous (XXX) PythonMethods to live side-by-side.
        This means that people who patched version 0.1.1
        will have to re-create any unsafe PMs they use (Sorry).

1999-09-10  Evan Simpson <evan at 4-am.com>

        * Version 0.1.1
	* Incorporated DT_Util builtins and guards
	* Fixed direct access via URL
	* Fixed methodAdd.dtml
	* rstrip function body
	* Major changes to zbytecodehacks


=== Added File Products/PythonMethod/COPYRIGHT.txt ===
This software is released under the following Wide-Open Source licence:

Copyright (c) 1999 Evan Simpson
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. Neither the name of the Author nor the names of other contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.


=== Added File Products/PythonMethod/Guarded.py ===
from zbytecodehacks.VSExec import SafeBlock, GuardedOps, UntupleFunction
from DocumentTemplate.VSEval import careful_mul
from DocumentTemplate.DT_Util import TemplateDict, test
from DocumentTemplate.DT_Var import special_formats

safefuncs = TemplateDict()
safebin = safefuncs.__class__.__dict__.copy()
for k, v in safebin.items():
    if type(v)==type(test):
        safebin[k] = getattr(safefuncs, k)
safebin['special_formats'] = special_formats

class Guard:
    mul = careful_mul

theGuard = Guard()

class GuardedBlock(SafeBlock):
    Mungers = SafeBlock.Mungers + [GuardedOps(theGuard)]

# BEWARE OF THE LEOPARD!

# Set 'do_XXX' true to allow creation of XXXPythonMethods
# and ADD A MASSIVE GAPING SECURITY HOLE to Zope.
# DON'T DO THIS unless you use secure connections for ALL OF
# YOUR ADMINISTRATION or are RECKLESS and/or FEARLESS.
# Even if you DON'T create ANY XXXPythonMethods, just turning this
# on will PUT YOU AT RISK BIGTIME!
do_XXX = 0
if do_XXX:
    from zbytecodehacks.VSExec import CodeBlock, Printing
    class UnGuardedBlock(CodeBlock):
        Mungers = [Printing]


=== Added File Products/PythonMethod/PythonMethod.py ===
"""Python Method Product

This product provides support for internal methods, which allow
safe implementation in a large Python subset.
"""

__version__='$Revision: 1.1 $'[11:-2]

from Globals import MessageDialog, HTMLFile, package_home
import sys, os, traceback, re
from OFS.SimpleItem import pretty_tb, SimpleItem
from DateTime.DateTime import DateTime
from string import join, rstrip, split
import Guarded

class AnObject:
  def __init__(self, **kw):
    self.__dict__.update(kw)

_www = os.path.join(package_home(globals()), 'www')

manage_addPythonMethodForm=HTMLFile('methodAdd', _www)

def manage_addPythonMethod(self, id, title, params, body, REQUEST=None):
    """Add an internal method to a folder

    In addition to the standard Zope object-creation arguments,
    'id' and title, the following arguments are defined:

        params -- A standard Python parameter list.

        body -- The text of the Python function definition.
    """
    it = PythonMethod(id, title, params, body)
    self._setObject(id, it)
    return self.manage_main(self, REQUEST)

class PythonMethod(SimpleItem):
    """Web-callable functions written in a safe subset of Python.

    The function may include standard python code, so long as it does
    not assign to qualified names ([], .foo, ()), reference names
    beginning with an underscore, or attempt to use the "del", "exec",
    or "import" statements.

    """

    meta_type='Python Method'
    _proxy_roles = ()
    index_html = None

    _params = _body = None

    manage_options = (
        {'label':'Edit', 'action':'manage_main'},
        {'label':'Try It', 'action':''},
        {'label':'Proxy', 'action':'manage_proxyForm'},
        {'label':'Security', 'action':'manage_access'},
        )

    __ac_permissions__ = (
        ('View management screens', ('manage_main',)),
        ('Change Python Methods', ('manage_edit',)),
        ('Change proxy roles', ('manage_proxyForm', 'manage_proxy')),
        ('View', ('__call__','')),
        )

    def __init__(self, id, title, params, body):
        self.id = id
        self.manage_edit(title, params, body)

    manage = manage_main = HTMLFile('methodEdit', _www)
    manage_proxyForm = HTMLFile('methodProxy', _www)

    def manage_edit(self, title, params, body, height=None, width=None,
                    dtpref_cols='50', dtpref_rows='20', REQUEST=None):
        """Change the method

        See the description of manage_addPythonMethod for a
        description of the arguments 'params' and 'body'.
        """
        self._validateProxy(REQUEST)
        if height is not None or width is not None:
            return self._er(title, params, body, height, width, dtpref_cols,
                            dtpref_rows, REQUEST)
        self.title = title
        if type(body) is not type(''):
            body = body.read()
        if self._params <> params or self._body <> body:
            self._params = params
            self._body = rstrip(body)
            self.makeFunction(1)
        if REQUEST:
            message = "Content changed."
            if self.warnings:
                message = ("<strong>Warning:</strong> <i>%s</i>" 
                           % join(self.warnings, '<br>'))
            return self.manage_main(self, REQUEST, manage_tabs_message=message)

    def _er(self, title, params, body, height, width, dtpref_cols,
            dtpref_rows, REQUEST):
        szch = {'+': 5, '-': -5, None: 0}
        try: rows = int(height)
        except: rows = max(1, int(dtpref_rows) + szch.get(height, 0))
        try: cols = int(width)
        except: cols = max(40, int(dtpref_cols) + szch.get(width, 0))
        e = (DateTime('GMT') + 365).rfc822()
        resp = REQUEST['RESPONSE']
        resp.setCookie('dtpref_rows',str(rows),path='/',expires=e)
        resp.setCookie('dtpref_cols',str(cols),path='/',expires=e)
        return self.manage_main(self, REQUEST, title=title, params=params,
            body=body, dtpref_cols=cols, dtpref_rows=rows)

    def _checkCBlock(self, MakeBlock):
        blk = MakeBlock(self.id, self._params, self._body)
        self.errors, self.warnings = blk.errors, blk.warnings
        if blk.errors:
            if hasattr(self, '_v_f'): del self._v_f
            self.func_defaults = ()
            self.func_code = AnObject(co_varnames = (), co_argcount=0)
        else:
            self._t = blk.t

    def _newfun(self, compiled, g, **kws):
        from Guarded import UntupleFunction
        self._v_f = f = apply(UntupleFunction, (self._t, g), kws)
        fc = f.func_code
        if compiled:
            self.func_defaults=f.func_defaults
            self.func_code=AnObject(co_varnames=fc.co_varnames,
                    co_argcount=fc.co_argcount)
        fdefs = (f.func_defaults and len(f.func_defaults)) or 0
        self._v_selfish = (fc.co_varnames and fc.co_varnames[0]=='self' and
                           (fc.co_argcount - fdefs - 1))
        return f

    def makeFunction(self, compile=0):
        from Guarded import GuardedBlock, safefuncs, theGuard, safebin
        if compile:
            self._checkCBlock(GuardedBlock)
        if self.errors:
            raise "Python Method Error", join(self.errors, '\n')
        return self._newfun(compile, {'$guard': theGuard},
              __builtins__=safebin, _=safefuncs)

    def __call__(self, *args, **kw):
        """Call a Python Method

        Calling a Python Method is an actual function invocation.
        If:

        - The supplied number of arguments is one less than the
          required number of arguments, and

        - The name of the function's first argument is 'self'.

        then the URL parent of the object is supplied as the
        first argument.
        """

        if hasattr(self, '_v_f'):
            f = self._v_f
        else: f = self.makeFunction()

        __traceback_info__=args, kw, self.func_defaults

        try:
            return apply(f, args, kw)
        except TypeError, v:
            tb=sys.exc_traceback
            try:
                if self._v_selfish == len(args):
                    return apply(f,(self.aq_parent.this(),)+args,kw)
                raise TypeError, v, tb
            finally: tb=None

    def manage_haveProxy(self,r): return r in self._proxy_roles

    def _validateProxy(self, request, roles=None):
        if roles is None: roles=self._proxy_roles
        if not roles: return
        user=u=request.get('AUTHENTICATED_USER',None)
        if user is not None:
            user=user.hasRole
            for r in roles:
                if r and not user(None, (r,)):
                    user=None
                    break
            if user is not None: return

        raise 'Forbidden', ('You are not authorized to change <em>%s</em> '
            'because you do not have proxy roles.\n<!--%s, %s-->' 
            % (self.id, u, roles))

    def manage_proxy(self, roles=(), REQUEST=None):
        "Change Proxy Roles"
        self._validateProxy(REQUEST, roles)
        self._validateProxy(REQUEST)
        self._proxy_roles=tuple(roles)
        if REQUEST: return MessageDialog(
                    title  ='Success!',
                    message='Your changes have been saved',
                    action ='manage_main')

    def PUT(self, REQUEST, RESPONSE):
        """ Handle HTTP PUT requests """
        self.dav__init(REQUEST, RESPONSE)
        body=REQUEST.get('BODY', '')
        self._validateProxy(REQUEST)
        m = re.match('\s*<params>(.*)</params>\s*\n', body, re.I)
        if m:
          self._params = m.group(1)
          body = body[m.end():]
        self._body = rstrip(body)
        self.makeFunction(1)
        RESPONSE.setStatus(204)
        return RESPONSE

    def manage_FTPget(self):
        "Get source for FTP download"
        return '<params>%s</params>\n%s' % (self._params, self._body)

    def params(self): return self._params
    def body(self): return self._body
    def get_size(self): return len(self._body)
    getSize = get_size

    def PrincipiaSearchSource(self):
        "Support for searching - the document's contents are searched."
        return "%s\n%s" % (self._params, self._body)

    def document_src(self, REQUEST, RESPONSE):
        """Return unprocessed document source."""

        m = re.search('^[ ]+', self._body, re.M)
        if m: prefix = ' ' * len(m.group(0))
        else: prefix = ' ' * 4

        body = join(split(self._body, '\n'), '\n%s' % prefix)

        RESPONSE.setHeader('Content-Type', 'text/plain')
        return "def %s(%s):\n%s%s" % (self.id, self._params, prefix, body)

    def __bobo_traverse__(self, REQUEST, sub):
        """Collect direct traversal subpath if we have a parameter for it."""
        vname = 'traverse_subpath'
        noaq = self.aq_explicit
        if hasattr(noaq, sub):
            return getattr(self, sub)
        varnames = noaq.func_code.co_varnames
        if not (varnames and vname in varnames):
            REQUEST.RESPONSE.notFoundError("%s\n%s" % (REQUEST.URL, sub))
        subpath = REQUEST.other.get(vname, [])
        subpath.append(sub)
        REQUEST.set(vname, subpath)
        return self

if Guarded.do_XXX:
    class XXXPythonMethod(PythonMethod):
      """Unsafe version of PythonMethod

      No restrictions, all normal builtins, unlimited import.  The only thing
      funky is the same print-command interception as normal PMs.
      """

      meta_type='XXX Python Method'

      def makeFunction(self, compile=0):
          from Guarded import UnGuardedBlock, safefuncs
          if compile:
              self._checkCBlock(UnGuardedBlock)
          if self.errors:
              raise "XXX Python Method Error", join(self.errors, '\n')
          return self._newfun(compile, {}, __builtins__=__builtins__,
                              _=safefuncs)

    manage_addXXXPythonMethodForm=HTMLFile('XXXmethodAdd', _www)

    def manage_addXXXPythonMethod(self, id, title, params, body, REQUEST=None):
        """Add an internal method to a folder

        In addition to the standard Zope object-creation arguments,
        'id' and title, the following arguments are defined:

            params -- A standard Python parameter list.

            body -- The text of the Python function definition.
        """
        it = XXXPythonMethod(id, title, params, body)
        self._setObject(id, it)
        return self.manage_main(self, REQUEST)


=== Added File Products/PythonMethod/README.txt ===
Python Methods


  The Python Method product provides support for internal Python
  methods, exposing them as callable objects within the Zope 
  environment.


=== Added File Products/PythonMethod/__init__.py ===
__doc__='''PythonMethod Product'''
__version__='$Revision: 1.1 $'[11:-2]

import PythonMethod

def initialize(context):
    context.registerClass(instance_class=PythonMethod.PythonMethod,
        constructors=(PythonMethod.manage_addPythonMethodForm,
                      PythonMethod.manage_addPythonMethod),
        icon='www/pymethod.gif')
    import Guarded
    if Guarded.do_XXX:
        context.registerClass(instance_class=PythonMethod.XXXPythonMethod,
            constructors=(PythonMethod.manage_addXXXPythonMethodForm,
                          PythonMethod.manage_addXXXPythonMethod),
            icon='www/xpymethod.gif')


=== Added File Products/PythonMethod/version.txt ===
PythonMethod-0-1-7





More information about the zopeorg-checkins mailing list