Skip to content

Commit

Permalink
Added publisher and util.
Browse files Browse the repository at this point in the history
apache.py now uses imp instead of __import__, and cgihandler
now automatically reloads even indirectly loaded scripts.
  • Loading branch information
gtrubetskoy committed Nov 12, 2000
1 parent 9f9df1b commit 0ce8a57
Show file tree
Hide file tree
Showing 4 changed files with 435 additions and 58 deletions.
101 changes: 53 additions & 48 deletions lib/python/mod_python/apache.py
Expand Up @@ -3,7 +3,7 @@
This file is part of mod_python. See COPYRIGHT file for details.
$Id: apache.py,v 1.24 2000/11/09 00:09:18 gtrubetskoy Exp $
$Id: apache.py,v 1.25 2000/11/12 05:01:22 gtrubetskoy Exp $
"""

Expand All @@ -18,8 +18,6 @@
import types
import _apache

# XXX consider using intern() for some strings

# a small hack to improve PythonPath performance. This
# variable stores the last PythonPath in raw (unevaled) form.
_path = None
Expand Down Expand Up @@ -61,44 +59,6 @@ class CallBack:
def __init__(self):
self.req = None


def resolve_object(self, module, object_str, silent=0):
"""
This function traverses the objects separated by .
(period) to find the last one we're looking for:
From left to right, find objects, if it is
an unbound method of a class, instantiate the
class passing the request as single argument
"""

obj = module

for obj_str in string.split(object_str, '.'):

parent = obj

# don't through attribute errors when silent
if silent and not hasattr(module, obj_str):
return None

# this adds a little clarity if we have an attriute error
if obj == module and not hasattr(module, obj_str):
if hasattr(module, "__file__"):
s = "module '%s' contains no '%s'" % (module.__file__, obj_str)
raise AttributeError, s

obj = getattr(obj, obj_str)

if hasattr(obj, "im_self") and not obj.im_self:
# this is an unbound method, its class
# needs to be instantiated
instance = parent(self.req)
obj = getattr(instance, obj_str)

return obj


class HStack:
"""
The actual stack string lives in the request object so
Expand Down Expand Up @@ -181,7 +141,7 @@ def Dispatch(self, _req, htype):

# find the object
silent = config.has_key("PythonHandlerModule")
object = self.resolve_object(module, object_str, silent)
object = resolve_object(module, object_str, silent)

if object:

Expand All @@ -201,7 +161,6 @@ def Dispatch(self, _req, htype):

handler = hstack.pop()


except SERVER_RETURN, value:
# SERVER_RETURN indicates a non-local abort from below
# with value as (result, status) or (result, None) or result
Expand Down Expand Up @@ -287,7 +246,7 @@ def ReportError(self, etype, evalue, etb, htype="N/A", hname="N/A", debug=0):
etb = None
return DONE

def import_module(module_name, req=None):
def import_module(module_name, req=None, path=None):
"""
Get the module to handle the request. If
autoreload is on, then the module will be reloaded
Expand Down Expand Up @@ -331,10 +290,21 @@ def import_module(module_name, req=None):
# import the module for the first time
else:

module = __import__(module_name)
components = string.split(module_name, '.')
for cmp in components[1:]:
module = getattr(module, cmp)
parts = string.split(module_name, '.')
for i in range(len(parts)):
f, p, d = imp.find_module(parts[i], path)
try:
mname = string.join(parts[:i+1], ".")
module = imp.load_module(mname, f, p, d)
finally:
if f: f.close()
if hasattr(module, "__path__"):
path = module.__path__

## module = __import__(module_name)
## components = string.split(module_name, '.')
## for cmp in components[1:]:
## module = getattr(module, cmp)

# find out the last modification time
# but only if there is a __file__ attr
Expand Down Expand Up @@ -363,6 +333,41 @@ def import_module(module_name, req=None):

return module

def resolve_object(module, object_str, silent=0):
"""
This function traverses the objects separated by .
(period) to find the last one we're looking for:
From left to right, find objects, if it is
an unbound method of a class, instantiate the
class passing the request as single argument
"""

obj = module

for obj_str in string.split(object_str, '.'):

parent = obj

# don't throw attribute errors when silent
if silent and not hasattr(module, obj_str):
return None

# this adds a little clarity if we have an attriute error
if obj == module and not hasattr(module, obj_str):
if hasattr(module, "__file__"):
s = "module '%s' contains no '%s'" % (module.__file__, obj_str)
raise AttributeError, s

obj = getattr(obj, obj_str)

if hasattr(obj, "im_self") and not obj.im_self:
# this is an unbound method, its class
# needs to be instantiated
instance = parent(self.req)
obj = getattr(instance, obj_str)

return obj

def build_cgi_env(req):
"""
Expand Down
30 changes: 20 additions & 10 deletions lib/python/mod_python/cgihandler.py
@@ -1,7 +1,7 @@
"""
(C) Gregory Trubetskoy, 1998 <grisha@ispol.com>
$Id: cgihandler.py,v 1.6 2000/10/29 01:29:06 gtrubetskoy Exp $
$Id: cgihandler.py,v 1.7 2000/11/12 05:01:22 gtrubetskoy Exp $
This file is part of mod_python. See COPYRIGHT file for details.
Expand All @@ -10,6 +10,7 @@
import apache
import imp
import os
import sys

# if threads are not available
# create a functionless lock object
Expand All @@ -32,18 +33,27 @@ def release(self):
# is a more sensible remedy, but this seems to work OK.
os.environ = {}

# uncomment the 5 lines beginning ### to enable experimental reload feature
# remember all imported modules
###import sys
###original = sys.modules.keys()
original = sys.modules.keys()

# find out the standard library location
stdlib, x = os.path.split(os.__file__)

def handler(req):

### # if there are any new modules since the import of this module,
### # delete them
### for m in sys.modules.keys():
### if m not in original:
### del sys.modules[m]
### if you don't need indirect modules reloaded, comment out
### code unitl ### end

# if there are any new modules since the import of this module,
# delete them.
for m in sys.modules.keys():
if m not in original:
# unless they are part of standard library
mod = sys.modules[m]
if hasattr(mod, "__file__"):
path, x = os.path.split(mod.__file__)
if path != stdlib:
del sys.modules[m]
### end

# get the filename of the script
if req.subprocess_env.has_key("script_filename"):
Expand Down
113 changes: 113 additions & 0 deletions lib/python/mod_python/publisher.py
@@ -0,0 +1,113 @@
"""
Copyright (c) 2000 Gregory Trubetskoy. All rights reserved.
This file is part of mod_python. See COPYRIGHT file for details.
$Id: publisher.py,v 1.1 2000/11/12 05:01:22 gtrubetskoy Exp $
This handler is conceputally similar to Zope's ZPublisher, except
that it:
1. Is written specifically for mod_python and is therefore much faster
2. Does not require objects to have a documentation string
3. Passes all arguments as simply string
4. Does not try to match Python errors to HTTP errors
"""

import apache
import util

import os
import string
import imp
import re

# list of suffixes - .py, .pyc, etc.
suffixes = map(lambda x: x[0], imp.get_suffixes())

# now compile a regular expression out of it:
exp = "\\" + string.join(suffixes, "$|\\")
suff_matcher = re.compile(exp)

def handler(req):

_req = req._req

args = {}

fs = util.FieldStorage(req)

# step through fields
for field in fs.list:

# if it is a file, make File()
if field.filename:
val = File(field)
else:
val = field.value

if args.has_key(field.name):
args[field.name].append(val)
else:
args[field.name] = [val]

# at the end, we replace lists with single values
for arg in args.keys():
if len(arg) == 1:
args[arg] = arg[0]

# add req
args["req"] = req

# import the script
path, module_name = os.path.split(_req.filename)

# get rid of the suffix
module_name = suff_matcher.sub("", module_name)

# import module (or reload if needed)
module = apache.import_module(module_name, _req, [path])

# now get the path PATH_INFO (everthing after script)
# and begin traversal
if not _req.subprocess_env.has_key("PATH_INFO"):
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND

func_path = _req.subprocess_env["PATH_INFO"][1:] # skip fist /
func_path = string.replace(func_path, "/", ".")

# if any part of the path begins with "_", abort
if func_path[0] == '_' or string.count(func_path, "._"):
raise apache.SERVER_RETURN, apache.HTTP_FORBIDDEN

# resolve the object ('traverse')
object = apache.resolve_object(module, func_path, silent=0)

# callable?
if callable(object):
# call it!
result = apply(object, (), args)
else:
result = object

req.send_http_header()

req.write(str(result))

return apache.OK

class File:
""" Like a file, but also has headers and filename
"""

def __init__(self, field):

# steal all the file-like methods
methods = field.file.__methods__
for m in methods:
self.__dict__[m] = methods[m]

self.headers = field.headers
self.filename = field.filename

0 comments on commit 0ce8a57

Please sign in to comment.