A few weeks back I created a JIRA entry relating to integrating server
side includes with Python. The entry is:

  http://issues.apache.org/jira/browse/MODPYTHON-104

I finally got around to having a go at implementing it and have some
initial code now working. The point of this email is to get feedback on
the approach used and whether anyone has any alternate suggestions of
how it should in practice work, or any other ideas for that matter.

For those who might not know what server side includes are all about,
see:

  http://httpd.apache.org/docs/2.0/mod/mod_include.html

Effectively, what it is is an Apache output filter that will look for
SGML (HTML/XML) comments in content returned by a handler or as may be
found in a static file. It will identify specially tagged comments and
process them to yield new content which will be inserted in place in the
output instead of the original comment. The filter even supports
conditionals and is able to trigger sub requests and CGI scripts.

To cater for other sources of input, the filter can be extended to
support additional tags. For example, mod_perl adds the tag "perl",
allowing one to use:

  <!--#perl sub="MySSI::remote_host" -->

  <!--#perl arg="Hello" arg="SSI" arg="World"
         sub="sub {
                  my($r, @args) = @_;
                  print qq(@args);
              }"
  -->

Now, there is no reason that support for Python can't also be added and
it is actually quite straightforward to do. The only question that needs
to be answered is how it would be used. What one would get out of it is
a simple template mechanism which uses only mod_python and existing
features of Apache itself, and which can also be used in conjunction
with other modules like mod_perl as well.

Now for how the syntax would work. What I have got going at the moment
are the following two scenarios:

  <html>
    <body>
      <pre>

      <!--#python eval="filter.req.subprocess_env['SERVER_ADMIN']" -->

<!--#python exec="filter.write(filter.req.subprocess_env['SERVER_ADMIN'])" --> <!--#python exec="print >> filter, filter.req.subprocess_env['SERVER_ADMIN']" -->

      </pre>
    </body>
  </html>

This first will run "eval" with the result being converted into a string
and included direct into the output.

The second will run "exec" instead. In this case there is no result and
thus filter.write() has to be used to generate the output.

In both cases, the "filter" object is pushed into the local variable
set and would be accessible.

When using "exec", multiple lines of Python code can be provided. As
usual Python indentation is always fun, and as such, when providing
multiple lines of code, you can't have leading whitespace unless it is
truly required for nesting.

  <html>
    <body>
      <pre>

      <!--#python exec="
  for key in filter.req.subprocess_env:
      print >> filter, key, filter.req.subprocess_env[key]
      "-->

      </pre>
    </body>
  </html>

Having to include all the code in the HTML isn't fun and it is actually
better to separate it out anyway.

To do that, one simply has to perform an import of the required module
and execute some function contained in it or access data and write it out.

    <!--#python exec="
import sys
print >> filter, sys.version
    "-->

If the target module is already the subject of automatic module reloading
as implemented by mod_python, you might instead use:

    <!--#python exec="
from mod_python import apache
module = apache.import_module("example")
module.output_header(filter)
    "-->

For "eval", one could provide a short cut mechanism whereby one could specify
the module in which the code should be evaluated.

    <!--#python module="sys" eval="version" -->

Ie., this is essentially equivalent to having said:

  from mod_python import apache
  module = apache.import_module("sys")
  filter.write(str(eval("version", module.__dict__, {"filter":filter})))

The "module" short cut wouldn't exist for "exec" as any module imports then performed would pollute the global name space of the specified module, which
may not be desirable.

As you can see, various things are possible but what is the minimum that
should be provided?

In the interests of discouraging a lot of code inside of a HTML file and
thus protecting people from themselves, should the "exec" variant above
be discarded, leaving just the "eval" variant? This would have the effect
of forcing users to put complex code into separate modules and simply
triggering calls of the separate code.

    <!--#python module="example" eval="output_header(filter)" -->

The only hard bit is how one should treat the response from the function
called. One could say that if the result of the eval is 'None' then no
output is written and if it was a function which was called, it is assumed
that the called function did that by writing back to the filter object.

In other words, the result of the eval isn't simply converted to a string. This would only be done if result is not 'None', which 'None' truly meaning
no output.

Anyway, that is all there is to it.

Given that the intent here is to try and get this functionality rolled into mod_python, any feedback would be most appreciated. No consensus probably
means it would get rejected.

Thanks.

Graham



Reply via email to