You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Brian Behlendorf <br...@hyperreal.com> on 1996/06/04 09:03:07 UTC

mod_python.c with documentation (fwd)

I have no bandwidth to investigate this (nor any experience with python)
but hopefully someone else does.  Enjoy.

	Brian

---------- Forwarded message ----------
Date: Tue, 28 May 96 12:39:52 +0200
From: Lele Gaifax <le...@nautilus.eclipse.it>
Reply-To: lele@eclipse.it
To: Python List <py...@cwi.nl>
Cc: brian@hyperreal.com, Neal Becker <Ne...@comsat.com>
Subject: mod_python.c with documentation

Hi all,
here is the latest version of the module that embeds a Python  
Interpreter in the Apache HTTPD Server. It is better than the  
previous in one aspect: its directives work right in `.htaccess'  
files now, as stated in the documentation. An header at top of  
mod_python.tex explains how include it in the Apache manual.

I'm CC'ing to brian@hyperreal.com of the Apache Folks since I did  
not find a better address, and I think this module deserves to be  
mentioned to them.

It's not clear to me how to put this in the contrib directory of  
Python Home Site, so if some kind soul that have better access to  
the net and/or to the Python site would do it for me and drop me a  
note...

That's it, enjoy and share it!
bye, lele.

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1996-05-28 12:20 MET by <le...@nautilus>.
# Source directory was `/Discobolo/LocalDeveloper/WiP/Apache'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#  20299 -rw-r--r-- src/mod_python.c
#   4190 -rw-r--r-- manual/mod_python.tex
#
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  echo 'WARNING: not restoring timestamps.  Consider getting and'
  echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
# ============= src/mod_python.c ==============
if test ! -d 'src'; then
  echo 'x - creating directory src'
  mkdir 'src'
fi
if test -f 'src/mod_python.c' && test X"$1" != X"-c"; then
  echo 'x - skipping src/mod_python.c (file already exists)'
else
  echo 'x - extracting src/mod_python.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'src/mod_python.c' &&
/* Copyleft (c) 1996 by Lele Gaifax.  All Rights Reserved
X *
X * This file is part of Nothing (yet).
X *
X * $RCSfile: mod_python.c,v $
X * $Revision: 1.8 $
X * $Date: 1996/05/28 10:18:38 $
X *
X * Created Fri May  3 15:12:20 1996.
X */
X
/* This module supports the Native Execution of Python Scripts within
X   the Apache HTTPD Server. It does not add any new capability to the
X   server, but lets it execute Python Scripts without the need of the
X   external Python Interpreter.
X
X   To use it, you must first rebuild the Apache Server linking in this
X   module:
X
X     a. Drop this source in the `src/' directory of the Apache  
distribution.
X     b. Modify src/Configuration adding these lines:
X
X        PYTHONDIR= /usr/local/lib/python/lib
X	EXTRA_LIBS= $(PYTHONDIR)/config.o $(PYTHONDIR)/getpath.o  
$(PYTHONDIR)/libModules.a $(PYTHONDIR)/libPython.a  
$(PYTHONDIR)/libObjects.a $(PYTHONDIR)/libParser.a
X	Module python_module mod_python.o
X
X	of course adapting PYTHONDIR value to your situation.
X
X	Please note that you should execute a `make libainstall' in
X	the Python source tree to get `getpath.c' and `config.c'
X	installed (of course, this must be done *after* a successful
X	installation of the interpreter itself); moreover, you need to
X	compile those sources by yourself, since the install procedure
X	does not complete the operation (too bad, neither the Makefile
X	installed in that directory will help you): look at the option
X	used to build the interpreter, and reusing them compile
X	`getpath.c' and `config.c' by hand. The following lines are
X	from a real situation:
X
X	$ cd /usr/local/lib/python/lib
X	$ cc -O2 -I/usr/local/include/Py -I. -DHAVE_CONFIG_H -c config.c
X	$ cc -O2 -I/usr/local/include/Py -I. -DHAVE_CONFIG_H  
-DPYTHONPATH='".:/usr/local/lib/python:/usr/local/lib/python/next3_3"'  
-c getpath.c
X
X	that, adjusting the PYTHONPATH value (in particular the
X	machine-dep directory) should work for you too.
X	
X	Alternatively, you may point PYTHONDIR directly where you keep
X	the Python source tree: in this case you will have to correct
X	the location of the libraries (`libParser.a' is in the
X	`Parser/' directory, `libObjects.a' lives in the `Objects/'
X	directory, and so on).
X
X     c. Run the `Configure' script to update the Makefile.
X     d. Run `make' to rebuild the server.
X
X   Once the server is built, to activate this module you need to put
X   somewhere in its `conf/srm.conf' a line like
X
X     AddHandler python/x-httpd-cgi .py
X
X   meaning that whenever the server is asked for a resource whose name
X   ends in `.py' it should handle it through this module
X   (`python-cgi-script' is an alias for `python/x-httpd-cgi').
X
X
X   You can configure further the Python Environment on a per directory
X   basis, for example to set the Verbosity On in your testing
X   directory, or set a particular module search path... For example,
X   my `conf/access.conf' contains something like
X
X     <Directory /usr/local/etc/apache/cgi-bin/lele>
X     PythonPath /users/lele/Library/testing-python
X     PythonVerbose On
X     PythonDebug On
X     </Directory>
X
X   to test these features: the `PythonPath' command cause its argument
X   to be prepended to the standard path (computed at compile time by
X   Python); `PythonVerbose' is equivalent to the `-v' option of the
X   interpreter, and `PythonDebug' to the `-d' option; in this case
X   these settings are available only in the given directory. */
X
/* BUGS:
********
X
X   * When handling a parsed HTML, the directive `<!--#exec  
cgi="file.py"-->'
X     is performed as a normal CGI, not through this module. This  
is caused
X     by the way Apache treats that directive. The easier  
workaround is to use
X     `<!--#include virtual="file.py"-->', that is similar to the  
former but
X     does not have this limitation.
X
X   * Since the Apache Server executes itself under user `nobody',  
it is able
X     to create the compiled version of the scripts it executes  
only when the
X     directory containing them is writable by everybody, which may be an
X     insane choice in some circumstances. The workaround is to  
create/update
X     the compiled scripts whenever you install their sources (see the
X     `compileall.py' library module in the Python Distribution). */
X
#include "httpd.h"
#include "http_config.h"
#include "util_script.h"
X
#include <Py/Python.h>
#include <Py/import.h>
#include <Py/compile.h>
#include <Py/marshal.h>
#include <Py/graminit.h>
#include <Py/node.h>
X
/* XXX Is this right? Am I correctly understanding the form of the  
type? Or
X   maybe "application/python-httpd-cgi" is a better choice? */
#define PYTHON_MAGIC_TYPE "python/x-httpd-cgi"
X
/****************
X  CONFIGURATION
*****************/
X
module python_module;
X
typedef struct python_dir_config
{
X  char * path;			/* set with PythonPath new-path */
X  int debug;			/* set with PythonDebug On|Off */
X  int verbose;			/* set with PythonVerbose On|Off */
} python_dir_config;
X
static void *
create_python_dir_config (pool * p, char * dummy)
{
X  python_dir_config * conf = (python_dir_config *) palloc (p,  
sizeof (*conf));
X
X  conf->path = NULL;
X  conf->debug = 0;
X  conf->verbose = 0;
X
X  return conf;
}
X
/* This function gets called at configuration time when a  
PythonPath command
X   is found. */
static char *
set_python_path (cmd_parms * parms, python_dir_config * dc, char * arg)
{
X  dc->path = pstrdup (parms->pool, arg);
X
X  return NULL;
}
X
/* Returns the path to be used by the Python Script. This is computed by
X   joining the value of the PythonPath command, if any, with the  
standard
X   path, retrieved with getpythonpath(). */
static const char *
get_python_path (request_rec * r)
{
X  extern char * getpythonpath();
X  python_dir_config * conf = (python_dir_config *)  
get_module_config (r->per_dir_config, &python_module);
X  char * standard_path = getpythonpath();
X
X  if (conf->path)
X    return pstrcat (r->connection->pool, conf->path, ":",  
standard_path, NULL);
X  else
X    return standard_path;
}
X
/* This function gets called at configuration time when a  
PythonDebug command
X   is found. */
static char *
set_python_debug (cmd_parms * parms, python_dir_config * dc, char * arg)
{
X  if (!strcasecmp (arg, "off"))
X    dc->debug = 0;
X  else if (!strcasecmp (arg, "on"))
X    dc->debug = 1;
X  else
X    return "PythonDebug must be set to `On' or `Off'";
X
X  return NULL;
}
X
/* This function gets called at configuration time when a  
PythonVerbose command
X   is found. */
static char *
set_python_verbose (cmd_parms * parms, python_dir_config * dc, char  
* arg)
{
X  if (!strcasecmp (arg, "off"))
X    dc->verbose = 0;
X  else if (!strcasecmp (arg, "on"))
X    dc->verbose = 1;
X  else
X    return "PythonVerbose must be set to `On' or `Off'";
X
X  return NULL;
}
X
static command_rec python_cmds[] =
{
X  { "PythonPath", set_python_path, NULL,  
ACCESS_CONF|RSRC_CONF|OR_ALL, TAKE1, "the path to be used for Python  
Scripts" },
X  { "PythonVerbose", set_python_verbose, NULL,  
ACCESS_CONF|RSRC_CONF|OR_ALL, TAKE1, "On or Off" },
X  { "PythonDebug", set_python_debug, NULL,  
ACCESS_CONF|RSRC_CONF|OR_ALL, TAKE1, "On or Off" },
X  { NULL }
};
X
/*********
X  HANDLERS
**********/
X
typedef struct python_child_stuff
{
X  request_rec * r;
X  char * argv0;			/* This is r->filename with  
the path stripped off */
X  FILE * script_in;		/* This is attached to the script's  
stdin */
X  FILE * script_out;		/* and this to its stdout, if not NPH. */
X  int args_size;		/* If > 0 this is an M_PUT or  
M_POST request */
X  int nph;			/* If != 0 this is an NPH script */
} python_child_stuff;
X
/* Validates the script, returns OK or the error code. */
static int
check (python_child_stuff * pcsp)
{
X  request_rec * r = pcsp->r;
X  int is_included = !strcmp (r->protocol, "INCLUDED");
X
X  if ((pcsp->argv0 = strrchr (pcsp->r->filename, '/')) != NULL)
X    pcsp->argv0++;
X  else
X    pcsp->argv0 = pcsp->r->filename;
X
X  /* If the name of the script begins with "nph-" then we are not going
X     to parse the headers coming from it, but we will pass the  
script output
X     "as is" to the client. */
X  pcsp->nph = !(strncmp (pcsp->argv0, "nph-", 4));
X
X  if (pcsp->nph && is_included)
X    {
X      log_reason("attempt to include NPH CGI script", r->filename, r);
X      return FORBIDDEN;
X    }
X
X  if (S_ISDIR(r->finfo.st_mode))
X    {
X      log_reason("attempt to invoke directory as script",  
r->filename, r);
X      return FORBIDDEN;
X    }
X
X  if (r->finfo.st_mode == 0)
X    {
X      log_reason("script not found or unable to stat", r->filename, r);
X      return NOT_FOUND;
X    }
X
X  if (! can_exec (&r->finfo))
X    {
X      log_reason("file permissions deny server execution",  
r->filename, r);
X      return FORBIDDEN;
X    }
X
X  if (r->method_number == M_POST || r->method_number == M_PUT)
X    {
X      const char * content_length = table_get (r->headers_in,  
"Content-length");
X
X      if (!content_length)
X	{
X	  log_reason("POST or PUT without Content-length:",  
r->filename, r);
X	  return BAD_REQUEST;
X	}
X      else
X	pcsp->args_size = atoi (content_length);
X    }
X
X  return OK;
}
X
/* Checks the existence of a valid compiled script. If it is there,  
returns
X   an opened file on it, otherwise NULL. */
static FILE *
check_compiled_module (char * cpathname, long mtime)
{
X  FILE *fp;
X  long magic;
X  long pyc_mtime;
X
X  fp = fopen (cpathname, "rb");
X  if (fp == NULL)
X    return NULL;
X
X  magic = PyMarshal_ReadLongFromFile (fp);
X  if (magic != PyImport_GetMagicNumber())
X    {
X      fclose(fp);
X      return NULL;
X    }
X
X  pyc_mtime = PyMarshal_ReadLongFromFile (fp);
X  if (pyc_mtime != mtime)
X    {
X      fclose(fp);
X      return NULL;
X    }
X
X  return fp;
}
X
/* Executes the Python Script, maybe fetching the compiled version if it
X   exists. Returns OK or SERVER_ERROR. */
static int
exec_the_script (python_child_stuff * pcsp)
{
X  char * compiled;
X  int argv0_len;
X  PyObject * main, * maindict, * result, * PyEval_EvalCode(), * error;
X  PyCodeObject * code;
X  FILE * fp;
X  long mtime;
X
X  main = PyImport_AddModule ("__main__");
X  if (!main)
X    {
X      log_reason ("Cannot add __main__ module", pcsp->r->filename,  
pcsp->r);
X      return SERVER_ERROR;
X    }
X  maindict = PyModule_GetDict (main);
X
X  mtime = PyOS_GetLastModificationTime (pcsp->argv0);
X
X  argv0_len = strlen (pcsp->argv0);
X  compiled = palloc (pcsp->r->connection->pool, argv0_len+2);
X  strcpy (compiled, pcsp->argv0);
X  compiled[argv0_len] = 'c';
X  compiled[argv0_len+1] = '\0';
X
X  if ((fp = check_compiled_module (compiled, mtime)))
X    {
X      /* Ok, a valid compiled script exist. */
X
X      code = (PyCodeObject *) PyMarshal_ReadObjectFromFile (fp);
X      if (! code)
X	{
X	  log_reason ("Cannot load compiled codeobject",  
pcsp->r->filename, pcsp->r);
X	  return SERVER_ERROR;
X	}
X    }
X  else
X    {
X      node * n, * PyParser_SimpleParseFile();
X      FILE * compfile;
X
X      /* Nop, parse it and execute */
X      fp = fopen (pcsp->argv0, "r");
X
X      if (! fp)
X	{
X	  log_reason ("Cannot open the Python Script",  
pcsp->r->filename, pcsp->r);
X	  return SERVER_ERROR;
X	}
X
X      n = PyParser_SimpleParseFile (fp, pcsp->argv0, file_input);
X      if (! n)
X	{
X	  log_reason ("Cannot parse the Python Script",  
pcsp->r->filename, pcsp->r);
X	  return SERVER_ERROR;
X	}
X
X      code = PyNode_Compile (n, pcsp->argv0);
X      PyNode_Free (n);
X
X      if (! code)
X	{
X	  log_reason ("Cannot compile the Python Script",  
pcsp->r->filename, pcsp->r);
X	  return SERVER_ERROR;
X	}
X
X      /* Write out the compiled version */
X      compfile = fopen (compiled, "wb");
X      if (compfile)
X	{
X	  PyMarshal_WriteLongToFile (PyImport_GetMagicNumber(),  
compfile);
X
X	  /* First write a 0 for mtime */
X	  PyMarshal_WriteLongToFile (0L, compfile);
X
X	  PyMarshal_WriteObjectToFile ((PyObject *) code, compfile);
X
X	  if (ferror (compfile))
X	    {
X	      /* Don't keep partial file */
X	      fclose (compfile);
X	      (void) unlink (compiled);
X	    }
X	  else
X	    {
X	      /* Now write the true mtime */
X	      fseek (compfile, 4L, 0);
X	      PyMarshal_WriteLongToFile (mtime, compfile);
X	
X	      fflush (compfile);
X	      fclose (compfile);
X	    }
X	}
X    }
X
X  result = PyEval_EvalCode (code, maindict, maindict);
X  Py_DECREF (code);
X
X  Py_XDECREF (result);
X
X  /* We are the child, and our parent is not paying particular
X     attention on us, so it's useless to send back an error condition
X     when the script exits with errors: in any case, our parent is
X     going to parse script's output. Eventually the script may
X     print something like 'Status: 500' on its output... */
X  if ((error = PyErr_Occurred()))
X    {
X      if (error != PyExc_SystemExit)
X	{
X	  log_reason ("Errors evaluating Python Script",  
pcsp->r->filename, pcsp->r);
X	  PyErr_Print();
X	}
X    }
X
X  return OK;
}
X
/* Initializes the Python environment, then executes the script. */
static void
python_child (void * child_stuff)
{
X  python_child_stuff * pcsp = child_stuff;
X  PyObject * os_module, * script_module;
X  const char * ppath;
X  extern int Py_VerboseFlag, Py_DebugFlag;
X  python_dir_config * conf = (python_dir_config *)  
get_module_config (pcsp->r->per_dir_config, &python_module);
X
X  error_log2stderr (pcsp->r->server);
X
X  Py_VerboseFlag = conf->verbose;
X  Py_DebugFlag = conf->debug;
X
X  Py_Initialize();
X
X  /* Set the default PYTHONPATH. */
X  if ((ppath = get_python_path (pcsp->r)))
X    PySys_SetPath (ppath);
X
X  /* Set the command line arguments list */
X  if ((!pcsp->r->args) || (!pcsp->r->args[0]) ||  
(ind(pcsp->r->args,'=') >= 0))
X    PySys_SetArgv (0, NULL);
X  else
X    {
X      int argc;
X      char ** argv = create_argv (pcsp->r->pool, pcsp->argv0,  
pcsp->r->args);
X
X      for (argc=0; argv[argc]; argc++)
X	/* do nothing */;
X
X      PySys_SetArgv (argc, argv);
X    }
X
X  /* Initialize the process environment: create a new Python dictionary
X     and fill it with the enviroment variables. */
X  os_module = PyImport_ImportModule ("os");
X  if (!os_module)
X    {
X      log_reason ("Cannot import ``os'' module",  
pcsp->r->filename, pcsp->r);
X      Py_Exit (1);
X    }
X  else
X    {
X      int i;
X      PyObject * environ_dict = PyDict_New();
X      PyObject * module_dict = PyModule_GetDict (os_module);
X      array_header *env_arr = table_elts (pcsp->r->subprocess_env);
X      table_entry *elts = (table_entry *)env_arr->elts;
X      char * tz = getenv("TZ");
X
X      if (tz != NULL)
X	{
X	  PyObject * ptz = PyString_FromString (tz);
X
X	  PyDict_SetItemString (environ_dict, "TZ", ptz);
X	  Py_DECREF (ptz);
X	}
X
X      for (i = 0; i < env_arr->nelts; ++i)
X	{
X	  if (elts[i].key)
X	    {
X	      PyObject * value = PyString_FromString (elts[i].val);
X
X	      PyDict_SetItemString (environ_dict, elts[i].key, value);
X	      Py_DECREF (value);
X	    }
X	}
X
X      PyDict_SetItemString (module_dict, "environ", environ_dict);
X      Py_DECREF (environ_dict);
X    }
X
X  if (pcsp->nph)
X    {
X      /* The script claims to control the output by its own, so simply
X	 connect sys.stdout to our client's stdin */
X      PyObject * sys_module = PyImport_ImportModule ("sys");
X
X      if (!sys_module)
X	{
X	  log_reason ("Cannot import ``sys'' module",  
pcsp->r->filename, pcsp->r);
X	  Py_Exit (1);
X	}
X      else
X	{
X	  FILE * out = pfdopen (pcsp->r->connection->pool,
X				pcsp->r->connection->client->fd, "w");
X	  PyObject * new_sysout = PyFile_FromFile (out, "<stdout>",  
"w", NULL);
X	  PyObject * sys_dict = PyModule_GetDict (sys_module);
X
X	  PyDict_SetItemString (sys_dict, "stdout", new_sysout);
X	}
X    }
X
X  if (exec_the_script (pcsp) == OK)
X    Py_Exit (0);
X  else
X    Py_Exit (SERVER_ERROR);
}
X
/* Handles the script output, fetching the headers first, then  
forwarding it
X   to the server. */
static int
copy_client_result (python_child_stuff * pcsp)
{
X  int ret;
X
X  if ((ret = scan_script_header (pcsp->r, pcsp->script_out)) == 0)
X    {
X      const char * location = table_get (pcsp->r->headers_out,  
"Location");
X
X      if (location && location[0] == '/' && pcsp->r->status == 200)
X	{
X	  char buffer[HUGE_STRING_LEN];
X	
X	  /* Soak up all the script output */
X	  hard_timeout ("read from script", pcsp->r);
X	  while (fgets (buffer, HUGE_STRING_LEN-1,  
pcsp->script_out) != NULL)
X	    continue;
X	  kill_timeout (pcsp->r);
X	  	
X	  /* This redirect needs to be a GET no matter what the original
X	   * method was.
X	   */
X	  pcsp->r->method = pstrdup (pcsp->r->pool, "GET");
X	  pcsp->r->method_number = M_GET;
X	
X	  internal_redirect_handler (location, pcsp->r);
X	  ret = OK;
X	}
X      else if (location && pcsp->r->status == 200)
X	{
X	  /* XX Note that if a script wants to produce its own Redirect
X	   * body, it now has to explicitly *say* "Status: 302"
X	   */
X	  ret = REDIRECT;
X	}
X      else
X	{
X	  hard_timeout ("send script output", pcsp->r);
X	
X	  send_http_header (pcsp->r);
X	  if (!pcsp->r->header_only)
X	    send_fd (pcsp->script_out, pcsp->r);
X	
X	  kill_timeout (pcsp->r);
X	
X	  ret = OK;
X	}
X    }
X
X  return ret;
}
X
/* Forks a new process for the execution of the script. */
static int
execute_python_script (python_child_stuff * pcsp)
{
X  /* Create a child process: if the script claims to be NPH, then do not
X     use a timeout, just wait for it to finish. In this case do not even
X     pass a pointer to script_out, since we will not be interested  
in it,
X     since the script's output is connected directly to the client. */
X
X  if (! spawn_child (pcsp->r->connection->pool,
X		     python_child,
X		     (void *) pcsp,
X		     pcsp->nph ? just_wait : kill_after_timeout,
X		     &pcsp->script_in,
X		     pcsp->nph ? NULL : &pcsp->script_out))
X    {
X      log_reason ("couldn't spawn child process",  
pcsp->r->filename, pcsp->r);
X      return SERVER_ERROR;
X    }
X
X  /* If there are args coming from the client, forward them to the
X     script' stdin */
X  if (pcsp->args_size > 0)	/* M_POST || M_PUT */
X    {
X      void (*handler)();
X      int remaining = pcsp->args_size;
X      char argsbuffer[HUGE_STRING_LEN];
X
X      hard_timeout ("copy script args", pcsp->r);
X      handler = signal (SIGPIPE, SIG_IGN);
X
X      while ((remaining > 0))
X	{
X	  int len_read, len_to_read = remaining;
X	
X	  if (len_to_read > HUGE_STRING_LEN)
X	    len_to_read = HUGE_STRING_LEN;
X	
X	  len_read = read_client_block (pcsp->r, argsbuffer,  
len_to_read);
X	  if (len_read == 0)
X	    break;
X	  fwrite (argsbuffer, 1, len_read, pcsp->script_in);
X	  remaining -= len_read;
X	}
X
X      fflush (pcsp->script_in);
X      signal (SIGPIPE, handler);
X
X      kill_timeout (pcsp->r);
X    }
X
X  /* If the script is NPH our job finishes here; otherwise parse the
X     incoming headers and copy the result to the client. */
X  if (pcsp->nph)
X    return OK;
X  else
X    return copy_client_result (pcsp);
}
X
static int
python_cgi_handler (request_rec * r)
{
X  int ret;
X  python_child_stuff pcs = { r, NULL, NULL, NULL, -1, 0 };
X
X  if ((ret = check (&pcs)) != OK)
X    return ret;
X
X  chdir_file (r->filename);
X
X  add_common_vars (r);
X  add_cgi_vars (r);
X
X  ret = execute_python_script (&pcs);
X
X  return ret;
}
X
static handler_rec python_handlers[] = {
X  { PYTHON_MAGIC_TYPE, python_cgi_handler },
X  { "python-cgi-script", python_cgi_handler },
X  { NULL }
};
X
module python_module = {
X   STANDARD_MODULE_STUFF,
X   NULL,			/* initializer */
X   create_python_dir_config,	/* dir config creater */
X   NULL,			/* dir merger --- default is to  
override */
X   NULL,			/* server config */
X   NULL,			/* merge server config */
X   python_cmds,			/* command table */
X   python_handlers,		/* handlers */
X   NULL,			/* filename translation */
X   NULL,			/* check_user_id */
X   NULL,			/* check auth */
X   NULL,			/* check access */
X   NULL,			/* type_checker */
X   NULL,			/* fixups */
X   NULL				/* logger */
};
X
/* My Python Interpreter is configured to use GNU readline() for reading
X   interactive inputs. Since I do not want to link in the entire  
readline
X   library (and the termcap library too) in the Apache Server  
(where it is
X   not used at all) I patched Python's Makefiles to not include  
`myreadline.o'
X   in the `libParser.a' library. So now I have to furnish a  
simple-minded
X   implementation of this function. */
X
char *
PyOS_Readline (char * prompt)
{
X  int n = 1000;
X  char * p = malloc (n);
X  char * q;
X
X  if (p == NULL)
X    return NULL;
X
X  if (prompt)
X    fprintf (stderr, "%s", prompt);
X
X  q = fgets (p, n, stdin);
X  if (q == NULL)
X    {
X      *p = '\0';
X      return realloc (p, 1);
X    }
X  n = strlen (p);
X  if (n > 0 && p[n-1] != '\n')
X    p[n-1] = '\n';
X  return realloc(p, n+1);
}
SHAR_EOF
  $shar_touch -am 0528121896 'src/mod_python.c' &&
  chmod 0644 'src/mod_python.c' ||
  echo 'restore of src/mod_python.c failed'
  shar_count="`wc -c < 'src/mod_python.c'`"
  test 20299 -eq "$shar_count" ||
    echo "src/mod_python.c: original size 20299, current size  
$shar_count"
fi
# ============= manual/mod_python.tex ==============
if test ! -d 'manual'; then
  echo 'x - creating directory manual'
  mkdir 'manual'
fi
if test -f 'manual/mod_python.tex' && test X"$1" != X"-c"; then
  echo 'x - skipping manual/mod_python.tex (file already exists)'
else
  echo 'x - extracting manual/mod_python.tex (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'manual/mod_python.tex' &&
% This file documents the specific features of the Python Module. It
% should be included in the Apache manual (manual.tex) with an
% `\input' directive right before the `Apache API notes' chapter, thus
% extending the one titled `Apache Extension Modules'.
%
% $RCSfile: mod_python.tex,v $
% $Revision: 1.1 $
% $Date: 1996/05/28 10:16:35 $
%
% Created Tue May 27 09:34:32 1996.
%
\section{Module mod\_python}
This module is contained in the {\tt mod\_python.c} file, and is not
compiled in by default. It allows the server to execute {\bf Python}
Scripts {\it natively}, without the need of an external Python
Interpreter.
\subsection{Summary}
The server normally treats Python Scripts in the same way as any other
CGI Scripts: each of them is feeded to the underlying Operating System,
requesting it to run them in some way, that is usually to launch an
external interpreter (some kind of shell for Shell Scripts, Perl for
Perl Scripts, and so on) and telling it to execute the script.\par
To increase efficency, this module embeds directly in the server the
Python Interpreter, thus allowing the server itself to execute this
kind of scripts.\par
\subsection{Activation}
The module is enabled by putting somewhere in the {\tt srm.conf}
file
\begin{quote}
{\tt AddHandler python/x-httpd-cgi .py}
\end{quote}
to mean that whenever the server is asked for a resource whose name
ends with {\tt .py} it should treat it through this module.
\subsection{Directives}
This module adds three directives that alter in some way the execution
of the scripts. They must be used in the Apache's config files, either
from a {\tt $<$Directory$>$} section in the {\tt access.conf}, or from
defaults in {\tt srm.conf}.
\subsubsection{PythonPath}
\index{{\tt PythonPath} directive}
{\bf Syntax:} PythonPath {\it  
pathnames:to:prepend:to:the:standard:loadpath}\br
{\bf Context:} directory, .htaccess\br
{\bf Status:} Extension\br
{\bf Module:} mod\_python
X
The PythonPath directive lets you add the location of the modules you 
will {\it import} in the scripts. The standard load path is computed
at compile time and is usually {\it
/usr/local/lib/python:/usr/local/lib/python/mach-dep}; with this
directive you may add to the standard path the location of you
modules. Example:\begin{quote}
$<$Directory /usr/local/etc/apache/cgi-bin/lele$>$\br
PythonPath /users/lele/Library/testing-python\br
\end{quote}
adds the given path in front of the standard one when executing Python
Scripts in a particular directory.
\subsubsection{PythonVerbose}
\index{{\tt PythonVerbose} directive}
{\bf Syntax:} PythonVerbose {\it boolean}\br
{\bf Default:} {\tt PythonVerbose off}\br
{\bf Context:} directory, .htaccess\br
{\bf Status:} Extension\br
{\bf Module:} mod\_python
X
This directive enables the verbosity flag of the embedded Python
Interpreter; its use is mainly for debugging purposes, and it is the
equivalent of the Python {\tt -v} command line option. {\it  
Boolean} is either
{\tt on} or {\tt off}. I found it particularly handy to find out that
I was importing an old version of a module instead of the `current'
one: by turning on verbosity you will get a trace of the imports in the
error log file (usually `{\tt logs/error\_log}'):
\begin{quote}
\# /usr/local/lib/python/os.pyc matches /usr/local/lib/python/os.py\par
import os \# precompiled from /usr/local/lib/python/os.pyc\par
import posix \# builtin\par
\# /usr/local/lib/python/posixpath.pyc matches  
/usr/local/lib/python/posixpath.py\par
import posixpath \# precompiled from  
/usr/local/lib/python/posixpath.pyc\par
\# /usr/local/lib/python/stat.pyc matches  
/usr/local/lib/python/stat.py\par
import stat \# precompiled from /usr/local/lib/python/stat.pyc\par
\end{quote}\par
\subsubsection{PythonDebug}
\index{{\tt PythonDebug} directive}
{\bf Syntax:} PythonDebug {\it boolean}\br
{\bf Default:} {\tt PythonDebug off}\br
{\bf Context:} directory, .htaccess\br
{\bf Status:} Extension\br
{\bf Module:} mod\_python
X
This directive enables the debug flag of the embedded Python
Interpreter; its use is mainly for debugging purposes, and it is the
equivalent of the Python {\tt -d} command line option. {\it  
Boolean} is either
{\tt on} or {\tt off}.\par
SHAR_EOF
  $shar_touch -am 0528121696 'manual/mod_python.tex' &&
  chmod 0644 'manual/mod_python.tex' ||
  echo 'restore of manual/mod_python.tex failed'
  shar_count="`wc -c < 'manual/mod_python.tex'`"
  test 4190 -eq "$shar_count" ||
    echo "manual/mod_python.tex: original size 4190, current size  
$shar_count"
fi
exit 0

---
   Emanuele (lele) Gaifax    | ...calling Emacs convenient, of course, is
       lele@eclipse.it       | like calling oxygen useful   -- Rens  
Troost

--
   Emanuele (lele) Gaifax    | ...calling Emacs convenient, of course, is
       lele@eclipse.it       | like calling oxygen useful   -- Rens Troost