[Zope-CVS] CVS: Packages/cZPT/OFS - Makefile:1.1 cTraversable.c:1.1 pseudocode.py:1.1 speedtest.py:1.1

Shane Hathaway shane@cvs.zope.org
Mon, 16 Sep 2002 11:44:34 -0400


Update of /cvs-repository/Packages/cZPT/OFS
In directory cvs.zope.org:/tmp/cvs-serv23309/OFS

Added Files:
	Makefile cTraversable.c pseudocode.py speedtest.py 
Log Message:
Optimized TALInterpreter and restrictedTraverse() in C.

This code is just educational for now.  Before doing much more, we should
write performance tests and try out Pyrex.


=== Added File Packages/cZPT/OFS/Makefile ===

all: cTraversable.so

cTraversable.so: cTraversable.c
	gcc -g -Wall -I /usr/local/include/python2.1 \
	-I /home/shane/cvs/Zope/lib/Components/ExtensionClass/src -shared -o \
	cTraversable.so cTraversable.c



=== Added File Packages/cZPT/OFS/cTraversable.c === (554/654 lines abridged)
/*****************************************************************************

  Copyright (c) 2002 Zope Corporation and Contributors.
  All Rights Reserved.

  This software is subject to the provisions of the Zope Public License,
  Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  FOR A PARTICULAR PURPOSE

 ****************************************************************************/

/*
  $Id: cTraversable.c,v 1.1 2002/09/16 15:44:33 shane Exp $

  Based on revision 1.15 of OFS/Traversable.py and revision 1.36 of
  Products/PageTemplates/Expressions.py.
 */

#include <Python.h>
#include <ExtensionClass.h>
#include <Acquisition.h>

#define CHECKPTR(expr) if ((expr) == NULL) goto error
#define CHECKINT(expr) if ((expr) < 0) goto error
#define DECREF_ASSIGN(var, value) Py_DECREF(var); (var) = (value)
#define DECREF2_CHECK(owned, maybe_owned) \
  Py_DECREF(owned); CHECKPTR(maybe_owned); Py_DECREF(maybe_owned)
#define RETURN_NONE Py_INCREF(Py_None); return Py_None


PyObject *Unauthorized, *getSecurityManager, *aq_validate, *Containers;
PyObject *py___bobo_traverse__, *py_validate, *py_TraversalRequestNameStack;
PyObject *py_getPhysicalRoot, *py_path;


static int
isSimpleContainer(PyObject *object) {
  /*
    # Returns 1 for container object types that don't need to be protected.
    return Containers(type(object))
  */
  PyObject *o1, *o2;
  int res;

  CHECKPTR(o1 = PyTuple_New(1));
  CHECKPTR(o2 = PyObject_Type(object));
  PyTuple_SET_ITEM(o1, 0, o2);

[-=- -=- -=- 554 lines omitted -=- -=- -=-]

  PURE_MIXIN_CLASS(Traversable, "Traversable mixin (C)",
                   traversable_methods);

  if (!ExtensionClassImported)
    return;

  /* Create a type */
  CHECKPTR(m = Py_InitModule3("cTraversable",
                              module_functions, module___doc__));

  aq_init();

  INIT_STRING(py___bobo_traverse__, "__bobo_traverse__");
  INIT_STRING(py_validate, "validate");
  INIT_STRING(py_TraversalRequestNameStack, "TraversalRequestNameStack");
  INIT_STRING(py_getPhysicalRoot, "getPhysicalRoot");
  INIT_STRING(py_path, "path");

  d = PyModule_GetDict(m);
  PyExtensionClass_Export(d, "Traversable", TraversableType);

  /*
    from AccessControl import getSecurityManager, Unauthorized
    from AccessControl.cAccessControl import aq_validate
    from AccessControl.SimpleObjectPolicies import Containers
   */
  CHECKPTR(other = PyImport_ImportModule("AccessControl"));
  CHECKPTR(getSecurityManager =
           PyObject_GetAttrString(other, "getSecurityManager"));
  CHECKPTR(Unauthorized = PyObject_GetAttrString(other, "Unauthorized"));
  Py_DECREF(other);

  CHECKPTR(other = PyImport_ImportModule("AccessControl.cAccessControl"));
  CHECKPTR(aq_validate = PyObject_GetAttrString(other, "aq_validate"));
  Py_DECREF(other);

  CHECKPTR(other =
           PyImport_ImportModule("AccessControl.SimpleObjectPolicies"));
  CHECKPTR(Containers = PyObject_GetAttrString(other, "Containers"));
  Py_DECREF(other);

  other = NULL;

  return;

 error:
  Py_XDECREF(other);
  return;
}



=== Added File Packages/cZPT/OFS/pseudocode.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""pseudocode for cTraversable.c.

This is the pseudocode for the merging of OFS.Traversable.restrictedTraverse()
and Products.PageTemplates.Expressions.restrictedTraverse().  The two are
slightly different.  This pseudocode is the basis for most of cTraversable.

Note that this code probably works, but in Python it is probably slower
than either implementation of restrictedTraverse().  However, in C it
is much faster.
"""

from AccessControl import getSecurityManager, Unauthorized
from AccessControl.cAccessControl import aq_validate
from AccessControl.SimpleObjectPolicies import Containers


def isSimpleContainer(object):
    # Returns 1 for container object types that don't need to be protected.
    return Containers(type(object))


def traverseName(object, name, validate, request_ptr, path, allow_tuple):
    o = None

    if allow_tuple and isinstance(name, type(())):
        # Special case apparently used for ZPT repeat.
        return apply(object, name)

    if name[0] == '_':
        # Never allowed in a URL.
        raise Unauthorized, name

    if name == '.':
        return object

    if name == '..':
        o = aq_parent(object)
        if o is not None:
            if validate is not None and not isSimpleContainer(object):
                if not validate(object, object, name, o):
                    raise Unauthorized, name
            return o

    t = get(object, '__bobo_traverse__', N)
    if t is not None:
        if not request_ptr:
            # create the request on the fly.
            if path is None:
                path = []
            request_ptr.append({"TraversalRequestNameStack": path,
                                "path": path})
        o = t(request_ptr[0], name)

        if validate is not None and not isSimpleContainer(object):
            # Guess at the container. :-(
            container = None
            if hasattr(o, 'im_self'):
                container = o.im_self
            elif (hasattr(getattr(object, 'aq_base', object), name)
                and getattr(object, name) == o):
                container = object
            if not validate(object, container, name, o):
                raise Unauthorized, name
        return o

    # Try an attribute.
    o = getattr(object, name, _marker)
    if o is not _marker:
        if validate is not None and not isSimpleContainer(object):
            # Check access to the attribute.
            if has(object, 'aq_acquire'):
                object.aq_acquire(
                    name, aq_validate, validate)
            else:
                if not validate(object, object, name, o):
                    raise Unauthorized, name
        return o

    # Try an item.
    o=object[name]
    if validate is not None and not isSimpleContainer(object):
        if not validate(object, object, None, o):
            raise Unauthorized, name
    return o


def traversePath(start, orig_path, default_, restricted, allow_tuple):
    request_ptr = []
    securityManager = None
    validate = None
    path = None
    object = None
    name = None

    if not orig_path:
        return self

    if restricted:
        securityManager = getSecurityManager()
        validate = securityManager.validate

    if isinstance(orig_path, StringType):
        if '/' not in orig_path:
            try:
                return traverseName(start, orig_path, validate,
                                    request_ptr, None, allow_tuple)
            except:
                if default_ is not None:
                    return default_
                raise
        path = '/'.split(orig_path)
        path.reverse()
    else:
        if len(orig_path) == 1 and orig_path[0]:
            try:
                return traverseName(start, orig_path[0], validate,
                                    request_ptr, None, allow_tuple)
            except:
                if default_ is not None:
                    return default_
                raise
        path = list(orig_path)
        path.reverse()

    if len(path) > 1 and not path[0]:
        # Remove trailing slash
        path.pop(0)

    if not path[-1]:
        # If the path starts with an empty string, go to the root first.
        pop()
        object = start.getPhysicalRoot()
        if (restricted and not securityManager.validateValue(self)):
            raise Unauthorized, name
    else:
        object = start

    while path:
        name = path.pop()
        try:
            object = traverseName(object, name, validate,
                                  request_ptr, path, allow_tuple)
        except:
            if default_ is not None:
                return default_
            raise

    return object



=== Added File Packages/cZPT/OFS/speedtest.py ===

import ZODB
from OFS.cTraversable import restrictedTraverse as c_rt
from Products.PageTemplates.Expressions import py_restrictedTraverse as py_rt
from AccessControl import User, getSecurityManager
from time import time

ob = {'a': {'b': {'c': 53}}}
path = ['a', 'b', 'c']
iter = range(10000)

assert py_rt(ob, path) == ob['a']['b']['c']
assert c_rt(ob, path) == ob['a']['b']['c']

start = time()
for n in iter:
    py_rt(ob, path)
end = time()
py_t = end - start

start = time()
for n in iter:
    c_rt(ob, path)
end = time()
c_t = end - start

start = time()
for n in iter:
    ob[path[0]][path[1]][path[2]]
end = time()
lookup_t = end - start

print py_t, c_t, lookup_t

# c_t is 2x-3x the speed of py_t, but lookup_t is 20x faster than c_t.