├── COPYRIGHT ├── CREDITS ├── Doc ├── Makefile.in ├── about.rst ├── changes.rst ├── commandline.rst ├── conf.py ├── copyright.rst ├── directives.rst ├── handlers.rst ├── index.rst ├── installation.rst ├── introduction.rst ├── license.rst ├── pythonapi.rst ├── release-instructions.txt ├── ssi.rst ├── tools │ ├── roman.py │ ├── rstlint.py │ ├── serve.py │ ├── sphinx-build.py │ └── sphinxext │ │ └── suspicious.py └── tutorial.rst ├── LICENSE ├── Makefile.in ├── NEWS ├── NOTICE ├── README.md ├── configure ├── configure.in ├── dist ├── Makefile.in ├── README ├── build_installer.bat ├── setup.py.in ├── version.sh └── win32_postinstall.py ├── examples └── gzipfilter.py ├── install-sh ├── lib └── python │ └── mod_python │ ├── Cookie.py │ ├── Session.py │ ├── __init__.py │ ├── apache.py │ ├── cache.py │ ├── cgihandler.py │ ├── httpdconf.py │ ├── psp.py │ ├── publisher.py │ ├── python22.py │ ├── testhandler.py │ ├── util.py │ └── wsgi.py ├── scripts ├── Makefile.in └── mod_python.in ├── src ├── Makefile.in ├── Version.rc ├── _apachemodule.c ├── _pspmodule.c ├── connobject.c ├── filterobject.c ├── finfoobject.c ├── hlist.c ├── hlistobject.c ├── include │ ├── _apachemodule.h │ ├── _pspmodule.h │ ├── connobject.h │ ├── filterobject.h │ ├── finfoobject.h │ ├── hlist.h │ ├── hlistobject.h │ ├── mod_python.h │ ├── mod_python.h.in │ ├── mp_version.h │ ├── psp_flex.h │ ├── psp_parser.h │ ├── psp_string.h │ ├── requestobject.h │ ├── serverobject.h │ ├── tableobject.h │ └── util.h ├── mod_python.c ├── mod_python.vcproj ├── psp_parser.c ├── psp_parser.l ├── psp_string.c ├── requestobject.c ├── serverobject.c ├── tableobject.c └── util.c └── test ├── Makefile.in ├── README ├── conf └── mime.types ├── htdocs ├── cgitest.py ├── dummymodule.py ├── index.py ├── psp_parser.psp ├── psptest.psp ├── psptest_fail.psp ├── psptest_main.psp ├── ssi.shtml ├── subdir │ └── dummy.txt ├── tests.py └── wsgitest.py └── test.py /COPYRIGHT: -------------------------------------------------------------------------------- 1 | 2 | See LICENSE file. 3 | 4 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grisha/mod_python/188b55d24ae4590586dbf4f8750899f2ae3b83fa/CREDITS -------------------------------------------------------------------------------- /Doc/Makefile.in: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Makefile for Python documentation 4 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | # 6 | 7 | # You can set these variables from the command line. 8 | PYTHON = @PYTHON_BIN@ 9 | SVNROOT = http://svn.python.org/projects 10 | SPHINXOPTS = 11 | PAPER = 12 | SOURCES = 13 | DISTVERSION = $(shell ../dist/version.sh) 14 | 15 | ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees -D latex_paper_size=$(PAPER) \ 16 | $(SPHINXOPTS) . build/$(BUILDER) $(SOURCES) 17 | 18 | .PHONY: help checkout update build html htmlhelp latex text changes linkcheck \ 19 | suspicious coverage htmlview clean dist check serve \ 20 | autobuild-dev autobuild-stable 21 | 22 | help: 23 | @echo "Please use \`make ' where is one of" 24 | @echo " clean to remove build files" 25 | @echo " update to update build tools" 26 | @echo " html to make standalone HTML files" 27 | @echo " htmlhelp to make HTML files and a HTML help project" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter (NB: you'll need to backslash escape the _ in Mod_python in the .tex file)" 29 | @echo " text to make plain text files" 30 | @echo " changes to make an overview over all changed/added/deprecated items" 31 | @echo " linkcheck to check all external links for integrity" 32 | @echo " coverage to check documentation coverage for library and C API" 33 | @echo " dist to create a \"dist\" directory with archived docs for download" 34 | @echo " suspicious to check for suspicious markup in output text" 35 | @echo " check to run a check for frequent markup errors" 36 | @echo " serve to serve the documentation on the localhost (8000)" 37 | 38 | # Note: if you update versions here, do the same in make.bat and README.txt 39 | checkout: 40 | @if [ ! -d tools/sphinx ]; then \ 41 | echo "Checking out Sphinx..."; \ 42 | svn checkout $(SVNROOT)/external/Sphinx-1.0.7/sphinx tools/sphinx; \ 43 | fi 44 | @if [ ! -d tools/docutils ]; then \ 45 | echo "Checking out Docutils..."; \ 46 | svn checkout $(SVNROOT)/external/docutils-0.6/docutils tools/docutils; \ 47 | fi 48 | @if [ ! -d tools/jinja2 ]; then \ 49 | echo "Checking out Jinja..."; \ 50 | svn checkout $(SVNROOT)/external/Jinja-2.3.1/jinja2 tools/jinja2; \ 51 | fi 52 | @if [ ! -d tools/pygments ]; then \ 53 | echo "Checking out Pygments..."; \ 54 | svn checkout $(SVNROOT)/external/Pygments-1.3.1/pygments tools/pygments; \ 55 | fi 56 | 57 | update: clean checkout 58 | 59 | build: checkout 60 | mkdir -p build/$(BUILDER) build/doctrees 61 | $(PYTHON) tools/sphinx-build.py $(ALLSPHINXOPTS) 62 | @echo 63 | 64 | html: BUILDER = html 65 | html: build 66 | @echo "Build finished. The HTML pages are in build/html." 67 | 68 | htmlhelp: BUILDER = htmlhelp 69 | htmlhelp: build 70 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 71 | "build/htmlhelp/pydoc.hhp project file." 72 | 73 | latex: BUILDER = latex 74 | latex: build 75 | @echo "Build finished; the LaTeX files are in build/latex." 76 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 77 | "run these through (pdf)latex." 78 | 79 | text: BUILDER = text 80 | text: build 81 | @echo "Build finished; the text files are in build/text." 82 | 83 | changes: BUILDER = changes 84 | changes: build 85 | @echo "The overview file is in build/changes." 86 | 87 | linkcheck: BUILDER = linkcheck 88 | linkcheck: build 89 | @echo "Link check complete; look for any errors in the above output" \ 90 | "or in build/$(BUILDER)/output.txt" 91 | 92 | suspicious: BUILDER = suspicious 93 | suspicious: build 94 | @echo "Suspicious check complete; look for any errors in the above output" \ 95 | "or in build/$(BUILDER)/suspicious.csv. If all issues are false" \ 96 | "positives, append that file to tools/sphinxext/susp-ignored.csv." 97 | 98 | coverage: BUILDER = coverage 99 | coverage: build 100 | @echo "Coverage finished; see c.txt and python.txt in build/coverage" 101 | 102 | htmlview: html 103 | $(PYTHON) -c "import webbrowser; webbrowser.open('build/html/index.html')" 104 | 105 | clean: 106 | -rm -rf build/* 107 | -rm -rf tools/sphinx 108 | -rm -rf tools/pygments 109 | -rm -rf tools/jinja2 110 | -rm -rf tools/docutils 111 | 112 | distclean: clean 113 | rm -f Makefile 114 | 115 | dist: 116 | rm -rf dist 117 | mkdir -p dist 118 | 119 | # archive the HTML 120 | make html 121 | cp -pPR build/html dist/mod_python-$(DISTVERSION)-docs-html 122 | tar -C dist -cf dist/mod_python-$(DISTVERSION)-docs-html.tar mod_python-$(DISTVERSION)-docs-html 123 | bzip2 -9 -k dist/mod_python-$(DISTVERSION)-docs-html.tar 124 | (cd dist; zip -q -r -9 mod_python-$(DISTVERSION)-docs-html.zip mod_python-$(DISTVERSION)-docs-html) 125 | rm -r dist/mod_python-$(DISTVERSION)-docs-html 126 | rm dist/mod_python-$(DISTVERSION)-docs-html.tar 127 | 128 | # archive the text build 129 | make text 130 | cp -pPR build/text dist/mod_python-$(DISTVERSION)-docs-text 131 | tar -C dist -cf dist/mod_python-$(DISTVERSION)-docs-text.tar mod_python-$(DISTVERSION)-docs-text 132 | bzip2 -9 -k dist/mod_python-$(DISTVERSION)-docs-text.tar 133 | (cd dist; zip -q -r -9 mod_python-$(DISTVERSION)-docs-text.zip mod_python-$(DISTVERSION)-docs-text) 134 | rm -r dist/mod_python-$(DISTVERSION)-docs-text 135 | rm dist/mod_python-$(DISTVERSION)-docs-text.tar 136 | 137 | # archive the A4 latex 138 | rm -rf build/latex 139 | make latex PAPER=a4 140 | -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile 141 | (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) 142 | cp build/latex/docs-pdf.zip dist/mod_python-$(DISTVERSION)-docs-pdf-a4.zip 143 | cp build/latex/docs-pdf.tar.bz2 dist/mod_python-$(DISTVERSION)-docs-pdf-a4.tar.bz2 144 | 145 | # archive the letter latex 146 | rm -rf build/latex 147 | make latex PAPER=letter 148 | -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile 149 | (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) 150 | cp build/latex/docs-pdf.zip dist/mod_python-$(DISTVERSION)-docs-pdf-letter.zip 151 | cp build/latex/docs-pdf.tar.bz2 dist/mod_python-$(DISTVERSION)-docs-pdf-letter.tar.bz2 152 | 153 | check: 154 | $(PYTHON) tools/rstlint.py -i tools 155 | 156 | serve: 157 | ./tools/serve.py build/html 158 | 159 | # Targets for daily automated doc build 160 | 161 | # for development releases: always build 162 | autobuild-dev: 163 | make update 164 | make dist SPHINXOPTS='-A daily=1 -A versionswitcher=1' 165 | 166 | # for quick rebuilds (HTML only) 167 | autobuild-html: 168 | make html SPHINXOPTS='-A daily=1 -A versionswitcher=1' 169 | 170 | # for stable releases: only build if not in pre-release stage (alpha, beta, rc) 171 | autobuild-stable: 172 | @case $(DISTVERSION) in *[abc]*) \ 173 | echo "Not building; $(DISTVERSION) is not a release version."; \ 174 | exit 1;; \ 175 | esac 176 | @make autobuild-dev 177 | 178 | -------------------------------------------------------------------------------- /Doc/about.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | About these documents 3 | ===================== 4 | 5 | 6 | These documents are generated from `reStructuredText`_ sources by `Sphinx`_, a 7 | document processor specifically written for the Python documentation. 8 | 9 | .. _reStructuredText: http://docutils.sf.net/rst.html 10 | .. _Sphinx: http://sphinx.pocoo.org/ 11 | 12 | 13 | -------------------------------------------------------------------------------- /Doc/commandline.rst: -------------------------------------------------------------------------------- 1 | .. _cmd: 2 | 3 | ****************************** 4 | Command Line Tool - mod_python 5 | ****************************** 6 | 7 | .. _cmd-overview: 8 | 9 | Overview of mod_python command 10 | ============================== 11 | 12 | mod_python includes a command-line tool named ``mod_python``. The 13 | ``mod_python`` command exists to facilitate tasks related to 14 | configuration and management of mod_python. 15 | 16 | The general syntax for the command is ``mod_python `` 17 | where ```` is a separate tool with its own argument requirements. 18 | 19 | .. _cmd-subcommands: 20 | 21 | mod_python command line tool sub-commands 22 | ========================================= 23 | 24 | create 25 | ------ 26 | 27 | ``create`` sub-command creates a simple Apache configuration and a 28 | skeleton directory structure necessary for placement of configuration, 29 | logs and content. It is meant to be executed only once per lifetime of 30 | a project. 31 | 32 | The configuration generated by ``create`` consists of an 33 | :mod:`httpdconf` based version (in Python) which can then be used to 34 | generate an actual Apache configuration (by using the ``genconfig`` 35 | subcommand or simply executing the config files itself). The idea is 36 | that the Apache configuration is always generated and the Python 37 | version is the one meant for editing/adjustments. 38 | 39 | The ``create`` subcommand will create the necessary files and 40 | directories if they do not exist, but will not overwrite any existing 41 | files or directories only producing a warning when a file or directory 42 | already exists. It will abort if the Python version of the 43 | configuration file already exists. 44 | 45 | ``create`` requires a single argument: the distination directory, 46 | Apache ``ServerRoot``. 47 | 48 | ``create`` has the following command options: 49 | 50 | .. cmdoption:: --listen 51 | 52 | A string describing the port and optional IP address on which the 53 | server is to listen for incoming requests in the form 54 | ``[ip_address:]port`` The argument will be applied to the Apache 55 | ``Listen`` directive as is and therefore must be syntactically 56 | compatible with it. 57 | 58 | .. cmdoption:: --pythonpath 59 | 60 | A colon (``":"``) separate list of paths to be applied to the 61 | :ref:`dir-other-pp` directive. 62 | 63 | .. cmdoption:: --pythonhandler 64 | 65 | The name of the Python handler to use. Applied to the 66 | :ref:`dir-handlers-ph` directive. 67 | 68 | .. cmdoption:: --pythonoption 69 | 70 | An option to be specified in the configuration. Multiple options 71 | are alowed. Applied to the :ref:`dir-other-po` directive. 72 | 73 | .. _cmd-sub-create-example: 74 | 75 | genconfig 76 | --------- 77 | 78 | This sub-command exists to facilitate re-generation of an Apache 79 | configuration from a Python-based one. All it does is run the script, 80 | but its use is recommended because the mod_python command will execute 81 | the correct version of Python under which mod_python was initially 82 | compiled. Example:: 83 | 84 | mod_python genconfig /path/to/server_root/httpd_conf.py > /path/to/server_root/httpd.conf 85 | 86 | start 87 | ----- 88 | 89 | Starts an Apache instance. Requires a single argument, the path to 90 | Apache configuration file. 91 | 92 | stop 93 | ---- 94 | 95 | Stops an Apache instance (using graceful-stop). Requires a single 96 | argument, the path to Apache configuration file. 97 | 98 | restart 99 | ------- 100 | 101 | Stops an Apache instance (using graceful). Requires a single argument, 102 | the path to Apache configuration file. 103 | 104 | version 105 | ------- 106 | 107 | This sub-command prints out version and location information about 108 | this mod_python installation, the Apache HTTP Server and Python used 109 | when building this mod_python instance. 110 | 111 | Example 112 | ------- 113 | 114 | To create an Apache instance with all the required directories for a 115 | WSGI application which is located in ``/path/to/myapp`` and defined in 116 | ``/path/to/myapp/myapp/myapp/wsgi.py``, run the following:: 117 | 118 | mod_python create /path/to/new/server_root \ 119 | --pythonpath=/path/to/my/app \ 120 | --pythonhandler=mod_python.wsgi \ 121 | --pythonoption="mod_python.wsgi.application myapp.wsgi::application" 122 | 123 | The above example will create a Python-based configuration in 124 | ``/path/to/new/server_root/conf/http_conf.py`` which is a simple 125 | Python script. When executed, the output of the script becomes an 126 | Apache configuration (``create`` will take care of generating the 127 | first Apache config for you). 128 | 129 | You should be able to run this Apache instance by executing:: 130 | 131 | mod_python start /path/to/new/server_root/conf/httpd.conf 132 | -------------------------------------------------------------------------------- /Doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Documentation build configuration file 4 | # 5 | # This file is execfile()d with the current directory set to its containing dir. 6 | # 7 | # The contents of this file are pickled, so don't put values in the namespace 8 | # that aren't pickleable (module imports are okay, they're removed automatically). 9 | 10 | import sys, os, time 11 | sys.path.append(os.path.abspath('tools/sphinxext')) 12 | 13 | 14 | # General configuration 15 | # --------------------- 16 | 17 | # General substitutions. 18 | project = 'Mod_python' 19 | copyright = '1990-%s, Apache Software Foundation, Gregory Trubetskoy' % time.strftime('%Y') 20 | 21 | # The default replacements for |version| and |release|. 22 | # 23 | # The short X.Y version. 24 | # version = '2.6' 25 | # The full version, including alpha/beta/rc tags. 26 | # release = '2.6a0' 27 | 28 | # version 29 | import subprocess 30 | v, r = subprocess.check_output( 31 | os.path.dirname(__file__) + "/../dist/version.sh", encoding='ASCII' 32 | ).rsplit('.', 1) 33 | version, release = v, v+'.'+r 34 | 35 | # Ignore .rst in Sphinx its self. 36 | exclude_trees = ['tools/sphinx'] 37 | 38 | # If true, '()' will be appended to :func: etc. cross-reference text. 39 | add_function_parentheses = True 40 | 41 | # If true, the current module name will be prepended to all description 42 | # unit titles (such as .. function::). 43 | add_module_names = True 44 | 45 | 46 | # Options for HTML output 47 | # ----------------------- 48 | 49 | html_theme = 'default' 50 | html_theme_options = {'collapsiblesidebar': True} 51 | 52 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 53 | # using the given strftime format. 54 | html_last_updated_fmt = '%b %d, %Y' 55 | 56 | # If true, SmartyPants will be used to convert quotes and dashes to 57 | # typographically correct entities. 58 | html_use_smartypants = True 59 | 60 | # Output file base name for HTML help builder. 61 | htmlhelp_basename = 'mod_python' + release.replace('.', '') 62 | 63 | # Split the index 64 | html_split_index = True 65 | 66 | 67 | # Options for LaTeX output 68 | # ------------------------ 69 | 70 | # The paper size ('letter' or 'a4'). 71 | latex_paper_size = 'a4' 72 | 73 | # The font size ('10pt', '11pt' or '12pt'). 74 | latex_font_size = '10pt' 75 | 76 | # Grouping the document tree into LaTeX files. List of tuples 77 | # (source start file, target name, title, author, document class [howto/manual]). 78 | _stdauthor = r'Gregory Trubetskoy' 79 | latex_documents = [ 80 | ('contents', 'contents.tex', 81 | 'Mod_python Documentation', _stdauthor, 'manual'), 82 | ] 83 | 84 | # Documents to append as an appendix to all manuals. 85 | latex_appendices = ['about', 'license', 'copyright'] 86 | 87 | # Get LaTeX to handle Unicode correctly 88 | latex_elements = {'inputenc': r'\usepackage[utf8x]{inputenc}', 'utf8extra': ''} 89 | -------------------------------------------------------------------------------- /Doc/copyright.rst: -------------------------------------------------------------------------------- 1 | ********* 2 | Copyright 3 | ********* 4 | 5 | Mod_python and this documentation is: 6 | 7 | Copyright © 2000, 2001, 2013, 2024 Gregory Trubetskoy 8 | 9 | Copyright © 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 10 | 11 | ------- 12 | 13 | See :ref:`history-and-license` for complete license and permissions information. 14 | -------------------------------------------------------------------------------- /Doc/index.rst: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | Mod_python Documentation 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | 5 | This document aims to be the only necessary and authoritative source 6 | of information about mod_python, usable as a comprehensive reference, 7 | a user guide and a tutorial all-in-one. 8 | 9 | .. seealso:: 10 | 11 | `Python Language Web Site `_ 12 | for information on the Python language 13 | 14 | `Apache HTTP Server Project Web Site `_ 15 | for information on the Apache server 16 | 17 | .. toctree:: 18 | 19 | introduction.rst 20 | installation.rst 21 | tutorial.rst 22 | pythonapi.rst 23 | directives.rst 24 | handlers.rst 25 | commandline.rst 26 | ssi.rst 27 | changes.rst 28 | license.rst 29 | about.rst 30 | copyright.rst 31 | -------------------------------------------------------------------------------- /Doc/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | ************ 4 | Installation 5 | ************ 6 | 7 | .. note:: 8 | 9 | By far the best place to get help with installation and other issues 10 | is the mod_python mailing list. Please take a moment to join the 11 | mod_python mailing list by sending an e-mail with the word 12 | "subscribe" in the subject to mod_python-request@modpython.org or visit the 13 | `mod_python mailing list page `_ 14 | 15 | 16 | .. _inst-prerequisites: 17 | 18 | Prerequisites 19 | ============= 20 | 21 | In the ideal case your Operating System provides a pre-packaged 22 | version of mod_python. If not, you will need to compile it 23 | yourself. This version of mod_python requires: 24 | 25 | * Python 2 (2.6 and up) or Python 3 (3.3 and up). 26 | * Apache 2.2 or later. Apache 2.4 is highly recommended over 2.2. 27 | 28 | In order to compile mod_python you will need to have the include files 29 | for both Apache and Python, as well as the Python library installed on 30 | your system. If you installed Python and Apache from source, then you 31 | already have everything needed. However, if you are using pre-packaged 32 | software then you may need to install the "development" packages 33 | which contain the include files and libraries necessary to compile 34 | mod_python. Please check your OS documentation for specifics. (Hint: 35 | look for packages named python-devel or python-dev and apache-devel or 36 | apache-dev or httpd-dev, etc.). 37 | 38 | .. _inst-compiling: 39 | 40 | Compiling 41 | ========= 42 | 43 | .. _inst-configure: 44 | 45 | Running :file:`./configure` 46 | --------------------------- 47 | 48 | The :file:`./configure` script will analyze your environment and 49 | create custom Makefiles particular to your system. Aside from all the 50 | standard autoconf stuff, :file:`./configure` does the following: 51 | 52 | .. index:: 53 | single: apxs 54 | pair: ./configure; --with-apxs 55 | 56 | * Finds out whether a program called :program:`apxs` is available. This 57 | program is part of the standard Apache distribution, and is required 58 | for compilation. 59 | 60 | You can manually specify the location of apxs by using the 61 | :option:`with-apxs` option, e.g.:: 62 | 63 | $ ./configure --with-apxs=/usr/local/apache/bin/apxs 64 | 65 | It is recommended that you specify this option. 66 | 67 | .. index:: 68 | single: libpython.a 69 | pair: ./configure; --with-python 70 | 71 | * Checks your Python version and attempts to figure out where 72 | :file:`libpython` is by looking at various parameters compiled into 73 | your Python binary. By default, it will use the :program:`python` 74 | program found in your :envvar:`PATH`. 75 | 76 | If the first Python binary in the path is not suitable or not the one 77 | desired for mod_python, you can specify an alternative location with the 78 | :option:`with-python` option, e.g.:: 79 | 80 | $ ./configure --with-python=/usr/local/bin/python2.3 81 | 82 | .. index:: 83 | pair: ./configure; --with-mutex-dir 84 | 85 | * Sets the directory for the apache mutex locks (if the mutex 86 | mechanism chosen by APR requires one). 87 | 88 | Note: mutex locks are used only by :ref:`mod_python Sessions ` and 89 | :ref:`PSP ` (which maintains a Session implicitly). If you're 90 | not using mod_python Sessions or PSP, then this setting should not 91 | matter. 92 | 93 | Default is :file:`/tmp`. The directory must exist and be 94 | writable by the owner of the apache process. 95 | 96 | Use :option:`with-mutex-dir` option, e.g:: 97 | 98 | $ ./configure --with-mutex-dir=/var/run/mod_python 99 | 100 | The mutex directory can also be specified at run time using 101 | :ref:`dir-other-po` ``mod_python.mutex_directory``. 102 | See :ref:`inst-apacheconfig`. 103 | 104 | *New in version 3.3.0* 105 | 106 | .. index:: 107 | pair: ./configure; --with-max-locks 108 | 109 | * Sets the maximum number of mutex locks reserved by mod_python. 110 | 111 | Note: mutex locks are used only by :ref:`mod_python Sessions ` and 112 | :ref:`PSP ` (which maintains a Session implicitly). If you're 113 | not using mod_python Sessions or PSP, then this setting should not 114 | matter. 115 | 116 | The mutexes used for locking are a limited resource on some 117 | systems. Increasing the maximum number of locks may increase performance 118 | when using session locking. The default is 8. A reasonable number for 119 | higher performance would be 32. 120 | Use :option:`with-max-locks` option, e.g:: 121 | 122 | $ ./configure --with-max-locks=32 123 | 124 | The number of locks can also be specified at run time using 125 | :ref:`dir-other-po` ``mod_python.mutex_locks``. 126 | See :ref:`inst-apacheconfig`. 127 | 128 | *New in version 3.2.0* 129 | 130 | .. index:: 131 | single: flex 132 | pair: ./configure; --with-flex 133 | 134 | * Attempts to locate :program:`flex` and determine its version. 135 | If :program:`flex` cannot be found in your :envvar:`PATH` :program:`configure` 136 | will fail. If the wrong version is found :program:`configure` will generate a warning. 137 | You can generally ignore this warning unless you need to re-create 138 | :file:`src/psp_parser.c`. 139 | 140 | The parser used by psp (See :ref:`pyapi-psp`) is written in C 141 | generated using :program:`flex`. (This requires a reentrant version 142 | of :program:`flex`, 2.5.31 or later). 143 | 144 | If the first flex binary in the path is not suitable or not the one desired 145 | you can specify an alternative location with the option:with-flex: 146 | option, e.g:: 147 | 148 | $ ./configure --with-flex=/usr/local/bin/flex 149 | 150 | *New in version 3.2.0* 151 | 152 | .. _inst-make: 153 | 154 | Running :file:`make` 155 | -------------------- 156 | 157 | .. index:: 158 | single: make 159 | 160 | * To start the build process, simply run:: 161 | 162 | $ make 163 | 164 | .. _inst-installing: 165 | 166 | Installing 167 | ========== 168 | 169 | .. _inst-makeinstall: 170 | 171 | .. index:: 172 | pair: make; install 173 | 174 | Running :file:`make install` 175 | 176 | * This part of the installation in most cases needs to be done as root:: 177 | 178 | $ sudo make install 179 | 180 | * This will copy the mod_python library (:file:`mod_python.so`) into your Apache 181 | :file:`libexec` or :file:`modules` directory, where all the other modules are. 182 | 183 | * Lastly, it will install the Python libraries in 184 | :file:`site-packages` and compile them. 185 | 186 | .. index:: 187 | pair: make targets; install_py_lib 188 | pair: make targets; install_dso 189 | 190 | .. note:: 191 | 192 | If you wish to selectively install just the Python libraries 193 | or the DSO (mod_python.so) (which may not always require superuser 194 | privileges), you can use the following :program:`make` targets: 195 | :option:`install_py_lib` and :option:`install_dso`. 196 | 197 | .. _inst-apacheconfig: 198 | 199 | Configuring Apache 200 | ================== 201 | 202 | .. index:: 203 | pair: LoadModule; apache configuration 204 | single: mod_python.so 205 | 206 | * *LoadModule* 207 | 208 | You need to configure Apache to load the module by adding the 209 | following line in the Apache configuration file, usually called 210 | :file:`httpd.conf` or :file:`apache.conf`:: 211 | 212 | LoadModule python_module libexec/mod_python.so 213 | 214 | The actual path to :program:`mod_python.so` may vary, but :program:`make install` 215 | should report at the very end exactly where :program:`mod_python.so` 216 | was placed and how the ``LoadModule`` directive should appear. 217 | 218 | * See :ref:`inst-testing` below for more basic configuration parameters. 219 | 220 | 221 | .. _inst-testing: 222 | 223 | Testing 224 | ======= 225 | 226 | #. Make a directory that would be visible on your web site, e.g. ``htdocs/test``. 227 | 228 | #. Add the following configuration directives to the main server config file:: 229 | 230 | 231 | AddHandler mod_python .py 232 | PythonHandler mptest 233 | PythonDebug On 234 | 235 | 236 | (Substitute ``/some/directory`` above for something applicable to 237 | your system, usually your Apache ServerRoot) 238 | 239 | This configuration can also be specified in an :file:`.htaccess` 240 | file. Note that :file:`.htaccess` configuration is typically 241 | disabled by default, to enable it in a directory specify 242 | ``AllowOverride`` with at least ``FileInfo``. 243 | 244 | #. This causes all requests for URLs ending in ``.py`` to be processed 245 | by mod_python. Upon being handed a request, mod_python looks for 246 | the appropriate *python handler* to handle it. Here, there is a 247 | single ``PythonHandler`` directive defining module ``mptest`` as 248 | the python handler to use. We'll see next how this python handler 249 | is defined. 250 | 251 | #. At this time, if you made changes to the main configuration file, 252 | you will need to restart Apache in order for the changes to take 253 | effect. 254 | 255 | #. Edit :file:`mptest.py` file in the :file:`htdocs/test` directory so 256 | that is has the following lines (be careful when cutting and 257 | pasting from your browser, you may end up with incorrect 258 | indentation and a syntax error):: 259 | 260 | from mod_python import apache 261 | 262 | def handler(req): 263 | req.content_type = 'text/plain' 264 | req.write("Hello World!") 265 | return apache.OK 266 | 267 | #. Point your browser to the URL referring to the :file:`mptest.py`; 268 | you should see ``'Hello World!'``. If you didn't - refer to the 269 | troubleshooting section next. 270 | 271 | #. Note that according to the configuration written above, you can 272 | point your browser to *any* URL ending in .py in the test 273 | directory. Therefore pointing your browser to 274 | :file:`/test/foobar.py` will be handled exactly the same way by 275 | :file:`mptest.py`. This is because the code in the ``handler`` 276 | function does not bother examining the URL and always acts the same 277 | way no matter what the URL is. 278 | 279 | #. If everything worked well, move on to Chapter :ref:`tutorial`. 280 | 281 | 282 | .. _inst-trouble: 283 | 284 | Troubleshooting 285 | =============== 286 | 287 | There are a few things you can try to identify the problem: 288 | 289 | * Carefully study the error output, if any. 290 | 291 | * Check the server error log file, it may contain useful clues. 292 | 293 | * Try running Apache from the command line in single process mode:: 294 | 295 | ./httpd -X 296 | 297 | This prevents it from backgrounding itself and may provide some useful 298 | information. 299 | 300 | * Beginning with mod_python 3.2.0, you can use the mod_python.testhandler 301 | to diagnose your configuration. Add this to your :file:`httpd.conf` file:: 302 | 303 | 304 | SetHandler mod_python 305 | PythonHandler mod_python.testhandler 306 | 307 | 308 | Now point your browser to the :file:`/mpinfo` URL 309 | (e.g. :file:`http://localhost/mpinfo`) and note down the information given. 310 | This will help you reporting your problem to the mod_python list. 311 | 312 | * Ask on the `mod_python list `_. 313 | Please make sure to provide specifics such as: 314 | 315 | * mod_python version. 316 | * Your operating system type, name and version. 317 | * Your Python version, and any unusual compilation options. 318 | * Your Apache version. 319 | * Relevant parts of the Apache config, .htaccess. 320 | * Relevant parts of the Python code. 321 | 322 | 323 | -------------------------------------------------------------------------------- /Doc/introduction.rst: -------------------------------------------------------------------------------- 1 | .. _introduction: 2 | 3 | ************ 4 | Introduction 5 | ************ 6 | 7 | .. _performance: 8 | 9 | Performance 10 | =========== 11 | 12 | One of the main advantages of mod_python is the increase in 13 | performance over traditional CGI. Below are results of a very crude 14 | test. The test was done on a 1.2GHz Pentium machine running Red Hat 15 | Linux 7.3. `Ab `_ 16 | was used to poll 4 kinds of scripts, all of which imported the 17 | standard cgi module (because this is how a typical Python cgi script 18 | begins), then output a single word ``'Hello!'``. The results are 19 | based on 10000 requests with concurrency of 1:: 20 | 21 | Standard CGI: 23 requests/s 22 | Mod_python cgihandler: 385 requests/s 23 | Mod_python publisher: 476 requests/s 24 | Mod_python handler: 1203 requests/s 25 | 26 | 27 | .. _apache_api: 28 | 29 | Apache HTTP Server API 30 | ====================== 31 | 32 | Apache processes requests in *phases* (i.e. read the request, parse 33 | headers, check access, etc.). Phases are implemented by 34 | functions called *handlers*. Traditionally, handlers are written in C 35 | and compiled into Apache modules. Mod_python provides a way to extend 36 | Apache functionality by writing Apache handlers in Python. For a 37 | detailed description of the Apache request processing process, see the 38 | `Apache Developer Documentation `_, as well as the 39 | `Mod_python - Integrating Python with Apache `_ 40 | paper. 41 | 42 | Currently only a subset of the Apache HTTP Server API is accessible 43 | via mod_python. It was never the goal of the project to provide a 100% 44 | coverage of the API. Rather, mod_python is focused on the most useful 45 | parts of the API and on providing the most "Pythonic" ways of using 46 | it. 47 | 48 | .. _intro_other: 49 | 50 | Other Features 51 | ============== 52 | 53 | Mod_python also provides a number features that fall in the category 54 | of web development, e.g. a parser for embedding Python in HTML 55 | (:ref:`pyapi-psp`), a handler that maps URL space into modules and 56 | functions (:ref:`hand-pub`), support for session (:ref:`pyapi-sess`) 57 | and cookie (:ref:`pyapi-cookie`) handling. 58 | 59 | .. seealso:: 60 | 61 | `Apache HTTP Server Developer Documentation `_ 62 | for HTTP developer information 63 | 64 | `Mod_python - Integrating Python with Apache `_ 65 | for details on how mod_python interfaces with Apache HTTP Server 66 | -------------------------------------------------------------------------------- /Doc/release-instructions.txt: -------------------------------------------------------------------------------- 1 | Release Instructions 2 | ==================== 3 | 4 | Notes 5 | ----- 6 | 7 | The following instructions are appropriate for version 3.2.0. 8 | Adjust accordingly for a different version. 9 | 10 | The current version/date is in src/include/version.h 11 | 12 | You will need to have TeTeX installed and the python source code 13 | to produce the documentation. 14 | 15 | Instructions 16 | ------------ 17 | 18 | REPOS=http://svn.apache.org/repos/asf/httpd/mod_python 19 | 20 | 1. Create the new branch for the release in svn repository. 21 | svn copy --username USER --password PASS $REPOS/trunk $REPOS/tags/release-3-2-0 -m "Tagged for release" 22 | 23 | 2. Checkout a working copy of the new branch. 24 | cd /tmp 25 | svn co $REPOS/tags/release-3-2-0 mod_python 26 | 27 | 3. Update the version information. 28 | cd /tmp/mod_python/src/include/ 29 | Change the following in mpversion.h: 30 | MPV_MAJOR 31 | MPV_MINOR 32 | MVP_PATCH 33 | MVP_STRING 34 | 35 | cd /tmp/mod_python/lib/python/mod_python 36 | In __init__.py change 37 | version 38 | 39 | Once you've update the version information, checkin the changes with 40 | cd /tmp/mod_python 41 | svn ci -m "updated version infomation" 42 | 43 | 4. Generate the html docs. 44 | cd mod_python 45 | ./configure --with-apxs=`which apxs` --with-python-src=/path/to/python/src 46 | cd Doc 47 | make dist 48 | 49 | 5. Export a working copy to a release copy. 50 | cd /tmp 51 | svn export mod_python mod_python-3.2.0 52 | 53 | 6. Copy the html docs generated in step 3 from your working copy to 54 | your release copy. 55 | cp -r mod_python/doc-html/ mod_python-3.2.0/ 56 | 57 | 7. Create a tarball for the release. 58 | tar czvf mod_python-3.2.0.tgz mod_python-3.2.0 59 | 60 | 8. Generate the pdf file for the website 61 | cd mod_python/Doc 62 | make pdf 63 | 64 | 9. Send Doc/modpython.pdf to the mod_python.org website admin. 65 | 66 | 67 | Hints 68 | ----- 69 | 70 | To sign: gpg -a -b mod_python-3.2.0.win32-py2.3.exe 71 | 72 | -------------------------------------------------------------------------------- /Doc/ssi.rst: -------------------------------------------------------------------------------- 1 | .. _ssi: 2 | 3 | ******************** 4 | Server Side Includes 5 | ******************** 6 | 7 | .. _ssi-overview: 8 | 9 | Overview of SSI 10 | =============== 11 | 12 | SSI (Server Side Includes) are directives that are placed in HTML pages, 13 | and evaluated on the server while the pages are being served. They let you 14 | add dynamically generated content to an existing HTML page, without having 15 | to serve the entire page via a CGI program, or other dynamic technology 16 | such as a mod_python handler. 17 | 18 | SSI directives have the following syntax::: 19 | 20 | 21 | 22 | 23 | It is formatted like an HTML comment, so if you don't have SSI correctly 24 | enabled, the browser will ignore it, but it will still be visible in the 25 | HTML source. If you have SSI correctly configured, the directive will be 26 | replaced with its results. 27 | 28 | For a more thorough description of the SSI mechanism and how to enable it, 29 | see the `SSI tutorial `_ 30 | provided with the Apache documentation. 31 | 32 | Version 3.3 of mod_python introduces support for using Python code within 33 | SSI files. Note that mod_python honours the intent of the Apache 34 | ``IncludesNOEXEC`` option to the ``Options`` directive. That is, if 35 | ``IncludesNOEXEC`` is enabled, then Python code within a SSI file will 36 | not be executed. 37 | 38 | 39 | .. _ssi-python-code: 40 | 41 | Using Python Code 42 | ================= 43 | 44 | The extensions to mod_python to allow Python code to be used in conjunction 45 | with SSI introduces the new SSI directive called ``'python'``. This directive 46 | can be used in two forms, these being ``'eval'`` and ``'exec'`` modes. In the case 47 | of ``'eval'``, a Python expression is used and it is the result of that 48 | expression which is substituted in place into the page.:: 49 | 50 | 51 | 52 | 53 | 54 | Where the result of the expression is not a string, the value will be 55 | automatically converted to a string by applying ``'str()'`` to the value. 56 | 57 | In the case of ``'exec'`` a block of Python code may be included. For any 58 | output from this code to appear in the page, it must be written back 59 | explicitly as being part of the response. As SSI are processed by an Apache 60 | output filter, this is done by using an instance of the mod_python 61 | ``filter`` object which is pushed into the global data set available to 62 | the code.:: 63 | 64 | 68 | 69 | Any Python code within the ``'exec'`` block must have a zero first level 70 | indent. You cannot start the code block with an arbitrary level of indent 71 | such that it lines up with any indenting used for surrounding HTML 72 | elements. 73 | 74 | Although the mod_python ``filter`` object is not a true file object, that 75 | it provides the ``write()`` method is sufficient to allow the ``print`` 76 | statement to be used on it directly. This will avoid the need to explicitly 77 | convert non string objects to a string before being output.:: 78 | 79 | 82 | 83 | 84 | .. _ssi-data-scope: 85 | 86 | Scope of Global Data 87 | ==================== 88 | 89 | Multiple instances of ``'eval'`` or ``'exec'`` code blocks may be used within the 90 | one page. Any changes to or creation of global data which is performed 91 | within one code block will be reflected in any following code blocks. 92 | Global data constitutes variables as well as module imports, function and 93 | class definitions.:: 94 | 95 | 101 | 102 | 103 |
104 |    
105 | 
106 |    
114 |    
115 | 116 | 117 | 118 | 119 | The lifetime of any global data is for the current request only. If data 120 | must persist between requests, it must reside in external modules and as 121 | necessary be protected against multithreaded access in the event that a 122 | multithreaded Apache MPM is used. 123 | 124 | 125 | .. _ssi-globals: 126 | 127 | Pre-populating Globals 128 | ====================== 129 | 130 | Any Python code which appears within the page has to be compiled each time 131 | the page is accessed before it is executed. In other words, the compiled 132 | code is not cached between requests. To limit such recompilation and to 133 | avoid duplication of common code amongst many pages, it is preferable to 134 | pre-populate the global data from within a mod_python handler prior to the 135 | page being processed. 136 | 137 | In most cases, a fixup handler would be used for this purpose. For this to 138 | work, first need to configure Apache so that it knows to call the fixup 139 | handler.:: 140 | 141 | PythonFixupHandler _handlers 142 | 143 | 144 | The implementation of the fixup handler contained in ``_handlers.py`` 145 | then needs to create an instance of a Python dictionary, store that in the 146 | mod_python request object as ``ssi_globals`` and then populate that 147 | dictionary with any data to be available to the Python code executing 148 | within the page.:: 149 | 150 | from mod_python import apache 151 | 152 | import html, time 153 | 154 | def _escape(object): 155 | return html.escape(str(object)) 156 | 157 | def _header(filter): 158 | print >> filter, '...' 159 | 160 | def _footer(filter): 161 | print >> filter, '...' 162 | 163 | def fixuphandler(req): 164 | req.ssi_globals = {} 165 | req.ssi_globals['time'] = time 166 | req.ssi_globals['_escape'] = _escape 167 | req.ssi_globals['_header'] = _header 168 | req.ssi_globals['_footer'] = _footer 169 | return apache.OK 170 | 171 | 172 | This is most useful where it is necessary to insert common information such 173 | as headers, footers or menu panes which are dynamically generated into many 174 | pages.:: 175 | 176 | 179 | 180 | 181 | 182 |
183 |    
184 |    
185 | 186 | 187 | 188 | 189 | .. _ssi-conditionals: 190 | 191 | Conditional Expressions 192 | ======================= 193 | 194 | SSI allows for some programmability in its own right through the use of 195 | conditional expressions. The structure of this conditional construct is::: 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | A test condition can be any sort of logical comparison, either comparing 204 | values to one another, or testing the 'truth' of a particular value. 205 | 206 | The source of variables used in conditional expressions is distinct from 207 | the set of global data used by the Python code executed within a page. 208 | Instead, the variables are sourced from the ``subprocess_env`` table 209 | object contained within the request object. The values of these variables 210 | can be set from within a page using the SSI ``'set'`` directive, or by a range 211 | of other Apache directives within the Apache configuration files such as 212 | ``BrowserMatchNoCase`` and ``SetEnvIf``. 213 | 214 | To set these variables from within a mod_python handler, the 215 | ``subprocess_env`` table object would be manipulated directly through 216 | the request object.:: 217 | 218 | from mod_python import apache 219 | 220 | def fixuphandler(req): 221 | debug = req.get_config().get('PythonDebug', '0') 222 | req.subprocess_env['DEBUG'] = debug 223 | return apache.OK 224 | 225 | 226 | If being done from within Python code contained within the page itself, the 227 | request object would first have to be accessed via the filter object.:: 228 | 229 | 233 | 234 | 235 | 236 | DEBUG ENABLED 237 | 238 | DEBUG DISABLED 239 | 240 | 241 | 242 | 243 | 244 | .. _ssi-output-filter: 245 | 246 | Enabling INCLUDES Filter 247 | ======================== 248 | 249 | SSI is traditionally enabled using the ``AddOutputFilter`` directive in 250 | the Apache configuration files. Normally this would be where the request 251 | mapped to a static file.:: 252 | 253 | AddOutputFilter INCLUDES .shtml 254 | 255 | When mod_python is being used, the ability to dynamically enable output 256 | filters for the current request can instead be used. This could be done 257 | just for where the request maps to a static file, but may just as easily be 258 | carried out where the content of a response is generated dynamically. In 259 | either case, to enable SSI for the current request, the 260 | :meth:`request.add_output_filter` method of the mod_python request object would be 261 | used.:: 262 | 263 | from mod_python import apache 264 | 265 | def fixuphandler(req): 266 | req.add_output_filter('INCLUDES') 267 | return apache.OK 268 | 269 | -------------------------------------------------------------------------------- /Doc/tools/roman.py: -------------------------------------------------------------------------------- 1 | """Convert to and from Roman numerals""" 2 | 3 | __author__ = "Mark Pilgrim (f8dy@diveintopython.org)" 4 | __version__ = "1.4" 5 | __date__ = "8 August 2001" 6 | __copyright__ = """Copyright (c) 2001 Mark Pilgrim 7 | 8 | This program is part of "Dive Into Python", a free Python tutorial for 9 | experienced programmers. Visit http://diveintopython.org/ for the 10 | latest version. 11 | 12 | This program is free software; you can redistribute it and/or modify 13 | it under the terms of the Python 2.1.1 license, available at 14 | http://www.python.org/2.1.1/license.html 15 | """ 16 | 17 | import re 18 | 19 | #Define exceptions 20 | class RomanError(Exception): pass 21 | class OutOfRangeError(RomanError): pass 22 | class NotIntegerError(RomanError): pass 23 | class InvalidRomanNumeralError(RomanError): pass 24 | 25 | #Define digit mapping 26 | romanNumeralMap = (('M', 1000), 27 | ('CM', 900), 28 | ('D', 500), 29 | ('CD', 400), 30 | ('C', 100), 31 | ('XC', 90), 32 | ('L', 50), 33 | ('XL', 40), 34 | ('X', 10), 35 | ('IX', 9), 36 | ('V', 5), 37 | ('IV', 4), 38 | ('I', 1)) 39 | 40 | def toRoman(n): 41 | """convert integer to Roman numeral""" 42 | if not (0 < n < 5000): 43 | raise OutOfRangeError("number out of range (must be 1..4999)") 44 | if int(n) != n: 45 | raise NotIntegerError("decimals can not be converted") 46 | 47 | result = "" 48 | for numeral, integer in romanNumeralMap: 49 | while n >= integer: 50 | result += numeral 51 | n -= integer 52 | return result 53 | 54 | #Define pattern to detect valid Roman numerals 55 | romanNumeralPattern = re.compile(""" 56 | ^ # beginning of string 57 | M{0,4} # thousands - 0 to 4 M's 58 | (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's), 59 | # or 500-800 (D, followed by 0 to 3 C's) 60 | (XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's), 61 | # or 50-80 (L, followed by 0 to 3 X's) 62 | (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's), 63 | # or 5-8 (V, followed by 0 to 3 I's) 64 | $ # end of string 65 | """ ,re.VERBOSE) 66 | 67 | def fromRoman(s): 68 | """convert Roman numeral to integer""" 69 | if not s: 70 | raise InvalidRomanNumeralError('Input can not be blank') 71 | if not romanNumeralPattern.search(s): 72 | raise InvalidRomanNumeralError('Invalid Roman numeral: %s' % s) 73 | 74 | result = 0 75 | index = 0 76 | for numeral, integer in romanNumeralMap: 77 | while s[index:index+len(numeral)] == numeral: 78 | result += integer 79 | index += len(numeral) 80 | return result 81 | -------------------------------------------------------------------------------- /Doc/tools/rstlint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Check for stylistic and formal issues in .rst and .py 5 | # files included in the documentation. 6 | # 7 | # 01/2009, Georg Brandl 8 | 9 | # TODO: - wrong versions in versionadded/changed 10 | # - wrong markup after versionchanged directive 11 | 12 | from __future__ import with_statement 13 | 14 | import os 15 | import re 16 | import sys 17 | import getopt 18 | import subprocess 19 | from os.path import join, splitext, abspath, exists 20 | from collections import defaultdict 21 | 22 | directives = [ 23 | # standard docutils ones 24 | 'admonition', 'attention', 'caution', 'class', 'compound', 'container', 25 | 'contents', 'csv-table', 'danger', 'date', 'default-role', 'epigraph', 26 | 'error', 'figure', 'footer', 'header', 'highlights', 'hint', 'image', 27 | 'important', 'include', 'line-block', 'list-table', 'meta', 'note', 28 | 'parsed-literal', 'pull-quote', 'raw', 'replace', 29 | 'restructuredtext-test-directive', 'role', 'rubric', 'sectnum', 'sidebar', 30 | 'table', 'target-notes', 'tip', 'title', 'topic', 'unicode', 'warning', 31 | # Sphinx custom ones 32 | 'acks', 'attribute', 'autoattribute', 'autoclass', 'autodata', 33 | 'autoexception', 'autofunction', 'automethod', 'automodule', 'centered', 34 | 'cfunction', 'class', 'classmethod', 'cmacro', 'cmdoption', 'cmember', 35 | 'code-block', 'confval', 'cssclass', 'ctype', 'currentmodule', 'cvar', 36 | 'data', 'deprecated', 'describe', 'directive', 'doctest', 'envvar', 'event', 37 | 'exception', 'function', 'glossary', 'highlight', 'highlightlang', 'index', 38 | 'literalinclude', 'method', 'module', 'moduleauthor', 'productionlist', 39 | 'program', 'role', 'sectionauthor', 'seealso', 'sourcecode', 'staticmethod', 40 | 'tabularcolumns', 'testcode', 'testoutput', 'testsetup', 'toctree', 'todo', 41 | 'todolist', 'versionadded', 'versionchanged' 42 | ] 43 | 44 | all_directives = '(' + '|'.join(directives) + ')' 45 | seems_directive_re = re.compile(r'\.\. %s([^a-z:]|:(?!:))' % all_directives) 46 | default_role_re = re.compile(r'(^| )`\w([^`]*?\w)?`($| )') 47 | leaked_markup_re = re.compile(r'[a-z]::[^=]|:[a-z]+:|`|\.\.\s*\w+:') 48 | 49 | 50 | checkers = {} 51 | 52 | checker_props = {'severity': 1, 'falsepositives': False} 53 | 54 | def checker(*suffixes, **kwds): 55 | """Decorator to register a function as a checker.""" 56 | def deco(func): 57 | for suffix in suffixes: 58 | checkers.setdefault(suffix, []).append(func) 59 | for prop in checker_props: 60 | setattr(func, prop, kwds.get(prop, checker_props[prop])) 61 | return func 62 | return deco 63 | 64 | 65 | @checker('.py', severity=4) 66 | def check_syntax(fn, lines): 67 | """Check Python examples for valid syntax.""" 68 | code = ''.join(lines) 69 | if '\r' in code: 70 | if os.name != 'nt': 71 | yield 0, '\\r in code file' 72 | code = code.replace('\r', '') 73 | try: 74 | compile(code, fn, 'exec') 75 | except SyntaxError, err: 76 | yield err.lineno, 'not compilable: %s' % err 77 | 78 | 79 | @checker('.rst', severity=2) 80 | def check_suspicious_constructs(fn, lines): 81 | """Check for suspicious reST constructs.""" 82 | inprod = False 83 | for lno, line in enumerate(lines): 84 | if seems_directive_re.match(line): 85 | yield lno+1, 'comment seems to be intended as a directive' 86 | if '.. productionlist::' in line: 87 | inprod = True 88 | elif not inprod and default_role_re.search(line): 89 | yield lno+1, 'default role used' 90 | elif inprod and not line.strip(): 91 | inprod = False 92 | 93 | 94 | @checker('.py', '.rst') 95 | def check_whitespace(fn, lines): 96 | """Check for whitespace and line length issues.""" 97 | for lno, line in enumerate(lines): 98 | if '\r' in line: 99 | yield lno+1, '\\r in line' 100 | if '\t' in line: 101 | yield lno+1, 'OMG TABS!!!1' 102 | if line[:-1].rstrip(' \t') != line[:-1]: 103 | yield lno+1, 'trailing whitespace' 104 | 105 | 106 | @checker('.rst', severity=0) 107 | def check_line_length(fn, lines): 108 | """Check for line length; this checker is not run by default.""" 109 | for lno, line in enumerate(lines): 110 | if len(line) > 81: 111 | # don't complain about tables, links and function signatures 112 | if line.lstrip()[0] not in '+|' and \ 113 | 'http://' not in line and \ 114 | not line.lstrip().startswith(('.. function', 115 | '.. method', 116 | '.. cfunction')): 117 | yield lno+1, "line too long" 118 | 119 | 120 | @checker('.html', severity=2, falsepositives=True) 121 | def check_leaked_markup(fn, lines): 122 | """Check HTML files for leaked reST markup; this only works if 123 | the HTML files have been built. 124 | """ 125 | for lno, line in enumerate(lines): 126 | if leaked_markup_re.search(line): 127 | yield lno+1, 'possibly leaked markup: %r' % line 128 | 129 | 130 | def main(argv): 131 | usage = '''\ 132 | Usage: %s [-v] [-f] [-s sev] [-i path]* [path] 133 | 134 | Options: -v verbose (print all checked file names) 135 | -f enable checkers that yield many false positives 136 | -s sev only show problems with severity >= sev 137 | -i path ignore subdir or file path 138 | ''' % argv[0] 139 | try: 140 | gopts, args = getopt.getopt(argv[1:], 'vfs:i:') 141 | except getopt.GetoptError: 142 | print usage 143 | return 2 144 | 145 | verbose = False 146 | severity = 1 147 | ignore = [] 148 | falsepos = False 149 | for opt, val in gopts: 150 | if opt == '-v': 151 | verbose = True 152 | elif opt == '-f': 153 | falsepos = True 154 | elif opt == '-s': 155 | severity = int(val) 156 | elif opt == '-i': 157 | ignore.append(abspath(val)) 158 | 159 | if len(args) == 0: 160 | path = '.' 161 | elif len(args) == 1: 162 | path = args[0] 163 | else: 164 | print usage 165 | return 2 166 | 167 | if not exists(path): 168 | print 'Error: path %s does not exist' % path 169 | return 2 170 | 171 | count = defaultdict(int) 172 | out = sys.stdout 173 | 174 | for root, dirs, files in os.walk(path): 175 | # ignore subdirs controlled by svn 176 | if '.svn' in dirs: 177 | dirs.remove('.svn') 178 | 179 | # ignore subdirs in ignore list 180 | if abspath(root) in ignore: 181 | del dirs[:] 182 | continue 183 | 184 | for fn in files: 185 | fn = join(root, fn) 186 | if fn[:2] == './': 187 | fn = fn[2:] 188 | 189 | # ignore files in ignore list 190 | if abspath(fn) in ignore: 191 | continue 192 | 193 | ext = splitext(fn)[1] 194 | checkerlist = checkers.get(ext, None) 195 | if not checkerlist: 196 | continue 197 | 198 | if verbose: 199 | print 'Checking %s...' % fn 200 | 201 | try: 202 | with open(fn, 'r') as f: 203 | lines = list(f) 204 | except (IOError, OSError), err: 205 | print '%s: cannot open: %s' % (fn, err) 206 | count[4] += 1 207 | continue 208 | 209 | for checker in checkerlist: 210 | if checker.falsepositives and not falsepos: 211 | continue 212 | csev = checker.severity 213 | if csev >= severity: 214 | for lno, msg in checker(fn, lines): 215 | print >>out, '[%d] %s:%d: %s' % (csev, fn, lno, msg) 216 | count[csev] += 1 217 | if verbose: 218 | print 219 | if not count: 220 | if severity > 1: 221 | print 'No problems with severity >= %d found.' % severity 222 | else: 223 | print 'No problems found.' 224 | else: 225 | for severity in sorted(count): 226 | number = count[severity] 227 | print '%d problem%s with severity %d found.' % \ 228 | (number, number > 1 and 's' or '', severity) 229 | return int(bool(count)) 230 | 231 | 232 | if __name__ == '__main__': 233 | sys.exit(main(sys.argv)) 234 | -------------------------------------------------------------------------------- /Doc/tools/serve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Small wsgiref based web server. Takes a path to serve from and an 4 | optional port number (defaults to 8000), then tries to serve files. 5 | Mime types are guessed from the file names, 404 errors are raised 6 | if the file is not found. Used for the make serve target in Doc. 7 | ''' 8 | import sys 9 | import os 10 | import mimetypes 11 | from wsgiref import simple_server, util 12 | 13 | def app(environ, respond): 14 | 15 | fn = os.path.join(path, environ['PATH_INFO'][1:]) 16 | if '.' not in fn.split(os.path.sep)[-1]: 17 | fn = os.path.join(fn, 'index.html') 18 | type = mimetypes.guess_type(fn)[0] 19 | 20 | if os.path.exists(fn): 21 | respond('200 OK', [('Content-Type', type)]) 22 | return util.FileWrapper(open(fn)) 23 | else: 24 | respond('404 Not Found', [('Content-Type', 'text/plain')]) 25 | return ['not found'] 26 | 27 | if __name__ == '__main__': 28 | path = sys.argv[1] 29 | port = int(sys.argv[2]) if len(sys.argv) > 2 else 8000 30 | httpd = simple_server.make_server('', port, app) 31 | print "Serving %s on port %s, control-C to stop" % (path, port) 32 | try: 33 | httpd.serve_forever() 34 | except KeyboardInterrupt: 35 | print "\b\bShutting down." 36 | -------------------------------------------------------------------------------- /Doc/tools/sphinx-build.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Sphinx - Python documentation toolchain 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | :copyright: 2007-2010 by Georg Brandl. 7 | :license: Python license. 8 | """ 9 | 10 | import sys 11 | import warnings 12 | 13 | # Get rid of UserWarnings reported by pkg_resources. 14 | warnings.filterwarnings('ignore', category=UserWarning, module='jinja2') 15 | 16 | if __name__ == '__main__': 17 | 18 | if sys.version_info[:3] < (2, 4, 0): 19 | print >>sys.stderr, """\ 20 | Error: Sphinx needs to be executed with Python 2.4 or newer 21 | (If you run this from the Makefile, you can set the PYTHON variable 22 | to the path of an alternative interpreter executable, e.g., 23 | ``make html PYTHON=python2.5``). 24 | """ 25 | sys.exit(1) 26 | 27 | from sphinx import main 28 | sys.exit(main(sys.argv)) 29 | -------------------------------------------------------------------------------- /Doc/tools/sphinxext/suspicious.py: -------------------------------------------------------------------------------- 1 | """ 2 | Try to detect suspicious constructs, resembling markup 3 | that has leaked into the final output. 4 | 5 | Suspicious lines are reported in a comma-separated-file, 6 | ``suspicious.csv``, located in the output directory. 7 | 8 | The file is utf-8 encoded, and each line contains four fields: 9 | 10 | * document name (normalized) 11 | * line number in the source document 12 | * problematic text 13 | * complete line showing the problematic text in context 14 | 15 | It is common to find many false positives. To avoid reporting them 16 | again and again, they may be added to the ``ignored.csv`` file 17 | (located in the configuration directory). The file has the same 18 | format as ``suspicious.csv`` with a few differences: 19 | 20 | - each line defines a rule; if the rule matches, the issue 21 | is ignored. 22 | - line number may be empty (that is, nothing between the 23 | commas: ",,"). In this case, line numbers are ignored (the 24 | rule matches anywhere in the file). 25 | - the last field does not have to be a complete line; some 26 | surrounding text (never more than a line) is enough for 27 | context. 28 | 29 | Rules are processed sequentially. A rule matches when: 30 | 31 | * document names are the same 32 | * problematic texts are the same 33 | * line numbers are close to each other (5 lines up or down) 34 | * the rule text is completely contained into the source line 35 | 36 | The simplest way to create the ignored.csv file is by copying 37 | undesired entries from suspicious.csv (possibly trimming the last 38 | field.) 39 | 40 | Copyright 2009 Gabriel A. Genellina 41 | 42 | """ 43 | 44 | import os 45 | import re 46 | import csv 47 | import sys 48 | 49 | from docutils import nodes 50 | from sphinx.builders import Builder 51 | 52 | detect_all = re.compile(ur''' 53 | ::(?=[^=])| # two :: (but NOT ::=) 54 | :[a-zA-Z][a-zA-Z0-9]+| # :foo 55 | `| # ` (seldom used by itself) 56 | (? don't care 67 | self.issue = issue # the markup fragment that triggered this rule 68 | self.line = line # text of the container element (single line only) 69 | self.used = False 70 | 71 | def __repr__(self): 72 | return '{0.docname},,{0.issue},{0.line}'.format(self) 73 | 74 | 75 | 76 | class dialect(csv.excel): 77 | """Our dialect: uses only linefeed as newline.""" 78 | lineterminator = '\n' 79 | 80 | 81 | class CheckSuspiciousMarkupBuilder(Builder): 82 | """ 83 | Checks for possibly invalid markup that may leak into the output. 84 | """ 85 | name = 'suspicious' 86 | 87 | def init(self): 88 | # create output file 89 | self.log_file_name = os.path.join(self.outdir, 'suspicious.csv') 90 | open(self.log_file_name, 'w').close() 91 | # load database of previously ignored issues 92 | self.load_rules(os.path.join(os.path.dirname(__file__), 93 | 'susp-ignored.csv')) 94 | 95 | def get_outdated_docs(self): 96 | return self.env.found_docs 97 | 98 | def get_target_uri(self, docname, typ=None): 99 | return '' 100 | 101 | def prepare_writing(self, docnames): 102 | pass 103 | 104 | def write_doc(self, docname, doctree): 105 | # set when any issue is encountered in this document 106 | self.any_issue = False 107 | self.docname = docname 108 | visitor = SuspiciousVisitor(doctree, self) 109 | doctree.walk(visitor) 110 | 111 | def finish(self): 112 | unused_rules = [rule for rule in self.rules if not rule.used] 113 | if unused_rules: 114 | self.warn('Found %s/%s unused rules:' % 115 | (len(unused_rules), len(self.rules))) 116 | for rule in unused_rules: 117 | self.info(repr(rule)) 118 | return 119 | 120 | def check_issue(self, line, lineno, issue): 121 | if not self.is_ignored(line, lineno, issue): 122 | self.report_issue(line, lineno, issue) 123 | 124 | def is_ignored(self, line, lineno, issue): 125 | """Determine whether this issue should be ignored.""" 126 | docname = self.docname 127 | for rule in self.rules: 128 | if rule.docname != docname: continue 129 | if rule.issue != issue: continue 130 | # Both lines must match *exactly*. This is rather strict, 131 | # and probably should be improved. 132 | # Doing fuzzy matches with levenshtein distance could work, 133 | # but that means bringing other libraries... 134 | # Ok, relax that requirement: just check if the rule fragment 135 | # is contained in the document line 136 | if rule.line not in line: continue 137 | # Check both line numbers. If they're "near" 138 | # this rule matches. (lineno=None means "don't care") 139 | if (rule.lineno is not None) and \ 140 | abs(rule.lineno - lineno) > 5: continue 141 | # if it came this far, the rule matched 142 | rule.used = True 143 | return True 144 | return False 145 | 146 | def report_issue(self, text, lineno, issue): 147 | if not self.any_issue: self.info() 148 | self.any_issue = True 149 | self.write_log_entry(lineno, issue, text) 150 | self.warn('[%s:%d] "%s" found in "%-.120s"' % ( 151 | self.docname.encode(sys.getdefaultencoding(),'replace'), 152 | lineno, 153 | issue.encode(sys.getdefaultencoding(),'replace'), 154 | text.strip().encode(sys.getdefaultencoding(),'replace'))) 155 | self.app.statuscode = 1 156 | 157 | def write_log_entry(self, lineno, issue, text): 158 | f = open(self.log_file_name, 'ab') 159 | writer = csv.writer(f, dialect) 160 | writer.writerow([self.docname.encode('utf-8'), 161 | lineno, 162 | issue.encode('utf-8'), 163 | text.strip().encode('utf-8')]) 164 | f.close() 165 | 166 | def load_rules(self, filename): 167 | """Load database of previously ignored issues. 168 | 169 | A csv file, with exactly the same format as suspicious.csv 170 | Fields: document name (normalized), line number, issue, surrounding text 171 | """ 172 | self.info("loading ignore rules... ", nonl=1) 173 | self.rules = rules = [] 174 | try: f = open(filename, 'rb') 175 | except IOError: return 176 | for i, row in enumerate(csv.reader(f)): 177 | if len(row) != 4: 178 | raise ValueError( 179 | "wrong format in %s, line %d: %s" % (filename, i+1, row)) 180 | docname, lineno, issue, text = row 181 | docname = docname.decode('utf-8') 182 | if lineno: lineno = int(lineno) 183 | else: lineno = None 184 | issue = issue.decode('utf-8') 185 | text = text.decode('utf-8') 186 | rule = Rule(docname, lineno, issue, text) 187 | rules.append(rule) 188 | f.close() 189 | self.info('done, %d rules loaded' % len(self.rules)) 190 | 191 | 192 | def get_lineno(node): 193 | """Obtain line number information for a node.""" 194 | lineno = None 195 | while lineno is None and node: 196 | node = node.parent 197 | lineno = node.line 198 | return lineno 199 | 200 | 201 | def extract_line(text, index): 202 | """text may be a multiline string; extract 203 | only the line containing the given character index. 204 | 205 | >>> extract_line("abc\ndefgh\ni", 6) 206 | >>> 'defgh' 207 | >>> for i in (0, 2, 3, 4, 10): 208 | ... print extract_line("abc\ndefgh\ni", i) 209 | abc 210 | abc 211 | abc 212 | defgh 213 | defgh 214 | i 215 | """ 216 | p = text.rfind('\n', 0, index) + 1 217 | q = text.find('\n', index) 218 | if q < 0: 219 | q = len(text) 220 | return text[p:q] 221 | 222 | 223 | class SuspiciousVisitor(nodes.GenericNodeVisitor): 224 | 225 | lastlineno = 0 226 | 227 | def __init__(self, document, builder): 228 | nodes.GenericNodeVisitor.__init__(self, document) 229 | self.builder = builder 230 | 231 | def default_visit(self, node): 232 | if isinstance(node, (nodes.Text, nodes.image)): # direct text containers 233 | text = node.astext() 234 | # lineno seems to go backwards sometimes (?) 235 | self.lastlineno = lineno = max(get_lineno(node) or 0, self.lastlineno) 236 | seen = set() # don't report the same issue more than only once per line 237 | for match in detect_all(text): 238 | issue = match.group() 239 | line = extract_line(text, match.start()) 240 | if (issue, line) not in seen: 241 | self.builder.check_issue(line, lineno, issue) 242 | seen.add((issue, line)) 243 | 244 | unknown_visit = default_visit 245 | 246 | def visit_document(self, node): 247 | self.lastlineno = 0 248 | 249 | def visit_comment(self, node): 250 | # ignore comments -- too much false positives. 251 | # (although doing this could miss some errors; 252 | # there were two sections "commented-out" by mistake 253 | # in the Python docs that would not be catched) 254 | raise nodes.SkipNode 255 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 2 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Originally developed by Gregory Trubetskoy. 17 | # 18 | 19 | @SET_MAKE@ 20 | LIBEXECDIR=@LIBEXECDIR@ 21 | AP_SRC=@AP_SRC@ 22 | AP_SRC_OWN=@AP_SRC_OWN@ 23 | AP_SRC_GRP=@AP_SRC_GRP@ 24 | INSTALL=@INSTALL@ 25 | PYTHON_BIN=@PYTHON_BIN@ 26 | 27 | .PHONY: all test clean 28 | 29 | all: @ALL@ 30 | 31 | dso: @DSO@ 32 | 33 | do_dso: 34 | @cd src && $(MAKE) 35 | @cd dist && $(MAKE) build 36 | @echo 37 | @echo 'Now run sudo make install' 38 | @echo ' (or, if you only want to perform a partial install,' 39 | @echo ' you can use make install_dso and make install_py_lib)' 40 | @echo 41 | 42 | no_dso: 43 | @echo 44 | @echo "DSO compilation not available. (Probably because apxs could not be found)." 45 | @echo 46 | 47 | static: @STATIC@ 48 | 49 | no_static: 50 | @echo 51 | @echo "Static compilation not available. (Probably because --with-apache was not specified)." 52 | @echo 53 | 54 | install: 55 | $(MAKE) install_dso 56 | $(MAKE) install_py_lib 57 | cd scripts && $(MAKE) install 58 | 59 | install_dso: dso 60 | @echo 61 | @echo "Performing DSO installation." 62 | @echo 63 | $(INSTALL) -d $(DESTDIR)$(LIBEXECDIR) 64 | $(INSTALL) src/mod_python.so $(DESTDIR)$(LIBEXECDIR) 65 | 66 | install_py_lib: 67 | cd dist && $(MAKE) install_py_lib 68 | 69 | clean: 70 | cd src && $(MAKE) clean 71 | cd dist && $(MAKE) clean 72 | cd scripts && $(MAKE) clean 73 | cd test && $(MAKE) clean 74 | rm -f core 75 | 76 | distclean: clean 77 | cd src && $(MAKE) distclean 78 | cd Doc && $(MAKE) distclean 79 | cd dist && $(MAKE) distclean 80 | cd scripts && $(MAKE) distclean 81 | cd test && $(MAKE) distclean 82 | rm -rf Makefile config.h config.status config.cache config.log \ 83 | test/testconf.py 84 | 85 | test: @ALL@ 86 | cd test && $(MAKE) test 87 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grisha/mod_python/188b55d24ae4590586dbf4f8750899f2ae3b83fa/NEWS -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This product includes software developed by 2 | The Apache Software Foundation (http://www.apache.org/). 3 | 4 | Mod_python was originally developed by Gregory Trubetskoy. 5 | 6 | This software is based on the original concept as published in the 7 | book "Internet Programming with Python" by Aaron Watters, Guido van 8 | Rossum and James C. Ahlstrom, 1996 M&T Books, ISBN: 1-55851-484-8. 9 | The book and original software is Copyright 1996 by M&T Books. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mod_python 2 | ========== 3 | 4 | Documentation for mod_python is on http://www.modpython.org/ 5 | 6 | Mod_python is going to be maintained on GitHub from here on. For issues and questions 7 | please use GitHub. 8 | 9 | Quick Start 10 | ----------- 11 | 12 | If you can't read instructions: 13 | 14 | ```shell 15 | $ ./configure 16 | $ make 17 | $ sudo make install 18 | $ make test 19 | ``` 20 | 21 | If the above worked - read the tutorial in the documentation. 22 | -------------------------------------------------------------------------------- /dist/Makefile.in: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2000, 2001, 2013 Gregory Trubetskoy 2 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Originally developed by Gregory Trubetskoy. 17 | # 18 | # $Id$ 19 | # 20 | 21 | PYTHON_BIN=@PYTHON_BIN@ 22 | 23 | build: mod_python src 24 | @cd src; $(MAKE) psp_parser.c 25 | $(PYTHON_BIN) setup.py build 26 | 27 | # this one requires at least python 2.3 28 | windist: mod_python.so 29 | $(PYTHON_BIN) setup.py bdist_wininst --install-script=win32_postinstall.py 30 | 31 | install: install_py_lib 32 | 33 | # this may require root priviledges 34 | install_py_lib: mod_python src 35 | @cd src; $(MAKE) psp_parser.c 36 | if test -z "$(DESTDIR)" ; then \ 37 | $(PYTHON_BIN) setup.py install --optimize 2 --force ; \ 38 | else \ 39 | $(PYTHON_BIN) setup.py install --optimize 2 --force --root $(DESTDIR) ; \ 40 | fi 41 | 42 | mod_python.so: 43 | @echo "Please place a WIN32 compiled mod_python.so in this directory" 44 | exit 1 45 | 46 | mod_python: 47 | ln -s ../lib/python/mod_python mod_python 48 | 49 | src: 50 | ln -s ../src src 51 | 52 | clean: 53 | rm -rf mod_python build dist 54 | 55 | distclean: 56 | rm -rf mod_python src build dist mod_python.so setup.py Makefile MANIFEST MANIFSET.in 57 | -------------------------------------------------------------------------------- /dist/README: -------------------------------------------------------------------------------- 1 | $Id$ 2 | 3 | This directory contains files necessary for building 4 | mod_python distributions. 5 | 6 | -------------------------------------------------------------------------------- /dist/build_installer.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem Copyright 2004 Apache Software Foundation 3 | rem 4 | rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | rem you may not use this file except in compliance with the License. 6 | rem You may obtain a copy of the License at 7 | rem 8 | rem http://www.apache.org/licenses/LICENSE-2.0 9 | rem 10 | rem Unless required by applicable law or agreed to in writing, software 11 | rem distributed under the License is distributed on an "AS IS" BASIS, 12 | rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | rem See the License for the specific language governing permissions and 14 | rem limitations under the License. 15 | rem 16 | rem Originally developed by Gregory Trubetskoy. 17 | rem 18 | rem $Id$ 19 | rem 20 | rem This script builds the installer for Windows 21 | 22 | rem Test for APACHESRC 23 | if "%APACHESRC%"=="" GOTO NOAPACHESRC 24 | if not exist "%APACHESRC%\include" GOTO BADAPACHESRC 25 | 26 | rem Cleanup 27 | rmdir /s /q build 28 | del /s ..\src\*.obj ..\src\*.lib ..\src\*.exp ..\src\*.res 29 | 30 | rem Build 31 | python setup.py.in bdist_wininst --install-script win32_postinstall.py 32 | GOTO END 33 | 34 | rem Use this instead of the previous line to create a debug build 35 | rem For this you need a Python debug build. The .py files will be installed 36 | rem directly in the Python debug build's site-packages. The .so file will remain 37 | rem in build/lib.win32-2.4, so you'll have to make sure your testconf.py file 38 | rem points to it instead of the copy that may already reside in LIBEXECDIR. 39 | 40 | rem python_d setup.py.in build --debug install 41 | rem GOTO END 42 | 43 | rem Compress the installer if possible 44 | upx.exe --no-color --no-progress --best dist\*.exe 45 | GOTO END 46 | 47 | :BADAPACHESRC 48 | echo Currently APACHESRC points to %APACHESRC% 49 | echo This value seems wrong as we could not find a proper 50 | echo Apache installation here. 51 | 52 | :NOAPACHESRC 53 | echo Please set the APACHESRC variable to point to your Apache setup 54 | echo E.g. set APACHESRC=c:\apache 55 | echo This can be a binary distribution, no need for the Apache sources. 56 | GOTO END 57 | 58 | :END 59 | -------------------------------------------------------------------------------- /dist/setup.py.in: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2000, 2001, 2013 Gregory Trubetskoy 2 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # 17 | # $Id: setup.py.in 475516 2006-11-16 01:12:40Z grahamd $ 18 | 19 | import sys 20 | if sys.version_info[0]*100 + sys.version_info[1] > 310: 21 | from setuptools import setup, Extension 22 | import sysconfig 23 | else: 24 | from distutils.core import setup, Extension 25 | from distutils import sysconfig 26 | 27 | 28 | import string 29 | import re 30 | import os.path 31 | if sys.version[0] == '2': 32 | from commands import getoutput 33 | else: 34 | from subprocess import getoutput 35 | 36 | try: 37 | __file__ 38 | except NameError: 39 | __file__ = '.' 40 | 41 | def getmp_rootdir(): 42 | """gets the root directory of the mod_python source tree...""" 43 | return os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 44 | 45 | def getmp_srcdir(): 46 | """gets the src subdirectory of the mod_python source tree...""" 47 | return os.path.join(getmp_rootdir(), 'src') 48 | 49 | def getmp_includedir(): 50 | """gets the src subdirectory of the mod_python source tree...""" 51 | return os.path.join(getmp_rootdir(), 'src', 'include') 52 | 53 | def getmp_mpdir(): 54 | """gets the mod_python dir""" 55 | return os.path.join(getmp_rootdir(), 'lib', 'python', 'mod_python') 56 | 57 | def getconfigure_option(option_name): 58 | """gets an option from the config.status file""" 59 | config_status_file = os.path.join(getmp_rootdir(), 'config.status') 60 | if not os.path.exists(config_status_file): 61 | raise AssertionError("config.status not found in expected location (%s)" % config_status_file) 62 | header = open(config_status_file, 'r') 63 | r = re.compile(r's,\s*@%s@,\s*(?P[^,]+),\s*' % (option_name)) 64 | for line in header.readlines(): 65 | m = r.search(line) 66 | if m is not None: 67 | return m.group('OPTION_STRING') 68 | raise AssertionError("unable to find @%s@ definition in %s", (option_name, config_status_file)) 69 | 70 | def getmp_version(): 71 | return getoutput('./version.sh') 72 | 73 | def getapxs_location(): 74 | """finds the location of apxs from the config.status file""" 75 | return getconfigure_option("APXS") 76 | 77 | def getapxs_option(option): 78 | APXS = getapxs_location() 79 | return getoutput("%s -q %s" % (APXS, option)) 80 | 81 | def getapache_srcdir(): 82 | """returns apache src directory""" 83 | return os.getenv("APACHESRC") 84 | 85 | def getapache_includedir(): 86 | """returns apache include directory""" 87 | apache_srcdir = getapache_srcdir() 88 | if apache_srcdir is None: 89 | return getapxs_option("INCLUDEDIR") 90 | else: 91 | return os.path.join(getapache_srcdir(), "include") 92 | 93 | def getapache_libdir(): 94 | """returns apache lib directory""" 95 | apache_srcdir = getapache_srcdir() 96 | if apache_srcdir is None: 97 | return getapxs_option("LIBDIR") 98 | else: 99 | return os.path.join(apache_srcdir, "lib") 100 | 101 | def generate_version_py(): 102 | with open(os.path.join(getmp_mpdir(), 'version.py'), 'w') as version_py: 103 | version_py.write('# THIS FILE IS AUTO-GENERATED BY setup.py\n\n') 104 | version_py.write('version = "%s"\n' % getmp_version()) 105 | version_py.write('\n# Some build-time constants:\n') 106 | version_py.write('HTTPD = "@HTTPD@"\n') 107 | version_py.write('HTTPD_VERSION = "@HTTPD_VERSION@"\n') 108 | version_py.write('APR_VERSION = "@APR_VERSION@"\n') 109 | version_py.write('LIBEXECDIR = "@LIBEXECDIR@"\n') 110 | version_py.write('SYSCONFDIR = "@SYSCONFDIR@"\n') 111 | version_py.write('TEST_MOD_PYTHON_SO = "@TEST_MOD_PYTHON_SO@" #NB: This is for test.py only\n') 112 | version_py.write('TESTHOME = "@TEST_SERVER_ROOT@"\n') 113 | version_py.write('PYTHON_BIN = "@PYTHON_BIN@"\n') 114 | 115 | 116 | VER = getmp_version() 117 | 118 | # TODO: improve the intelligence here... 119 | winbuild = ("bdist_wininst" in sys.argv) or (os.name == "nt") 120 | 121 | class PSPExtension(Extension): 122 | """a class that helps build the PSP extension""" 123 | def __init__(self, source_dir, include_dirs): 124 | Extension.__init__(self, "mod_python._psp", 125 | [os.path.join(source_dir, source_file) for source_file in 126 | ("psp_string.c", "psp_parser.c", "_pspmodule.c")], 127 | include_dirs=include_dirs 128 | ) 129 | 130 | if winbuild: 131 | self.define_macros.extend([('WIN32', None), ('NDEBUG', None), ('_WINDOWS', None)]) 132 | 133 | PSPModule = PSPExtension(getmp_srcdir(), [getmp_includedir()]) 134 | 135 | modpy_src_files = ("mod_python.c", "_apachemodule.c", "connobject.c", "filterobject.c", 136 | "hlist.c", "hlistobject.c", "requestobject.c", "serverobject.c", "tableobject.c", 137 | "util.c", "finfoobject.c") 138 | 139 | class finallist(list): 140 | """this represents a list that cannot be appended to...""" 141 | def append(self, object): 142 | return 143 | 144 | class ModPyExtension(Extension): 145 | """a class that actually builds the mod_python.so extension for Apache (yikes)""" 146 | def __init__(self, source_dir, include_dirs, library_dirs): 147 | if winbuild: 148 | apr1 = 0 149 | for dir in library_dirs: 150 | if os.path.exists(os.path.join(dir, 'libapr-1.lib')): 151 | apr1 = 1 152 | if apr1: 153 | libraries = ['libhttpd', 'libapr-1', 'libaprutil-1', 'ws2_32'] 154 | else: 155 | libraries = ['libhttpd', 'libapr', 'libaprutil', 'ws2_32'] 156 | else: 157 | libraries = ['apr-0', 'aprutil-0'] 158 | 159 | Extension.__init__(self, "mod_python_so", 160 | sources = [os.path.join(source_dir, source_file) for source_file in modpy_src_files], 161 | include_dirs=include_dirs, 162 | libraries = libraries, 163 | library_dirs=library_dirs 164 | ) 165 | if winbuild: 166 | self.define_macros.extend([('WIN32', None),('NDEBUG', None),('_WINDOWS', None)]) 167 | self.sources.append(os.path.join(source_dir, "Version.rc")) 168 | else: 169 | # TODO: fix this to autodetect if required... 170 | self.include_dirs.append("/usr/include/apr-0") 171 | # this is a hack to prevent build_ext from trying to append "initmod_python" to the export symbols 172 | self.export_symbols = finallist(self.export_symbols) 173 | 174 | 175 | if winbuild: 176 | 177 | # build mod_python.so 178 | ModPyModule = ModPyExtension(getmp_srcdir(), [getmp_includedir(), getapache_includedir()], [getapache_libdir()]) 179 | 180 | scripts = ["win32_postinstall.py"] 181 | # put the mod_python.so file in the Python root ... 182 | # win32_postinstall.py will pick it up from there... 183 | # data_files = [("", [(os.path.join(getmp_srcdir(), 'Release', 'mod_python.so'))])] 184 | data_files = [] 185 | ext_modules = [ModPyModule, PSPModule] 186 | 187 | else: 188 | 189 | scripts = [] 190 | data_files = [] 191 | ext_modules = [PSPModule] 192 | 193 | generate_version_py() 194 | 195 | if sys.platform == "darwin": 196 | if not '-undefined' in sysconfig.get_config_var("LDSHARED").split(): 197 | sysconfig._config_vars["LDSHARED"] = \ 198 | string.replace(sysconfig.get_config_var("LDSHARED"), \ 199 | " -bundle "," -bundle -flat_namespace -undefined suppress ") 200 | sysconfig._config_vars["BLDSHARED"] = \ 201 | string.replace(sysconfig.get_config_var("BLDSHARED"), \ 202 | " -bundle "," -bundle -flat_namespace -undefined suppress ") 203 | 204 | setup(name="mod_python", 205 | version=VER, 206 | description="Apache/Python Integration", 207 | author="Gregory Trubetskoy et al", 208 | author_email="mod_python@modpython.org", 209 | url="http://www.modpython.org/", 210 | packages=["mod_python"], 211 | package_dir={'mod_python': os.path.join(getmp_rootdir(), 'lib', 'python', 'mod_python')}, 212 | scripts=scripts, 213 | data_files=data_files, 214 | ext_modules=ext_modules) 215 | 216 | # makes emacs go into python mode 217 | ### Local Variables: 218 | ### mode:python 219 | ### End: 220 | -------------------------------------------------------------------------------- /dist/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | MPV_PATH="`dirname $0`/../src/include/mp_version.h" 4 | 5 | MAJ=`awk '/MP_VERSION_MAJOR/ {print $3}' $MPV_PATH` 6 | MIN=`awk '/MP_VERSION_MINOR/ {print $3}' $MPV_PATH` 7 | PCH=`awk '/MP_VERSION_PATCH/ {print $3}' $MPV_PATH` 8 | 9 | # if git exists in path 10 | if type git >/dev/null 2>&1; then 11 | # and we are in a checkout 12 | if git rev-parse 2>/dev/null; then 13 | # but not on a tag (which means this is a release) 14 | if test -z "`git log 'HEAD^!' --format=%d 2>/dev/null | grep 'tag: '`"; then 15 | # append git revision hash to version 16 | GIT="+`git describe --always --exclude '*'`" 17 | fi 18 | fi 19 | fi 20 | 21 | echo $MAJ.$MIN.$PCH$GIT 22 | -------------------------------------------------------------------------------- /dist/win32_postinstall.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2000, 2001, 2013 Gregory Trubetskoy 2 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Originally developed by Gregory Trubetskoy. 17 | # 18 | # $Id$ 19 | # 20 | # this script runs at the end of windows install 21 | 22 | 23 | import sys, os, shutil 24 | 25 | def getApacheDirOptions(): 26 | """find potential apache directories in the registry...""" 27 | try: 28 | import win32api, win32con 29 | class nullregkey: 30 | """a registry key that doesn't exist...""" 31 | def childkey(self, subkeyname): 32 | return nullregkey() 33 | def subkeynames(self): 34 | return [] 35 | def getvalue(self, valuename): 36 | raise AttributeError("Cannot access registry value %r: key does not exist" % (valuename)) 37 | class regkey: 38 | """simple wrapper for registry functions that closes keys nicely...""" 39 | def __init__(self, parent, subkeyname): 40 | self.key = win32api.RegOpenKey(parent, subkeyname) 41 | def childkey(self, subkeyname): 42 | try: 43 | return regkey(self.key, subkeyname) 44 | except win32api.error: 45 | return nullregkey() 46 | def subkeynames(self): 47 | numsubkeys = win32api.RegQueryInfoKey(self.key)[0] 48 | return [win32api.RegEnumKey(self.key, index) for index in range(numsubkeys)] 49 | def getvalue(self, valuename): 50 | try: 51 | return win32api.RegQueryValueEx(self.key, valuename) 52 | except win32api.error: 53 | raise AttributeError("Cannot access registry value %r" % (valuename)) 54 | def __del__(self): 55 | if hasattr(self, "key"): 56 | win32api.RegCloseKey(self.key) 57 | except ImportError: 58 | return {} 59 | versions = {} 60 | hklm_key = regkey(win32con.HKEY_LOCAL_MACHINE, "Software").childkey("Apache Group").childkey("Apache") 61 | hkcu_key = regkey(win32con.HKEY_CURRENT_USER, "Software").childkey("Apache Group").childkey("Apache") 62 | for apachekey in (hklm_key, hkcu_key): 63 | for versionname in apachekey.subkeynames(): 64 | try: 65 | serverroot = apachekey.childkey(versionname).getvalue("ServerRoot") 66 | except AttributeError: 67 | continue 68 | versions[versionname] = serverroot[0] 69 | return versions 70 | 71 | def askForApacheDir(apachediroptions): 72 | # try to ask for Apache directory 73 | if len(apachediroptions) > 0: 74 | # get the most recent version... 75 | versionnames = apachediroptions.keys() 76 | versionnames.sort() 77 | initialdir = apachediroptions[versionnames[-1]] 78 | else: 79 | initialdir="C:/Program Files/Apache Group/Apache2" 80 | # TODO: let the user select the name from a list, or click browse to choose... 81 | try: 82 | from tkFileDialog import askdirectory 83 | from Tkinter import Tk 84 | root = Tk() 85 | root.withdraw() 86 | path = askdirectory(title="Where is Apache installed?", 87 | initialdir=initialdir, 88 | mustexist=1, master=root) 89 | root.quit() 90 | root.destroy() 91 | return path 92 | except ImportError: 93 | try: 94 | from win32com.shell import shell 95 | pidl, displayname, imagelist = shell.SHBrowseForFolder(0, None, "Where is Apache installed?") 96 | path = shell.SHGetPathFromIDList(pidl) 97 | return path 98 | except ImportError: 99 | return "" 100 | 101 | # if we're called during removal, just exit 102 | if len(sys.argv) == 1 or (len(sys.argv) > 1 and sys.argv[1] != "-remove"): 103 | 104 | if sys.version_info[0]*100 + sys.version_info[1] > 310: 105 | import sysconfig 106 | lib = get_path('platlib') 107 | else: 108 | import distutils.sysconfig 109 | lib = distutils.sysconfig.get_python_lib() 110 | mp = os.path.join(lib, "mod_python_so.pyd") 111 | 112 | apachediroptions = getApacheDirOptions() 113 | 114 | apachedir = askForApacheDir(apachediroptions) 115 | 116 | if apachedir: 117 | 118 | # put mod_python.so there 119 | mod_python_so_path = os.path.join(apachedir, "modules", "mod_python.so") 120 | shutil.move(mp, mod_python_so_path) 121 | file_created(mod_python_so_path) 122 | 123 | print """Important Note for Windows users, PLEASE READ!!! 124 | 125 | 1. This script does not attempt to modify Apache configuration, 126 | you must do it manually: 127 | 128 | Edit %s, 129 | find where other LoadModule lines are and add this: 130 | LoadModule python_module modules/mod_python.so 131 | 132 | 2. Now test your installation using the instructions at this link: 133 | http://www.modpython.org/live/current/doc-html/inst-testing.html 134 | 135 | """ % os.path.join(apachedir, "conf", "httpd.conf") 136 | 137 | else: 138 | 139 | print """Important Note for Windows users, PLEASE READ!!! 140 | 141 | 1. It appears that you do not have Tkinter installed, 142 | which is required for a part of this installation. 143 | Therefore you must manually take 144 | "%s" 145 | and copy it to your Apache modules directory. 146 | 147 | 2. This script does not attempt to modify Apache configuration, 148 | you must do it manually: 149 | 150 | Edit %s, 151 | find where other LoadModule lines and add this: 152 | LoadModule python_module modules/mod_python.so 153 | 154 | 3. Now test your installation using the instructions at this link: 155 | http://www.modpython.org/live/current/doc-html/inst-testing.html 156 | 157 | """ % (mp, os.path.join(apachedir, "conf", "httpd.conf")) 158 | -------------------------------------------------------------------------------- /examples/gzipfilter.py: -------------------------------------------------------------------------------- 1 | # 2 | # Usage: 3 | # 4 | # PythonOutputFilter gzipfilter 5 | # SetOutputFilter gzipfilter 6 | # 7 | 8 | from mod_python import apache 9 | 10 | import os 11 | import sys 12 | import gzip 13 | import cStringIO 14 | from mod_python import apache 15 | 16 | def compress(s): 17 | sio = cStringIO.StringIO() 18 | f = gzip.GzipFile(mode='wb', fileobj=sio) 19 | f.write(s) 20 | f.close() 21 | return sio.getvalue() 22 | 23 | def accepts_gzip(req): 24 | if req.headers_in.has_key('accept-encoding'): 25 | encodings = req.headers_in['accept-encoding'] 26 | return (encodings.find("gzip") != -1) 27 | return 0 28 | 29 | ### 30 | ### main filter function 31 | ### 32 | def outputfilter(filter): 33 | 34 | if (filter.req.main or 35 | not accepts_gzip(filter.req)): 36 | 37 | # Presense of filter.req.main tells us that 38 | # we are in a subrequest. We don't want to compress 39 | # the data more than once, so we pass_on() in 40 | # subrequests. We also pass_on() if the client 41 | # does not accept gzip encoding, of course. 42 | 43 | filter.pass_on() 44 | else: 45 | 46 | if not filter.req.sent_bodyct: 47 | 48 | # the above test allows us to set the encoding once 49 | # rather than every time the filter is invoked 50 | 51 | filter.req.headers_out['content-encoding'] = 'gzip' 52 | 53 | # loop through content, compressing 54 | 55 | s = filter.read() 56 | 57 | while s: 58 | s = compress(s) 59 | filter.write(s) 60 | s = filter.read() 61 | 62 | if s is None: 63 | 64 | # this means we received an EOS, so we pass it on 65 | # by closing the filter 66 | 67 | filter.close() 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /install-sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # install - install a program, script, or datafile 4 | # This comes from X11R5 (mit/util/scripts/install.sh). 5 | # 6 | # Copyright 1991 by the Massachusetts Institute of Technology 7 | # 8 | # Permission to use, copy, modify, distribute, and sell this software and its 9 | # documentation for any purpose is hereby granted without fee, provided that 10 | # the above copyright notice appear in all copies and that both that 11 | # copyright notice and this permission notice appear in supporting 12 | # documentation, and that the name of M.I.T. not be used in advertising or 13 | # publicity pertaining to distribution of the software without specific, 14 | # written prior permission. M.I.T. makes no representations about the 15 | # suitability of this software for any purpose. It is provided "as is" 16 | # without express or implied warranty. 17 | # 18 | # Calling this script install-sh is preferred over install.sh, to prevent 19 | # `make' implicit rules from creating a file called install from it 20 | # when there is no Makefile. 21 | # 22 | # This script is compatible with the BSD install script, but was written 23 | # from scratch. It can only install one file at a time, a restriction 24 | # shared with many OS's install programs. 25 | 26 | 27 | # set DOITPROG to echo to test this script 28 | 29 | # Don't use :- since 4.3BSD and earlier shells don't like it. 30 | doit="${DOITPROG-}" 31 | 32 | 33 | # put in absolute paths if you don't have them in your path; or use env. vars. 34 | 35 | mvprog="${MVPROG-mv}" 36 | cpprog="${CPPROG-cp}" 37 | chmodprog="${CHMODPROG-chmod}" 38 | chownprog="${CHOWNPROG-chown}" 39 | chgrpprog="${CHGRPPROG-chgrp}" 40 | stripprog="${STRIPPROG-strip}" 41 | rmprog="${RMPROG-rm}" 42 | mkdirprog="${MKDIRPROG-mkdir}" 43 | 44 | transformbasename="" 45 | transform_arg="" 46 | instcmd="$cpprog" 47 | chmodcmd="$chmodprog 0755" 48 | chowncmd="" 49 | chgrpcmd="" 50 | stripcmd="" 51 | rmcmd="$rmprog -f" 52 | mvcmd="$mvprog" 53 | src="" 54 | dst="" 55 | dir_arg="" 56 | 57 | while [ x"$1" != x ]; do 58 | case $1 in 59 | -c) instcmd="$cpprog" 60 | shift 61 | continue;; 62 | 63 | -d) dir_arg=true 64 | shift 65 | continue;; 66 | 67 | -m) chmodcmd="$chmodprog $2" 68 | shift 69 | shift 70 | continue;; 71 | 72 | -o) chowncmd="$chownprog $2" 73 | shift 74 | shift 75 | continue;; 76 | 77 | -g) chgrpcmd="$chgrpprog $2" 78 | shift 79 | shift 80 | continue;; 81 | 82 | -s) stripcmd="$stripprog" 83 | shift 84 | continue;; 85 | 86 | -t=*) transformarg=`echo $1 | sed 's/-t=//'` 87 | shift 88 | continue;; 89 | 90 | -b=*) transformbasename=`echo $1 | sed 's/-b=//'` 91 | shift 92 | continue;; 93 | 94 | *) if [ x"$src" = x ] 95 | then 96 | src=$1 97 | else 98 | # this colon is to work around a 386BSD /bin/sh bug 99 | : 100 | dst=$1 101 | fi 102 | shift 103 | continue;; 104 | esac 105 | done 106 | 107 | if [ x"$src" = x ] 108 | then 109 | echo "install: no input file specified" 110 | exit 1 111 | else 112 | true 113 | fi 114 | 115 | if [ x"$dir_arg" != x ]; then 116 | dst=$src 117 | src="" 118 | 119 | if [ -d $dst ]; then 120 | instcmd=: 121 | chmodcmd="" 122 | else 123 | instcmd=mkdir 124 | fi 125 | else 126 | 127 | # Waiting for this to be detected by the "$instcmd $src $dsttmp" command 128 | # might cause directories to be created, which would be especially bad 129 | # if $src (and thus $dsttmp) contains '*'. 130 | 131 | if [ -f $src -o -d $src ] 132 | then 133 | true 134 | else 135 | echo "install: $src does not exist" 136 | exit 1 137 | fi 138 | 139 | if [ x"$dst" = x ] 140 | then 141 | echo "install: no destination specified" 142 | exit 1 143 | else 144 | true 145 | fi 146 | 147 | # If destination is a directory, append the input filename; if your system 148 | # does not like double slashes in filenames, you may need to add some logic 149 | 150 | if [ -d $dst ] 151 | then 152 | dst="$dst"/`basename $src` 153 | else 154 | true 155 | fi 156 | fi 157 | 158 | ## this sed command emulates the dirname command 159 | dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` 160 | 161 | # Make sure that the destination directory exists. 162 | # this part is taken from Noah Friedman's mkinstalldirs script 163 | 164 | # Skip lots of stat calls in the usual case. 165 | if [ ! -d "$dstdir" ]; then 166 | defaultIFS=' 167 | ' 168 | IFS="${IFS-${defaultIFS}}" 169 | 170 | oIFS="${IFS}" 171 | # Some sh's can't handle IFS=/ for some reason. 172 | IFS='%' 173 | set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` 174 | IFS="${oIFS}" 175 | 176 | pathcomp='' 177 | 178 | while [ $# -ne 0 ] ; do 179 | pathcomp="${pathcomp}${1}" 180 | shift 181 | 182 | if [ ! -d "${pathcomp}" ] ; 183 | then 184 | $mkdirprog "${pathcomp}" 185 | else 186 | true 187 | fi 188 | 189 | pathcomp="${pathcomp}/" 190 | done 191 | fi 192 | 193 | if [ x"$dir_arg" != x ] 194 | then 195 | $doit $instcmd $dst && 196 | 197 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && 198 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && 199 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && 200 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi 201 | else 202 | 203 | # If we're going to rename the final executable, determine the name now. 204 | 205 | if [ x"$transformarg" = x ] 206 | then 207 | dstfile=`basename $dst` 208 | else 209 | dstfile=`basename $dst $transformbasename | 210 | sed $transformarg`$transformbasename 211 | fi 212 | 213 | # don't allow the sed command to completely eliminate the filename 214 | 215 | if [ x"$dstfile" = x ] 216 | then 217 | dstfile=`basename $dst` 218 | else 219 | true 220 | fi 221 | 222 | # Make a temp file name in the proper directory. 223 | 224 | dsttmp=$dstdir/#inst.$$# 225 | 226 | # Move or copy the file name to the temp name 227 | 228 | $doit $instcmd $src $dsttmp && 229 | 230 | trap "rm -f ${dsttmp}" 0 && 231 | 232 | # and set any options; do chmod last to preserve setuid bits 233 | 234 | # If any of these fail, we abort the whole thing. If we want to 235 | # ignore errors from any of these, just make sure not to ignore 236 | # errors from the above "$doit $instcmd $src $dsttmp" command. 237 | 238 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && 239 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && 240 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && 241 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && 242 | 243 | # Now rename the file to the real destination. 244 | 245 | $doit $rmcmd -f $dstdir/$dstfile && 246 | $doit $mvcmd $dsttmp $dstdir/$dstfile 247 | 248 | fi && 249 | 250 | 251 | exit 0 252 | -------------------------------------------------------------------------------- /lib/python/mod_python/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you 6 | # may not use this file except in compliance with the License. You 7 | # may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | # implied. See the License for the specific language governing 15 | # permissions and limitations under the License. 16 | # 17 | # Originally developed by Gregory Trubetskoy. 18 | # 19 | 20 | __all__ = ["apache", "cgihandler", "psp", 21 | "publisher", "util", "python22", "version"] 22 | 23 | # This is used by mod_python.c to make sure the version of C 24 | # code matches the Python code. 25 | from . import version 26 | mp_version = version.version 27 | 28 | try: 29 | # it's possible for mod_python to be imported outside httpd, e.g. to use 30 | # httpdconf, so we fail silently 31 | from . import apache 32 | except: pass 33 | -------------------------------------------------------------------------------- /lib/python/mod_python/cgihandler.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you 6 | # may not use this file except in compliance with the License. You 7 | # may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | # implied. See the License for the specific language governing 15 | # permissions and limitations under the License. 16 | # 17 | # Originally developed by Gregory Trubetskoy. 18 | # 19 | 20 | from . import apache 21 | import os 22 | import sys 23 | PY2 = sys.version[0] == '2' 24 | 25 | if PY2 or sys.hexversion < 0x030c0000: # 3.12.0 26 | import imp 27 | else: 28 | import importlib.util 29 | 30 | # if threads are not available 31 | # create a functionless lock object 32 | try: 33 | import threading 34 | _lock = threading.Lock() 35 | except (ImportError, AttributeError): 36 | class DummyLock: 37 | def acquire(self): 38 | pass 39 | def release(self): 40 | pass 41 | _lock = DummyLock() 42 | 43 | # the next statement deserves some explaining. 44 | # it seems that the standard os.environ object looses 45 | # memory if the environment is manipulated frequently. Since for 46 | # CGI you have to rebuild it for every request, your httpd will 47 | # grow rather fast. I am not exactly sure why it happens and if there 48 | # is a more sensible remedy, but this seems to work OK. 49 | os.environ = {} 50 | 51 | original = list(sys.modules.keys()) 52 | 53 | # find out the standard library location 54 | stdlib, x = os.path.split(os.__file__) 55 | 56 | def handler(req): 57 | 58 | ### if you don't need indirect modules reloaded, comment out 59 | ### code unitl ### end 60 | 61 | # if there are any new modules since the import of this module, 62 | # delete them. 63 | for m in list(sys.modules.keys()): 64 | if m not in original: 65 | # unless they are part of standard library 66 | mod = sys.modules[m] 67 | if hasattr(mod, "__file__"): 68 | path, x = os.path.split(mod.__file__) 69 | if path != stdlib: 70 | del sys.modules[m] 71 | ### end 72 | 73 | # get the filename of the script 74 | if "script_filename" in req.subprocess_env: 75 | dir, file = os.path.split(req.subprocess_env["script_filename"]) 76 | else: 77 | dir, file = os.path.split(req.filename) 78 | module_name, ext = os.path.splitext(file) 79 | 80 | _lock.acquire() 81 | try: 82 | 83 | try: 84 | 85 | # The CGI spec requires us to set current working 86 | # directory to that of the script. This is not 87 | # thread safe, this is why we must obtain the lock. 88 | cwd = os.getcwd() 89 | os.chdir(dir) 90 | 91 | # simulate cgi environment 92 | env, si, so = apache.setup_cgi(req) 93 | 94 | scriptPath = os.path.join(dir, file) 95 | 96 | if not os.path.exists(scriptPath): 97 | raise apache.SERVER_RETURN(apache.HTTP_NOT_FOUND) 98 | 99 | # avoid loading modules outside dir 100 | # (e.g. shenanigans like ../../../../etc/passwd) 101 | scriptPath = os.path.abspath(scriptPath) 102 | if not scriptPath.startswith(dir): 103 | raise apache.SERVER_RETURN(apache.HTTP_NOT_FOUND) 104 | 105 | if PY2 or sys.hexversion < 0x030c0000: # 3.12.0 106 | 107 | try: 108 | # we do not search the pythonpath (security reasons) 109 | fd, path, desc = imp.find_module(module_name, [dir]) 110 | except ImportError: 111 | raise apache.SERVER_RETURN(apache.HTTP_NOT_FOUND) 112 | 113 | # this executes the module 114 | imp.load_module(module_name, fd, path, desc) 115 | 116 | else: 117 | 118 | try: 119 | # we do not search the pythonpath (security reasons) 120 | spec = importlib.util.spec_from_file_location(module_name, scriptPath) 121 | except (ModuleNotFoundError, ValueError): 122 | raise apache.SERVER_RETURN(apache.HTTP_NOT_FOUND) 123 | 124 | if spec is None: 125 | raise apache.SERVER_RETURN(apache.HTTP_NOT_FOUND) 126 | 127 | module = importlib.util.module_from_spec(spec) 128 | sys.modules[module_name] = module 129 | spec.loader.exec_module(module) 130 | 131 | return apache.OK 132 | 133 | finally: 134 | # unsimulate the cgi environment 135 | apache.restore_nocgi(env, si, so) 136 | if PY2: 137 | try: 138 | fd.close() 139 | except: pass 140 | os.chdir(cwd) 141 | 142 | finally: 143 | _lock.release() 144 | -------------------------------------------------------------------------------- /lib/python/mod_python/python22.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you 6 | # may not use this file except in compliance with the License. You 7 | # may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | # implied. See the License for the specific language governing 15 | # permissions and limitations under the License. 16 | # 17 | # Originally developed by Gregory Trubetskoy. 18 | # 19 | 20 | # This file contains a bunch of hacks used to support Python 2.2 21 | 22 | import sys 23 | if sys.version < '2.3': 24 | import builtins as hack 25 | 26 | # Enumerate does not exists in Python 2.2 27 | hack.enumerate = lambda s : list(zip(list(range(len(s))),s)) 28 | -------------------------------------------------------------------------------- /lib/python/mod_python/testhandler.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you 6 | # may not use this file except in compliance with the License. You 7 | # may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | # implied. See the License for the specific language governing 15 | # permissions and limitations under the License. 16 | # 17 | # Originally developed by Gregory Trubetskoy. 18 | # 19 | 20 | """ 21 | 22 | This module is a mod_python handler that can be used to test the configuration. 23 | 24 | """ 25 | 26 | from mod_python import apache, util 27 | import sys, os 28 | 29 | class bounded_buffer(object): 30 | """ 31 | This class implements a bounded buffer, i.e. a list that keeps the last 32 | n lines. It doesn't use pop(0), which is costly. 33 | 34 | """ 35 | def __init__(self,size): 36 | self.max_size = size 37 | self.list = [] 38 | self.pos = 0 39 | 40 | def append(self,value): 41 | if len(self.list)') 55 | req.write('KeyValue\n') 56 | for key in table: 57 | req.write('%s%s\n'%( 58 | key, 59 | table[key] 60 | )) 61 | req.write('') 62 | 63 | def write_tree(req,tree,level): 64 | for entry in tree: 65 | if isinstance(entry,list): 66 | write_tree(req,entry,level+1) 67 | else: 68 | req.write(' '*level) 69 | req.write(' '.join(entry)) 70 | req.write('\n') 71 | 72 | def handler(req): 73 | req.form = util.FieldStorage(req) 74 | 75 | if req.form.getfirst('view_log'): 76 | log = open(os.path.join(apache.server_root(),req.server.error_fname),'rb') 77 | lines = bounded_buffer(100) 78 | for line in log: 79 | lines.append(line) 80 | log.close() 81 | req.content_type='text/plain' 82 | for line in lines: 83 | req.write(line) 84 | return apache.OK 85 | 86 | req.add_common_vars() 87 | req.content_type = 'text/html' 88 | req.write('mod_python test page\n') 89 | 90 | req.write('

General information

\n') 91 | req.write('\n') 92 | req.write('\n'%( 93 | 'Apache version', 94 | req.subprocess_env.get('SERVER_SOFTWARE') 95 | )) 96 | req.write('\n'%( 97 | 'Apache threaded MPM', 98 | ( 99 | apache.mpm_query(apache.AP_MPMQ_IS_THREADED) and 100 | 'Yes, maximum %i threads / process'% 101 | apache.mpm_query(apache.AP_MPMQ_MAX_THREADS) 102 | ) or 'No (single thread MPM)' 103 | )) 104 | req.write('\n'%( 105 | 'Apache forked MPM', 106 | ( 107 | apache.mpm_query(apache.AP_MPMQ_IS_FORKED) and 108 | 'Yes, maximum %i processes'% 109 | apache.mpm_query(apache.AP_MPMQ_MAX_DAEMONS) 110 | ) or 'No (single process MPM)' 111 | )) 112 | req.write('\n'%( 113 | 'Apache server root', 114 | apache.server_root() 115 | )) 116 | req.write('\n'%( 117 | 'Apache document root', 118 | req.document_root() 119 | )) 120 | if req.server.error_fname: 121 | req.write('\n'%( 122 | 'Apache error log', 123 | os.path.join(apache.server_root(),req.server.error_fname) 124 | )) 125 | else: 126 | req.write('\n'%( 127 | 'Apache error log', 128 | 'None' 129 | )) 130 | req.write('\n'%( 131 | 'Python sys.version', 132 | sys.version 133 | )) 134 | req.write('\n'%( 135 | 'Python sys.path', 136 | '\n'.join(sys.path) 137 | )) 138 | req.write('\n'%( 139 | 'Python interpreter name', 140 | req.interpreter 141 | )) 142 | req.write('\n') 149 | req.write('\n') 156 | req.write('
%s%s
%s%s
%s%s
%s%s
%s%s
%s%s (view last 100 lines)
%s%s
%s%s
%s
%s
%s%s
mod_python.publisher available') 143 | try: 144 | from mod_python import publisher 145 | req.write('Yes') 146 | except: 147 | req.write('No') 148 | req.write('
mod_python.psp available') 150 | try: 151 | from mod_python import psp 152 | req.write('Yes') 153 | except: 154 | req.write('No') 155 | req.write('
\n') 157 | 158 | req.write('

Request input headers

\n') 159 | write_table(req,req.headers_in) 160 | 161 | req.write('

Request environment

\n') 162 | write_table(req,req.subprocess_env) 163 | 164 | req.write('

Request configuration

\n') 165 | write_table(req,req.get_config()) 166 | 167 | req.write('

Request options

\n') 168 | write_table(req,req.get_options()) 169 | 170 | req.write('

Request notes

\n') 171 | write_table(req,req.notes) 172 | 173 | req.write('

Server configuration

\n') 174 | write_table(req,req.server.get_config()) 175 | 176 | req.write('

Server options

\n') 177 | write_table(req,req.server.get_options()) 178 | 179 | req.write('

Server configuration tree

\n
')
180 |     write_tree(req,apache.config_tree(),0)
181 |     req.write('
\n') 182 | 183 | req.write('') 184 | return apache.OK 185 | -------------------------------------------------------------------------------- /lib/python/mod_python/wsgi.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you 6 | # may not use this file except in compliance with the License. You 7 | # may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | # implied. See the License for the specific language governing 15 | # permissions and limitations under the License. 16 | # 17 | # Originally developed by Gregory Trubetskoy. 18 | # 19 | 20 | import sys 21 | from mod_python import apache 22 | 23 | def handler(req): 24 | 25 | options = req.get_options() 26 | 27 | ## Find the application callable 28 | 29 | app = None 30 | app_str = options['mod_python.wsgi.application'] 31 | if app_str: 32 | if '::' in app_str: 33 | mod_str, callable_str = app_str.split('::', 1) 34 | else: 35 | mod_str, callable_str = app_str, 'application' 36 | config = req.get_config() 37 | autoreload, log = True, False 38 | if "PythonAutoReload" in config: 39 | autoreload = config["PythonAutoReload"] == "1" 40 | if "PythonDebug" in config: 41 | log = config["PythonDebug"] == "1" 42 | module = apache.import_module(mod_str, autoreload=autoreload, log=log) 43 | 44 | try: 45 | app = module.__dict__[callable_str] 46 | except KeyError: pass 47 | 48 | if not app: 49 | req.log_error( 50 | 'WSGI handler: mod_python.wsgi.application (%s) not found, declining.' 51 | % repr(app_str), apache.APLOG_WARNING) 52 | return apache.DECLINED 53 | 54 | ## Build env 55 | 56 | env = req.build_wsgi_env() 57 | if env is None: 58 | # None means base_uri mismatch. The problem can be either 59 | # base_uri or Location, but because we couldn't be here if it 60 | # was Location, then it must be mod_python.wsgi.base_uri. 61 | base_uri = options.get('mod_python.wsgi.base_uri') 62 | req.log_error( 63 | "WSGI handler: req.uri (%s) does not start with mod_python.wsgi.base_uri (%s), declining." 64 | % (repr(req.uri), repr(base_uri)), apache.APLOG_WARNING) 65 | return apache.DECLINED 66 | 67 | ## Run the app 68 | 69 | response = None 70 | try: 71 | response = app(env, req.wsgi_start_response) 72 | [req.write(token) for token in response] 73 | finally: 74 | # call close() if there is one 75 | if type(response) not in (list, tuple): 76 | getattr(response, 'close', lambda: None)() 77 | 78 | return apache.OK 79 | -------------------------------------------------------------------------------- /scripts/Makefile.in: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 2 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Originally developed by Gregory Trubetskoy. 17 | # 18 | 19 | INSTALL=@INSTALL@ 20 | BINDIR=`dirname @PYTHON_BIN@` 21 | 22 | clean: 23 | rm -rf *~ 24 | 25 | distclean: clean 26 | rm -f Makefile 27 | 28 | install: 29 | $(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) 30 | $(INSTALL) -m 0755 mod_python $(DESTDIR)$(BINDIR)/mod_python 31 | -------------------------------------------------------------------------------- /scripts/mod_python.in: -------------------------------------------------------------------------------- 1 | #!@PYTHON_BIN@ 2 | 3 | # Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 4 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you 7 | # may not use this file except in compliance with the License. You 8 | # may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | # implied. See the License for the specific language governing 16 | # permissions and limitations under the License. 17 | # 18 | # Originally developed by Gregory Trubetskoy. 19 | 20 | 21 | # WARNING: 22 | # WARNING: Make sure you're editing mod_python.in, not mod_python! 23 | # WARNING: 24 | 25 | 26 | import sys 27 | import os 28 | import platform 29 | import StringIO 30 | import mod_python 31 | from mod_python import httpdconf 32 | 33 | 34 | def cmd_start(): 35 | parser = OptionParser(usage="%prog start \n" 36 | " Start Apache using config file ") 37 | (options, args) = parser.parse_args(sys.argv[2:]) 38 | if len(args) != 1: 39 | parser.error("Must specify ") 40 | os.execl(mod_python.version.HTTPD, mod_python.version.HTTPD, '-f', args[0], '-k', 'start') 41 | 42 | def cmd_stop(): 43 | parser = OptionParser(usage="%prog start \n" 44 | " Stop Apache using config file ") 45 | (options, args) = parser.parse_args(sys.argv[2:]) 46 | if len(args) != 1: 47 | parser.error("Must specify ") 48 | os.execl(mod_python.version.HTTPD, mod_python.version.HTTPD, '-f', args[0], '-k', 'graceful-stop') 49 | 50 | def cmd_restart(): 51 | parser = OptionParser(usage="%prog start \n" 52 | " Restart Apache using config file ") 53 | (options, args) = parser.parse_args(sys.argv[2:]) 54 | if len(args) != 1: 55 | parser.error("Must specify ") 56 | os.execl(mod_python.version.HTTPD, mod_python.version.HTTPD, '-f', args[0], '-k', 'graceful') 57 | 58 | def cmd_genconfig(): 59 | 60 | parser = OptionParser(usage="%prog genconfig > \n" 61 | " Run the config generation script ") 62 | 63 | (options, args) = parser.parse_args(sys.argv[2:]) 64 | if len(args) != 1: 65 | parser.error("Must specify ") 66 | 67 | execfile(args[0]) 68 | 69 | def cmd_create(): 70 | 71 | parser = OptionParser(usage="%prog create \n" 72 | " Create a mod_python skeleton in ") 73 | parser.add_option("--listen", action="store", type="string", dest="listen", default="8888") 74 | parser.add_option("--pythonpath", action="store", type="string", dest="pythonpath", default="") 75 | parser.add_option("--pythonhandler", action="store", type="string", dest="pythonhandler", default=None) 76 | parser.add_option("--pythonoption", action="append", type="string", dest="pythonoptions", default=[]) 77 | 78 | (options, args) = parser.parse_args(sys.argv[2:]) 79 | 80 | if len(args) != 1: 81 | parser.error("Must specify ") 82 | 83 | if not options.pythonhandler: 84 | parser.error("Must specify a --pythonhandler") 85 | 86 | dest = args[0] 87 | 88 | pythonpath = options.pythonpath.split(":") 89 | if options.pythonhandler == 'mod_python.wsgi': 90 | mp_comments = ['PythonOption mod_python.wsgi.base_url = ""'] 91 | conf_path = mod_python.httpdconf.write_basic_config(dest, listen=options.listen, pythonhandler=options.pythonhandler, 92 | pythonpath=pythonpath, pythonoptions=options.pythonoptions, 93 | mp_comments=mp_comments) 94 | if conf_path: 95 | print("\nCreated! Please look over %s." % repr(conf_path)) 96 | print("Remember to generate the Apache httpd config by running") 97 | print("%s genconfig %s > %s" % (sys.argv[0], conf_path, 98 | os.path.join(os.path.split(conf_path)[0], 'httpd.conf'))) 99 | print("From here on you can tweak %s and re-generate Apache config at any time." % repr(conf_path)) 100 | 101 | def cmd_version(): 102 | 103 | parser = OptionParser(usage="%prog version\n" 104 | " Print version") 105 | 106 | version = "\n" 107 | version += "mod_python: %s\n" % mod_python.mp_version 108 | version += " %s\n\n" % repr(os.path.join(mod_python.version.LIBEXECDIR, "mod_python.so")) 109 | version += "python: %s\n" % ''.join(sys.version.splitlines()) 110 | version += " %s\n\n" % repr(mod_python.version.PYTHON_BIN) 111 | version += "httpd: %s\n" % mod_python.version.HTTPD_VERSION 112 | version += " %s\n\n" % repr(mod_python.version.HTTPD) 113 | version += "apr: %s\n" % mod_python.version.APR_VERSION 114 | version += "platform: %s\n" % platform.platform() 115 | 116 | print(version) 117 | 118 | import optparse 119 | 120 | class OptionParser (optparse.OptionParser): 121 | 122 | def check_required (self, opt): 123 | option = self.get_option(opt) 124 | 125 | # Assumes the option's 'default' is set to None! 126 | if getattr(self.values, option.dest) is None: 127 | self.error("%s option not supplied" % option) 128 | 129 | 130 | def main(): 131 | 132 | module = sys.modules[__name__] 133 | commands = [c[4:] for c in dir(module) if c.startswith("cmd_")] 134 | 135 | parser = OptionParser(usage = "%%prog [command options]\n" 136 | " Where is one of: %s\n" 137 | " For help on a specific command, use: %%prog --help\n" 138 | % " ".join(commands)) 139 | 140 | # anything after a command is not our argument 141 | try: 142 | cmd_idx = [sys.argv.index(arg) for arg in sys.argv if arg in commands][0] 143 | except IndexError: 144 | cmd_idx = 1 145 | 146 | (options, args) = parser.parse_args(sys.argv[1:cmd_idx+1]) 147 | 148 | if not args: 149 | parser.error("Please specify a command") 150 | 151 | command = args[0] 152 | 153 | if command not in commands: 154 | parser.error("Invalid command: %s" % command) 155 | 156 | cmd_func = module.__dict__["cmd_"+command] 157 | cmd_func() 158 | 159 | if __name__ == "__main__": 160 | main() 161 | 162 | # makes emacs go into python mode 163 | ### Local Variables: 164 | ### mode:python 165 | ### End: 166 | -------------------------------------------------------------------------------- /src/Makefile.in: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 2 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Originally developed by Gregory Trubetskoy. 17 | # 18 | 19 | CC=@CC@ 20 | AR=@AR@ 21 | APXS=@APXS@ 22 | MKDEP=@MKDEP@ 23 | 24 | # requires flex 2.5.31 for reentrant support 25 | LEX=@LEX@ 26 | INCLUDES=@INCLUDES@ 27 | CPPFLAGS=@CPPFLAGS@ 28 | CFLAGS=@CFLAGS@ 29 | LDLIBS=@LDLIBS@ 30 | LDFLAGS=@LDFLAGS@ 31 | srcdir=. 32 | 33 | SRCS= mod_python.c _apachemodule.c requestobject.c tableobject.c util.c \ 34 | serverobject.c connobject.c filterobject.c hlist.c \ 35 | hlistobject.c finfoobject.c version.c \ 36 | include/_apachemodule.h include/filterobject.h include/hlist.h \ 37 | include/mod_python.h include/psp_flex.h include/psp_parser.h \ 38 | include/requestobject.h include/tableobject.h include/connobject.h \ 39 | include/finfoobject.h include/hlistobject.h include/mp_version.h \ 40 | include/_pspmodule.h include/psp_string.h include/serverobject.h \ 41 | include/util.h 42 | 43 | .PHONY: version 44 | 45 | all: @ALL@ 46 | 47 | psp_parser.c: psp_parser.l 48 | @rm -f psp_parser.c 49 | $(LEX) -R -opsp_parser.c --header-file=include/psp_flex.h psp_parser.l 50 | 51 | dso: mod_python.so 52 | 53 | mod_python.so: $(SRCS) @SOLARIS_HACKS@ 54 | @echo 55 | @echo 'Building mod_python.so.' 56 | @echo 57 | $(APXS) $(INCLUDES) $(CPPFLAGS) $(APXSFLAGS) $(CFLAGS) -c $(SRCS) $(LDFLAGS) $(LDLIBS) @SOLARIS_HACKS@ 58 | @rm -f mod_python.so 59 | @ln -s .libs/mod_python.so mod_python.so 60 | clean: 61 | rm -rf $(OBJS) version.c core libpython.a mod_python.so *~ .libs *.o *.slo *.lo *.la 62 | 63 | distclean: clean 64 | rm -f Makefile .depend 65 | 66 | version.c: 67 | @echo > version.c ; \ 68 | echo "/* THIS FILE IS AUTO-GENERATED BY Makefile */" >> version.c ; \ 69 | echo "#include \"mp_version.h\"" >> version.c ; \ 70 | echo "const int mp_version_major = MP_VERSION_MAJOR;" >> version.c ; \ 71 | echo "const int mp_version_minor = MP_VERSION_MINOR;" >> version.c ; \ 72 | echo "const int mp_version_patch = MP_VERSION_PATCH;" >> version.c ; \ 73 | echo "const char * const mp_version_string = \"`../dist/version.sh`\";" >> version.c ; \ 74 | echo "const char * const mp_version_component = \"mod_python/\" \"`../dist/version.sh`\";" >> version.c 75 | 76 | # this is a hack to help avoid a gcc/solaris problem 77 | # python uses assert() which needs _eprintf(). See 78 | # SOLARIS_HACKS above 79 | _eprintf.o: 80 | ar -x `gcc -print-libgcc-file-name` _eprintf.o 81 | _floatdidf.o: 82 | ar -x `gcc -print-libgcc-file-name` _floatdidf.o 83 | _muldi3.o: 84 | ar -x `gcc -print-libgcc-file-name` _muldi3.o 85 | 86 | # DO NOT DELETE THIS LINE 87 | -------------------------------------------------------------------------------- /src/Version.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grisha/mod_python/188b55d24ae4590586dbf4f8750899f2ae3b83fa/src/Version.rc -------------------------------------------------------------------------------- /src/_pspmodule.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * This file originally written by Stering Hughes 18 | * 19 | * 20 | * See accompanying documentation and source code comments for 21 | * details. 22 | * 23 | */ 24 | 25 | #include "psp_flex.h" 26 | #include "psp_parser.h" 27 | #include "psp_string.h" 28 | #include "_pspmodule.h" 29 | #include "Python.h" 30 | 31 | static psp_parser_t *psp_parser_init(void) 32 | { 33 | psp_parser_t *parser; 34 | 35 | parser = (psp_parser_t *) malloc(sizeof(*parser)); 36 | 37 | memset(&parser->pycode, 0, sizeof(psp_string)); 38 | memset(&parser->whitespace, 0, sizeof(psp_string)); 39 | parser->dir = NULL; 40 | parser->is_psp_echo = 0; 41 | parser->after_colon = 0; 42 | parser->seen_newline = 0; 43 | 44 | return parser; 45 | } 46 | 47 | static void psp_parser_cleanup(psp_parser_t *parser) 48 | { 49 | if (parser->pycode.allocated) { 50 | free(parser->pycode.blob); 51 | } 52 | 53 | if (parser->whitespace.allocated) { 54 | free(parser->whitespace.blob); 55 | } 56 | 57 | free(parser); 58 | } 59 | 60 | static PyObject * _psp_module_parse(PyObject *self, PyObject *argv) 61 | { 62 | PyObject *code; 63 | char *filename; 64 | char *dir = NULL; 65 | char *path; 66 | psp_parser_t *parser; 67 | yyscan_t scanner; 68 | FILE *f; 69 | 70 | if (!PyArg_ParseTuple(argv, "s|s", &filename, &dir)) { 71 | return NULL; 72 | } 73 | 74 | if (dir) { 75 | path = malloc(strlen(filename)+strlen(dir)+1); 76 | if (!path) 77 | return PyErr_NoMemory(); 78 | strcpy(path, dir); 79 | strcat(path, filename); 80 | } 81 | else { 82 | path = filename; 83 | } 84 | 85 | Py_BEGIN_ALLOW_THREADS 86 | f = fopen(path, "rb"); 87 | Py_END_ALLOW_THREADS 88 | 89 | if (f == NULL) { 90 | PyErr_SetFromErrnoWithFilename(PyExc_IOError, path); 91 | if (dir) free(path); 92 | return NULL; 93 | } 94 | if (dir) free(path); 95 | 96 | parser = psp_parser_init(); 97 | if (dir) 98 | parser->dir = dir; 99 | 100 | yylex_init(&scanner); 101 | yyset_in(f, scanner); 102 | yyset_extra(parser, scanner); 103 | yylex(scanner); 104 | yylex_destroy(scanner); 105 | 106 | fclose(f); 107 | psp_string_0(&parser->pycode); 108 | 109 | if (PyErr_Occurred()) { 110 | psp_parser_cleanup(parser); 111 | return NULL; 112 | } 113 | 114 | if (parser->pycode.blob) { 115 | code = MpBytesOrUnicode_FromString(parser->pycode.blob); 116 | } 117 | else { 118 | code = MpBytesOrUnicode_FromString(""); 119 | } 120 | 121 | psp_parser_cleanup(parser); 122 | 123 | return code; 124 | } 125 | 126 | static PyObject * _psp_module_parsestring(PyObject *self, PyObject *argv) 127 | { 128 | PyObject *code; 129 | PyObject *str; 130 | PyObject *latin = NULL; 131 | char *c_str = NULL; 132 | yyscan_t scanner; 133 | psp_parser_t *parser; 134 | 135 | if (!PyArg_ParseTuple(argv, "S", &str)) { 136 | return NULL; 137 | } 138 | 139 | Py_BEGIN_ALLOW_THREADS 140 | parser = psp_parser_init(); 141 | yylex_init(&scanner); 142 | yyset_extra(parser, scanner); 143 | 144 | if (PyUnicode_Check(str)) { 145 | latin = PyUnicode_AsLatin1String(str); 146 | if (latin) 147 | c_str = PyBytes_AsString(latin); 148 | } else if (PyBytes_Check(str)) 149 | c_str = PyBytes_AsString(str); 150 | 151 | if (!c_str) c_str = "UNICODE ERROR"; 152 | 153 | yy_scan_string(c_str, scanner); 154 | yylex(scanner); 155 | 156 | Py_XDECREF(latin); 157 | 158 | /* yy_delete_buffer(bs, scanner); */ 159 | yylex_destroy(scanner); 160 | 161 | psp_string_0(&parser->pycode); 162 | Py_END_ALLOW_THREADS 163 | 164 | if (parser->pycode.blob) { 165 | code = MpBytesOrUnicode_FromString(parser->pycode.blob); 166 | } 167 | else { 168 | code = MpBytesOrUnicode_FromString(""); 169 | } 170 | 171 | psp_parser_cleanup(parser); 172 | 173 | return code; 174 | } 175 | 176 | static PyMethodDef _psp_module_methods[] = { 177 | {"parse", (PyCFunction) _psp_module_parse, METH_VARARGS}, 178 | {"parsestring", (PyCFunction) _psp_module_parsestring, METH_VARARGS}, 179 | {NULL, NULL} 180 | }; 181 | 182 | #if PY_MAJOR_VERSION >= 3 183 | 184 | static struct PyModuleDef _psp_moduledef = { 185 | PyModuleDef_HEAD_INIT, 186 | "_psp", /* m_name */ 187 | NULL, /* m_doc */ 188 | -1, /* m_size */ 189 | _psp_module_methods, /* m_methods */ 190 | NULL, 191 | NULL, 192 | NULL, 193 | NULL, 194 | }; 195 | 196 | #endif 197 | 198 | PyObject * _init_psp(void) 199 | { 200 | PyObject *m; 201 | #if PY_MAJOR_VERSION < 3 202 | m = Py_InitModule("_psp", _psp_module_methods); 203 | #else 204 | m = PyModule_Create(&_psp_moduledef); 205 | #endif 206 | return m; 207 | } 208 | 209 | #if PY_MAJOR_VERSION < 3 210 | 211 | PyMODINIT_FUNC init_psp(void) { 212 | _init_psp(); 213 | } 214 | 215 | #else 216 | 217 | PyMODINIT_FUNC PyInit__psp(void) { 218 | return _init_psp(); 219 | } 220 | 221 | #endif 222 | -------------------------------------------------------------------------------- /src/hlist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * Originally developed by Gregory Trubetskoy. 18 | * 19 | * 20 | * hlist.c 21 | * 22 | * 23 | * See accompanying documentation and source code comments 24 | * for details. 25 | * 26 | */ 27 | 28 | #include "mod_python.h" 29 | 30 | /** 31 | ** hlist_new 32 | ** 33 | * Start a new list. 34 | */ 35 | 36 | hl_entry *hlist_new(apr_pool_t *p, const char *h, const char *d, 37 | char d_is_fnmatch, char d_is_location, 38 | ap_regex_t *regex, const char silent) 39 | 40 | { 41 | hl_entry *hle; 42 | 43 | hle = (hl_entry *)apr_pcalloc(p, sizeof(hl_entry)); 44 | 45 | hle->handler = h; 46 | hle->directory = d; 47 | hle->d_is_fnmatch = d_is_fnmatch; 48 | hle->d_is_location = d_is_location; 49 | hle->regex = regex; 50 | hle->silent = silent; 51 | 52 | return hle; 53 | } 54 | 55 | /** 56 | ** hlist_append 57 | ** 58 | * Appends an hl_entry to a list identified by hle, 59 | * and returns the new tail. This func will skip 60 | * to the tail of the list. 61 | * If hle is NULL, a new list is created. 62 | */ 63 | 64 | hl_entry *hlist_append(apr_pool_t *p, hl_entry *hle, const char * h, 65 | const char *d, char d_is_fnmatch, char d_is_location, 66 | ap_regex_t *regex, const char silent) 67 | 68 | { 69 | hl_entry *nhle; 70 | 71 | /* find tail */ 72 | while (hle && hle->next) 73 | hle = hle->next; 74 | 75 | nhle = (hl_entry *)apr_pcalloc(p, sizeof(hl_entry)); 76 | 77 | nhle->handler = h; 78 | nhle->directory = d; 79 | nhle->d_is_fnmatch = d_is_fnmatch; 80 | nhle->d_is_location = d_is_location; 81 | nhle->regex = regex; 82 | nhle->silent = silent; 83 | 84 | if (hle) 85 | hle->next = nhle; 86 | 87 | return nhle; 88 | } 89 | 90 | /** 91 | ** hlist_copy 92 | ** 93 | */ 94 | 95 | hl_entry *hlist_copy(apr_pool_t *p, const hl_entry *hle) 96 | { 97 | hl_entry *nhle; 98 | hl_entry *head; 99 | 100 | head = (hl_entry *)apr_pcalloc(p, sizeof(hl_entry)); 101 | head->handler = hle->handler; 102 | head->directory = hle->directory; 103 | head->d_is_fnmatch = hle->d_is_fnmatch; 104 | head->d_is_location = hle->d_is_location; 105 | head->regex = hle->regex; 106 | head->silent = hle->silent; 107 | 108 | hle = hle->next; 109 | nhle = head; 110 | while (hle) { 111 | nhle->next = (hl_entry *)apr_pcalloc(p, sizeof(hl_entry)); 112 | nhle = nhle->next; 113 | nhle->handler = hle->handler; 114 | nhle->directory = hle->directory; 115 | nhle->d_is_fnmatch = hle->d_is_fnmatch; 116 | nhle->d_is_location = hle->d_is_location; 117 | nhle->regex = hle->regex; 118 | hle = hle->next; 119 | } 120 | 121 | return head; 122 | } 123 | 124 | /** 125 | ** hlist_extend 126 | ** 127 | */ 128 | 129 | void hlist_extend(apr_pool_t *p, hl_entry *hle1, 130 | const hl_entry *hle2) 131 | { 132 | if (!hle2) 133 | return; 134 | 135 | /* find tail */ 136 | while (hle1 && hle1->next) 137 | hle1 = hle1->next; 138 | 139 | while (hle2) { 140 | hle1->next = (hl_entry *)apr_pcalloc(p, sizeof(hl_entry)); 141 | hle1 = hle1->next; 142 | hle1->handler = hle2->handler; 143 | hle1->directory = hle2->directory; 144 | hle1->d_is_fnmatch = hle2->d_is_fnmatch; 145 | hle1->d_is_location = hle2->d_is_location; 146 | hle1->regex = hle2->regex; 147 | hle1->silent = hle2->silent; 148 | hle2 = hle2->next; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/hlistobject.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * Originally developed by Gregory Trubetskoy. 18 | * 19 | * 20 | * hlist.c 21 | * 22 | * 23 | * See accompanying documentation and source code comments 24 | * for details. 25 | * 26 | */ 27 | 28 | #include "mod_python.h" 29 | 30 | /** 31 | ** MpHList_FromHLEntry() 32 | ** 33 | * new list from hl_entry 34 | */ 35 | 36 | PyObject *MpHList_FromHLEntry(hl_entry *hle) 37 | { 38 | hlistobject *result; 39 | 40 | result = PyObject_New(hlistobject, &MpHList_Type); 41 | if (! result) 42 | PyErr_NoMemory(); 43 | 44 | result->head = hle; 45 | 46 | return (PyObject *) result; 47 | } 48 | 49 | /** 50 | ** hlist_next 51 | ** 52 | */ 53 | 54 | static PyObject *hlist_next(hlistobject *self, PyObject *args) 55 | { 56 | self->head = self->head->next; 57 | 58 | Py_INCREF(Py_None); 59 | return Py_None; 60 | } 61 | 62 | static PyMethodDef hlistmethods[] = { 63 | {"next", (PyCFunction) hlist_next, METH_VARARGS}, 64 | { NULL, NULL } /* sentinel */ 65 | }; 66 | 67 | #define OFF(x) offsetof(hl_entry, x) 68 | 69 | static PyMemberDef hlist_memberlist[] = { 70 | {"handler", T_STRING, OFF(handler), READONLY}, 71 | {"silent", T_INT, OFF(silent), READONLY}, 72 | {"is_location", T_INT, OFF(d_is_location), READONLY}, 73 | {"directory", T_STRING, OFF(directory), READONLY}, 74 | {NULL} /* Sentinel */ 75 | }; 76 | 77 | /** 78 | ** hlist_dealloc 79 | ** 80 | */ 81 | 82 | static void hlist_dealloc(hlistobject *self) 83 | { 84 | PyObject_Del(self); 85 | } 86 | 87 | /** 88 | ** hlist_getattr 89 | ** 90 | */ 91 | 92 | static PyObject *hlist_getattr(hlistobject *self, char *name) 93 | { 94 | PyObject *res; 95 | 96 | PyMethodDef *ml = hlistmethods; 97 | for (; ml->ml_name != NULL; ml++) { 98 | if (name[0] == ml->ml_name[0] && 99 | strcmp(name+1, ml->ml_name+1) == 0) 100 | return PyCFunction_New(ml, (PyObject*)self); 101 | } 102 | 103 | PyErr_Clear(); 104 | 105 | /* when we are at the end of the list, everything 106 | would return None */ 107 | if (! self->head) { 108 | Py_INCREF(Py_None); 109 | return Py_None; 110 | } 111 | 112 | PyMemberDef *md = find_memberdef(hlist_memberlist, name); 113 | if (!md) { 114 | PyErr_SetString(PyExc_AttributeError, name); 115 | return NULL; 116 | } 117 | 118 | return PyMember_GetOne((char*)self->head, md); 119 | } 120 | 121 | /** 122 | ** hlist_repr 123 | ** 124 | * 125 | */ 126 | 127 | static PyObject *hlist_repr(hlistobject *self) 128 | { 129 | PyObject *t; 130 | PyObject *s = PyBytes_FromString("{"); 131 | 132 | if (self->head->handler) { 133 | PyBytes_ConcatAndDel(&s, PyBytes_FromString("'handler':")); 134 | t = PyBytes_FromString(self->head->handler); 135 | PyBytes_ConcatAndDel(&s, MpObject_ReprAsBytes(t)); 136 | Py_XDECREF(t); 137 | } 138 | if (self->head->directory) { 139 | PyBytes_ConcatAndDel(&s, PyBytes_FromString(",'directory':")); 140 | t = PyBytes_FromString(self->head->directory); 141 | PyBytes_ConcatAndDel(&s, MpObject_ReprAsBytes(t)); 142 | Py_XDECREF(t); 143 | } 144 | PyBytes_ConcatAndDel(&s, PyBytes_FromString(",'is_location':")); 145 | if (self->head->d_is_location) 146 | PyBytes_ConcatAndDel(&s, PyBytes_FromString("True")); 147 | else 148 | PyBytes_ConcatAndDel(&s, PyBytes_FromString("False")); 149 | PyBytes_ConcatAndDel(&s, PyBytes_FromString(",'silent':")); 150 | if (self->head->silent) 151 | PyBytes_ConcatAndDel(&s, PyBytes_FromString("1}")); 152 | else 153 | PyBytes_ConcatAndDel(&s, PyBytes_FromString("0}")); 154 | 155 | #if PY_MAJOR_VERSION < 3 156 | return s; 157 | #else 158 | { 159 | PyObject *str = PyUnicode_FromString(PyBytes_AS_STRING(s)); 160 | Py_DECREF(s); 161 | return str; 162 | } 163 | #endif 164 | } 165 | 166 | PyTypeObject MpHList_Type = { 167 | PyVarObject_HEAD_INIT(&PyType_Type, 0) 168 | "mp_hlist", /* tp_name */ 169 | sizeof(hlistobject), /* tp_basicsize */ 170 | 0, /* tp_itemsize */ 171 | (destructor) hlist_dealloc, /* tp_dealloc*/ 172 | 0, /* tp_print*/ 173 | (getattrfunc) hlist_getattr, /* tp_getattr*/ 174 | 0, /* tp_setattr*/ 175 | 0, /* tp_compare*/ 176 | (reprfunc)hlist_repr, /* tp_repr*/ 177 | 0, /* tp_as_number*/ 178 | 0, /* tp_as_sequence*/ 179 | 0, /* tp_as_mapping*/ 180 | 0, /* tp_hash*/ 181 | }; 182 | -------------------------------------------------------------------------------- /src/include/_apachemodule.h: -------------------------------------------------------------------------------- 1 | #ifndef Mp_APACHEMODULE_H 2 | #define Mp_APACHEMODULE_H 3 | 4 | /* 5 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 6 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); you 9 | * may not use this file except in compliance with the License. You 10 | * may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 17 | * implied. See the License for the specific language governing 18 | * permissions and limitations under the License. 19 | * 20 | * Originally developed by Gregory Trubetskoy. 21 | * 22 | * 23 | * apachemodule.h 24 | * 25 | * 26 | * See accompanying documentation and source code comments 27 | * for details. 28 | * 29 | */ 30 | 31 | PyObject *get_ServerReturn(void); 32 | PyMODINIT_FUNC init_apache(void); 33 | 34 | #endif /* !Mp_APACHEMODULE_H */ 35 | -------------------------------------------------------------------------------- /src/include/_pspmodule.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * 18 | */ 19 | 20 | #ifndef __PSP_MODULE_H 21 | #define __PSP_MODULE_H 22 | 23 | #include "Python.h" 24 | 25 | #if PY_MAJOR_VERSION < 3 26 | #define PyBytes_AsString PyString_AsString 27 | #define PyBytes_FromString PyString_FromString 28 | #define MpBytesOrUnicode_FromString PyString_FromString 29 | #else 30 | #define MpBytesOrUnicode_FromString PyUnicode_FromString 31 | #endif 32 | 33 | #endif /* __PSP_MODULE_H */ 34 | -------------------------------------------------------------------------------- /src/include/connobject.h: -------------------------------------------------------------------------------- 1 | #ifndef Mp_CONNOBJECT_H 2 | #define Mp_CONNOBJECT_H 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | /* 8 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 9 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); you 12 | * may not use this file except in compliance with the License. You 13 | * may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 20 | * implied. See the License for the specific language governing 21 | * permissions and limitations under the License. 22 | * 23 | * Originally developed by Gregory Trubetskoy. 24 | * 25 | * 26 | * connobject.h 27 | * 28 | * 29 | */ 30 | 31 | /* 32 | * This is a mapping of a Python object to an Apache table. 33 | * 34 | * This object behaves like a dictionary. Note that the 35 | * underlying table can have duplicate keys, which can never 36 | * happen to a Python dictionary. But this is such a rare thing 37 | * that I can't even think of a possible scenario or implications. 38 | * 39 | */ 40 | 41 | typedef struct connobject { 42 | PyObject_HEAD 43 | conn_rec *conn; 44 | PyObject *base_server; 45 | PyObject *notes; 46 | hlistobject *hlo; 47 | } connobject; 48 | 49 | PyAPI_DATA(PyTypeObject) MpConn_Type; 50 | 51 | #define MpConn_Check(op) (Py_TYPE(op) == &MpConn_Type) 52 | 53 | PyAPI_FUNC(PyObject *) MpConn_FromConn (conn_rec *c); 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | #endif /* !Mp_CONNOBJECT_H */ 59 | -------------------------------------------------------------------------------- /src/include/filterobject.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * Originally developed by Gregory Trubetskoy. 18 | * 19 | * 20 | * filterobject.h 21 | * 22 | * 23 | */ 24 | 25 | #ifndef Mp_FILTEROBJECT_H 26 | #define Mp_FILTEROBJECT_H 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | typedef struct filterobject { 32 | PyObject_HEAD 33 | ap_filter_t *f; 34 | 35 | /* in out refers to the dircetion of data with respect to 36 | filter, not the filter type */ 37 | apr_bucket_brigade *bb_in; 38 | apr_bucket_brigade *bb_out; 39 | 40 | apr_status_t rc; 41 | 42 | int is_input; 43 | ap_input_mode_t mode; 44 | apr_size_t readbytes; 45 | 46 | int closed; 47 | int softspace; 48 | int bytes_written; 49 | 50 | char *handler; 51 | char *dir; 52 | 53 | requestobject *request_obj; 54 | 55 | } filterobject; 56 | 57 | PyAPI_DATA(PyTypeObject) MpFilter_Type; 58 | 59 | #define MpFilter_Check(op) (Py_TYPE(op) == &MpFilter_Type) 60 | 61 | PyAPI_FUNC(PyObject *) 62 | MpFilter_FromFilter (ap_filter_t *f, apr_bucket_brigade *bb_in, 63 | int is_input, ap_input_mode_t mode, 64 | apr_size_t readbytes, char *hadler, char *dir); 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | #endif /* !Mp_FILTEROBJECT_H */ 70 | -------------------------------------------------------------------------------- /src/include/finfoobject.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * Originally developed by Gregory Trubetskoy. 18 | * 19 | * 20 | * finfoobject.h 21 | * 22 | * 23 | */ 24 | 25 | #ifndef Mp_FINFOOBJECT_H 26 | #define Mp_FINFOOBJECT_H 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | typedef struct finfoobject { 32 | PyObject_HEAD 33 | apr_pool_t *pool; 34 | apr_finfo_t *finfo; 35 | } finfoobject; 36 | 37 | PyAPI_DATA(PyTypeObject) MpFinfo_Type; 38 | 39 | #define MpFinfo_Check(op) (Py_TYPE(op) == &MpFinfo_Type) 40 | 41 | PyAPI_FUNC(PyObject *) MpFinfo_FromFinfo (apr_finfo_t *f); 42 | PyAPI_FUNC(PyObject *) MpFinfo_New (void); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | #endif /* !Mp_FINFOOBJECT_H */ 48 | -------------------------------------------------------------------------------- /src/include/hlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * Originally developed by Gregory Trubetskoy. 18 | * 19 | * 20 | * hlist.h 21 | * 22 | * 23 | * See accompanying documentation and source code comments 24 | * for details. 25 | * 26 | */ 27 | 28 | #ifndef Mp_HLIST_H 29 | #define Mp_HLIST_H 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | /* handler list entry */ 35 | typedef struct hl_entry { 36 | const char *handler; 37 | const char *directory; /* directory or location */ 38 | ap_regex_t *regex; 39 | char d_is_fnmatch; 40 | char d_is_location; 41 | char silent; /* 1 for PythonHandlerModule, where 42 | if a handler is not found in a module, 43 | no error should be reported */ 44 | struct hl_entry *next; 45 | } hl_entry; 46 | 47 | hl_entry *hlist_new(apr_pool_t *p, const char *h, const char *d, 48 | char d_is_fnmatch, char d_is_location, 49 | ap_regex_t *regex, const char silent); 50 | hl_entry *hlist_append(apr_pool_t *p, hl_entry *hle, const char * h, 51 | const char *d, char d_is_fnmatch, char d_is_location, 52 | ap_regex_t *regex, const char silent); 53 | 54 | hl_entry *hlist_copy(apr_pool_t *p, const hl_entry *hle); 55 | void hlist_extend(apr_pool_t *p, hl_entry *hle1, const hl_entry *hle2); 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | #endif /* !Mp_HLIST_H */ 61 | -------------------------------------------------------------------------------- /src/include/hlistobject.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * Originally developed by Gregory Trubetskoy. 18 | * 19 | * 20 | * hlistobject.h 21 | * 22 | * 23 | * See accompanying documentation and source code comments 24 | * for details. 25 | * 26 | */ 27 | 28 | #ifndef Mp_HLISTOBJECT_H 29 | #define Mp_HLISTOBJECT_H 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | typedef struct hlist { 35 | PyObject_HEAD 36 | struct hl_entry *head; 37 | } hlistobject; 38 | 39 | PyAPI_DATA(PyTypeObject) MpHList_Type; 40 | 41 | #define MpHList_Check(op) (Py_TYPE(op) == &MpHList_Type) 42 | 43 | PyAPI_FUNC(PyObject *)MpHList_FromHLEntry (hl_entry *hle); 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | #endif /* !Mp_HLISTOBJECT_H */ 49 | -------------------------------------------------------------------------------- /src/include/mod_python.h: -------------------------------------------------------------------------------- 1 | #ifndef Mp_MOD_PYTHON_H 2 | #define Mp_MOD_PYTHON_H 3 | 4 | /* 5 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 6 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); you 9 | * may not use this file except in compliance with the License. You 10 | * may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 17 | * implied. See the License for the specific language governing 18 | * permissions and limitations under the License. 19 | * 20 | * Originally developed by Gregory Trubetskoy. 21 | * 22 | * 23 | * mod_python.h 24 | * 25 | * See accompanying documentation and source code comments 26 | * for details. 27 | * 28 | */ 29 | 30 | /* 31 | * 32 | * 33 | * DO NOT EDIT - DO NOT EDIT - DO NOT EDIT - DO NOT EDIT 34 | * 35 | * 36 | * 37 | * If you are looking at mod_python.h, it is an auto-generated file on 38 | * UNIX. This file is kept around for the Win32 platform which 39 | * does not use autoconf. Any changes to mod_python.h must also be 40 | * reflected in mod_python.h.in. 41 | */ 42 | 43 | /* Python headers */ 44 | #define PY_SSIZE_T_CLEAN 45 | #include "Python.h" 46 | #include "structmember.h" 47 | 48 | /* Apache headers */ 49 | #include "httpd.h" 50 | #define CORE_PRIVATE 51 | #include "http_config.h" 52 | #include "http_core.h" 53 | #include "http_main.h" 54 | #include "http_connection.h" 55 | #include "http_protocol.h" 56 | #include "http_request.h" 57 | #include "util_script.h" 58 | #include "util_filter.h" 59 | #include "http_log.h" 60 | #include "apr_strings.h" 61 | #include "apr_lib.h" 62 | #include "apr_hash.h" 63 | #include "apr_fnmatch.h" 64 | #include "scoreboard.h" 65 | #include "ap_mpm.h" 66 | #include "ap_mmn.h" 67 | #include "mod_include.h" 68 | #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) 69 | #include "unixd.h" 70 | #endif 71 | 72 | #if !AP_MODULE_MAGIC_AT_LEAST(20050127,0) 73 | /* Debian backported ap_regex_t to Apache 2.0 and 74 | * thus made official version checking break. */ 75 | #ifndef AP_REG_EXTENDED 76 | typedef regex_t ap_regex_t; 77 | #define AP_REG_EXTENDED REG_EXTENDED 78 | #define AP_REG_ICASE REG_ICASE 79 | #endif 80 | #endif 81 | 82 | #if defined(WIN32) && !defined(WITH_THREAD) 83 | #error Python threading must be enabled on Windows 84 | #endif 85 | 86 | #if !defined(WIN32) 87 | #include 88 | #endif 89 | 90 | /* pool given to us in ChildInit. We use it for 91 | server.register_cleanup() */ 92 | extern apr_pool_t *child_init_pool; 93 | 94 | /* Apache module declaration */ 95 | extern module AP_MODULE_DECLARE_DATA python_module; 96 | 97 | #include "util.h" 98 | #include "hlist.h" 99 | #include "hlistobject.h" 100 | #include "tableobject.h" 101 | #include "serverobject.h" 102 | #include "connobject.h" 103 | #include "_apachemodule.h" 104 | #include "requestobject.h" 105 | #include "filterobject.h" 106 | #include "finfoobject.h" 107 | 108 | /** Things specific to mod_python, as an Apache module **/ 109 | 110 | #if PY_MAJOR_VERSION < 3 111 | 112 | #define PyBytesObject PyStringObject 113 | #define PyBytes_Check PyString_Check 114 | #define PyBytes_CheckExact PyString_CheckExact 115 | #define PyBytes_FromString PyString_FromString 116 | #define PyBytes_FromStringAndSize PyString_FromStringAndSize 117 | #define PyBytes_AS_STRING PyString_AS_STRING 118 | #define PyBytes_ConcatAndDel PyString_ConcatAndDel 119 | #define PyBytes_Size PyString_Size 120 | #define _PyBytes_Resize _PyString_Resize 121 | #define MpObject_ReprAsBytes PyObject_Repr 122 | #define MpBytesOrUnicode_FromString PyString_FromString 123 | 124 | #ifndef PyVarObject_HEAD_INIT 125 | #define PyVarObject_HEAD_INIT(type, size) \ 126 | PyObject_HEAD_INIT(type) size, 127 | #endif 128 | 129 | #ifndef Py_TYPE 130 | #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) 131 | #endif 132 | 133 | #else 134 | 135 | #define MpBytesOrUnicode_FromString PyUnicode_FromString 136 | 137 | #endif /* PY_MAJOR_VERSION < 3 */ 138 | 139 | #define MP_CONFIG_KEY "mod_python_config" 140 | #define MAIN_INTERPRETER "main_interpreter" 141 | #define FILTER_NAME "MOD_PYTHON" 142 | 143 | /* used in python_directive_handler */ 144 | #define SILENT 1 145 | #define NOTSILENT 0 146 | 147 | /* MAX_LOCKS can now be set as a configure option 148 | * ./configure --with-max-locks=INTEGER 149 | */ 150 | #define MAX_LOCKS 8 151 | 152 | /* MUTEX_DIR can be set as a configure option 153 | * ./configure --with-mutex-dir=/path/to/dir 154 | */ 155 | #define MUTEX_DIR "/tmp" 156 | 157 | /* version stuff */ 158 | extern const int mp_version_major; 159 | extern const int mp_version_minor; 160 | extern const int mp_version_patch; 161 | extern const char * const mp_version_string; 162 | extern const char * const mp_version_component; 163 | 164 | /* structure to hold interpreter data */ 165 | typedef struct { 166 | apr_array_header_t * tstates; /* tstates available for use */ 167 | PyInterpreterState *interp; 168 | PyObject *obcallback; 169 | } interpreterdata; 170 | 171 | /* global configuration parameters */ 172 | typedef struct 173 | { 174 | apr_global_mutex_t **g_locks; 175 | int nlocks; 176 | int parent_pid; 177 | } py_global_config; 178 | 179 | /* structure describing per directory configuration parameters */ 180 | typedef struct { 181 | int authoritative; 182 | char *config_dir; 183 | char d_is_location; 184 | apr_table_t *directives; 185 | apr_table_t *options; 186 | apr_hash_t *hlists; /* hlists for every phase */ 187 | apr_hash_t *in_filters; 188 | apr_hash_t *out_filters; 189 | apr_table_t *imports; /* for PythonImport */ 190 | } py_config; 191 | 192 | /* register_cleanup info */ 193 | typedef struct 194 | { 195 | request_rec *request_rec; 196 | server_rec *server_rec; 197 | PyObject *handler; 198 | const char *interpreter; 199 | PyObject *data; 200 | } cleanup_info; 201 | 202 | /* request config structure */ 203 | typedef struct 204 | { 205 | requestobject *request_obj; 206 | apr_hash_t *dynhls; /* dynamically registered handlers 207 | for this request */ 208 | apr_hash_t *in_filters; /* dynamically registered input filters 209 | for this request */ 210 | apr_hash_t *out_filters; /* dynamically registered output filters 211 | for this request */ 212 | 213 | } py_req_config; 214 | 215 | /* filter context */ 216 | typedef struct 217 | { 218 | char *name; 219 | int transparent; 220 | } python_filter_ctx; 221 | 222 | /* a structure to hold a handler, 223 | used in configuration for filters */ 224 | typedef struct 225 | { 226 | char *handler; 227 | char *directory; 228 | unsigned d_is_fnmatch : 1; 229 | unsigned d_is_location : 1; 230 | ap_regex_t *regex; 231 | } py_handler; 232 | 233 | apr_status_t python_cleanup(void *data); 234 | PyObject* python_interpreter_name(void); 235 | requestobject *python_get_request_object(request_rec *req, const char *phase); 236 | PyObject *_apache_module_init(); 237 | 238 | APR_DECLARE_OPTIONAL_FN(PyInterpreterState *, mp_acquire_interpreter, (const char *)); 239 | APR_DECLARE_OPTIONAL_FN(void, mp_release_interpreter, ()); 240 | APR_DECLARE_OPTIONAL_FN(PyObject *, mp_get_request_object, (request_rec *)); 241 | APR_DECLARE_OPTIONAL_FN(PyObject *, mp_get_server_object, (server_rec *)); 242 | APR_DECLARE_OPTIONAL_FN(PyObject *, mp_get_connection_object, (conn_rec *)); 243 | 244 | /* This macro assigns a C string representation of PyObject *obj to 245 | * char *str. obj must be a Unicode Latin1 or Bytes. It will try its 246 | * best to accomplish this with zero-copy. WARNING - it DECREFs 247 | * (unless obj_is_borrowed) and changes the value of obj when it is 248 | * unicode that must be recoded, so do not use obj afterwards other 249 | * than to DECREF it - it may not be what you expected. You MUST 250 | * Py_DECREF(obj) afterward (even if error), but not before you're 251 | * done with the value of str. Note that if the obj reference was 252 | * borrowed, and without the macro you wouldn't be DECREFing it, you 253 | * should indicate that by setting obj_is_borrowed to 1 and DECREF 254 | * it. If after this macro str is NULL, then a TypeError error has 255 | * been set by the macro. 256 | */ 257 | #if PY_MAJOR_VERSION < 3 258 | #define PyUnicode_1BYTE_KIND NULL 259 | #define PyUnicode_KIND(str) NULL 260 | #define PyUnicode_1BYTE_DATA(obj) "" 261 | #endif 262 | #define MP_ANYSTR_AS_STR(str, obj, obj_is_borrowed) do { \ 263 | str = NULL; \ 264 | if (PyUnicode_CheckExact(obj)) { \ 265 | if (PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 && \ 266 | PyUnicode_KIND(obj) == PyUnicode_1BYTE_KIND) { \ 267 | if (obj_is_borrowed) Py_INCREF(obj); /* so DECREF ok */ \ 268 | str = PyUnicode_1BYTE_DATA(obj); \ 269 | } else { \ 270 | PyObject *latin = PyUnicode_AsLatin1String(obj); \ 271 | if (latin) { \ 272 | str = PyBytes_AsString(latin); /* #define on 2.6 */ \ 273 | if (!obj_is_borrowed) Py_DECREF(obj); \ 274 | obj = latin; /* remember to DECREF me! */ \ 275 | } \ 276 | } \ 277 | } else if (PyBytes_CheckExact(obj)) { /* #define on 2.6 */ \ 278 | str = PyBytes_AsString(obj); /* #define on 2.6 */ \ 279 | if (obj_is_borrowed) Py_INCREF(obj); /* so DECREF ok */ \ 280 | } \ 281 | if (!str) { \ 282 | if (obj_is_borrowed) Py_INCREF(obj); /* so DECREF ok */ \ 283 | PyErr_SetString(PyExc_TypeError, \ 284 | "not an ISO-8859-1 string"); \ 285 | } \ 286 | } while (0) 287 | 288 | #ifndef MpObject_ReprAsBytes 289 | static inline PyObject *MpObject_ReprAsBytes(PyObject *o) { 290 | PyObject *result; 291 | PyObject *ucode = PyObject_Repr(o); 292 | /* we can do this because repr() should never have non-ascii characters XXX (really?) */ 293 | char *c = PyUnicode_1BYTE_DATA(ucode); 294 | if (c[0] == 'b') 295 | result = PyBytes_FromStringAndSize(PyUnicode_1BYTE_DATA(ucode)+1, PyUnicode_GET_LENGTH(ucode)-1); 296 | else 297 | result = PyBytes_FromStringAndSize(PyUnicode_1BYTE_DATA(ucode), PyUnicode_GET_LENGTH(ucode)); 298 | Py_DECREF(ucode); 299 | return result; 300 | } 301 | #endif 302 | 303 | #endif /* !Mp_MOD_PYTHON_H */ 304 | 305 | /* 306 | # makes emacs go into C mode 307 | ### Local Variables: 308 | ### mode:c 309 | ### End: 310 | */ 311 | -------------------------------------------------------------------------------- /src/include/mod_python.h.in: -------------------------------------------------------------------------------- 1 | #ifndef Mp_MOD_PYTHON_H 2 | #define Mp_MOD_PYTHON_H 3 | 4 | /* 5 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 6 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); you 9 | * may not use this file except in compliance with the License. You 10 | * may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 17 | * implied. See the License for the specific language governing 18 | * permissions and limitations under the License. 19 | * 20 | * Originally developed by Gregory Trubetskoy. 21 | * 22 | * 23 | * mod_python.h 24 | * 25 | * $Id: mod_python.h 231054 2005-08-09 15:37:04Z jgallacher $ 26 | * 27 | * See accompanying documentation and source code comments 28 | * for details. 29 | * 30 | */ 31 | 32 | /* 33 | * 34 | * 35 | * DO NOT EDIT - DO NOT EDIT - DO NOT EDIT - DO NOT EDIT 36 | * 37 | * 38 | * 39 | * If you are looking at mod_python.h, it is an auto-generated file on 40 | * UNIX. This file is kept around for the Win32 platform which 41 | * does not use autoconf. Any changes to mod_python.h must also be 42 | * reflected in mod_python.h.in. 43 | */ 44 | 45 | /* Python headers */ 46 | #define PY_SSIZE_T_CLEAN 47 | #include "Python.h" 48 | #include "structmember.h" 49 | 50 | /* Apache headers */ 51 | #include "httpd.h" 52 | #define CORE_PRIVATE 53 | #include "http_config.h" 54 | #include "http_core.h" 55 | #include "http_main.h" 56 | #include "http_connection.h" 57 | #include "http_protocol.h" 58 | #include "http_request.h" 59 | #include "util_script.h" 60 | #include "util_filter.h" 61 | #include "http_log.h" 62 | #include "apr_strings.h" 63 | #include "apr_lib.h" 64 | #include "apr_hash.h" 65 | #include "apr_fnmatch.h" 66 | #include "scoreboard.h" 67 | #include "ap_mpm.h" 68 | #include "ap_mmn.h" 69 | #include "mod_include.h" 70 | #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) 71 | #include "unixd.h" 72 | #endif 73 | 74 | #if !AP_MODULE_MAGIC_AT_LEAST(20050127,0) 75 | /* Debian backported ap_regex_t to Apache 2.0 and 76 | * thus made official version checking break. */ 77 | #ifndef AP_REG_EXTENDED 78 | typedef regex_t ap_regex_t; 79 | #define AP_REG_EXTENDED REG_EXTENDED 80 | #define AP_REG_ICASE REG_ICASE 81 | #endif 82 | #endif 83 | 84 | #if defined(WIN32) && !defined(WITH_THREAD) 85 | #error Python threading must be enabled on Windows 86 | #endif 87 | 88 | #if !defined(WIN32) 89 | #include 90 | #endif 91 | 92 | /* pool given to us in ChildInit. We use it for 93 | server.register_cleanup() */ 94 | extern apr_pool_t *child_init_pool; 95 | 96 | /* Apache module declaration */ 97 | extern module AP_MODULE_DECLARE_DATA python_module; 98 | 99 | #include "util.h" 100 | #include "hlist.h" 101 | #include "hlistobject.h" 102 | #include "tableobject.h" 103 | #include "serverobject.h" 104 | #include "connobject.h" 105 | #include "_apachemodule.h" 106 | #include "requestobject.h" 107 | #include "filterobject.h" 108 | #include "finfoobject.h" 109 | 110 | /** Things specific to mod_python, as an Apache module **/ 111 | 112 | #if PY_MAJOR_VERSION < 3 113 | 114 | #define PyBytesObject PyStringObject 115 | #define PyBytes_Check PyString_Check 116 | #define PyBytes_CheckExact PyString_CheckExact 117 | #define PyBytes_FromString PyString_FromString 118 | #define PyBytes_FromStringAndSize PyString_FromStringAndSize 119 | #define PyBytes_AS_STRING PyString_AS_STRING 120 | #define PyBytes_ConcatAndDel PyString_ConcatAndDel 121 | #define PyBytes_Size PyString_Size 122 | #define _PyBytes_Resize _PyString_Resize 123 | #define MpObject_ReprAsBytes PyObject_Repr 124 | #define MpBytesOrUnicode_FromString PyString_FromString 125 | 126 | #ifndef PyVarObject_HEAD_INIT 127 | #define PyVarObject_HEAD_INIT(type, size) \ 128 | PyObject_HEAD_INIT(type) size, 129 | #endif 130 | 131 | #ifndef Py_TYPE 132 | #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) 133 | #endif 134 | 135 | #else 136 | 137 | #define MpBytesOrUnicode_FromString PyUnicode_FromString 138 | 139 | #endif /* PY_MAJOR_VERSION < 3 */ 140 | 141 | #define MP_CONFIG_KEY "mod_python_config" 142 | #define MAIN_INTERPRETER "main_interpreter" 143 | #define FILTER_NAME "MOD_PYTHON" 144 | 145 | /* used in python_directive_handler */ 146 | #define SILENT 1 147 | #define NOTSILENT 0 148 | 149 | /* MAX_LOCKS can now be set as a configure option 150 | * ./configure --with-max-locks=INTEGER 151 | */ 152 | #define MAX_LOCKS @MAX_LOCKS@ 153 | 154 | /* MUTEX_DIR can be set as a configure option 155 | * ./configure --with-mutex-dir=/path/to/dir 156 | */ 157 | #define MUTEX_DIR "@MUTEX_DIR@" 158 | 159 | /* version stuff */ 160 | extern const int mp_version_major; 161 | extern const int mp_version_minor; 162 | extern const int mp_version_patch; 163 | extern const char * const mp_version_string; 164 | extern const char * const mp_version_component; 165 | 166 | /* structure to hold interpreter data */ 167 | typedef struct { 168 | apr_array_header_t * tstates; /* tstates available for use */ 169 | PyInterpreterState *interp; 170 | PyObject *obcallback; 171 | } interpreterdata; 172 | 173 | /* global configuration parameters */ 174 | typedef struct 175 | { 176 | apr_global_mutex_t **g_locks; 177 | int nlocks; 178 | int parent_pid; 179 | } py_global_config; 180 | 181 | /* structure describing per directory configuration parameters */ 182 | typedef struct { 183 | int authoritative; 184 | char *config_dir; 185 | char d_is_location; 186 | apr_table_t *directives; 187 | apr_table_t *options; 188 | apr_hash_t *hlists; /* hlists for every phase */ 189 | apr_hash_t *in_filters; 190 | apr_hash_t *out_filters; 191 | apr_table_t *imports; /* for PythonImport */ 192 | } py_config; 193 | 194 | /* register_cleanup info */ 195 | typedef struct 196 | { 197 | request_rec *request_rec; 198 | server_rec *server_rec; 199 | PyObject *handler; 200 | const char *interpreter; 201 | PyObject *data; 202 | } cleanup_info; 203 | 204 | /* request config structure */ 205 | typedef struct 206 | { 207 | requestobject *request_obj; 208 | apr_hash_t *dynhls; /* dynamically registered handlers 209 | for this request */ 210 | apr_hash_t *in_filters; /* dynamically registered input filters 211 | for this request */ 212 | apr_hash_t *out_filters; /* dynamically registered output filters 213 | for this request */ 214 | 215 | } py_req_config; 216 | 217 | /* filter context */ 218 | typedef struct 219 | { 220 | char *name; 221 | int transparent; 222 | } python_filter_ctx; 223 | 224 | /* a structure to hold a handler, 225 | used in configuration for filters */ 226 | typedef struct 227 | { 228 | char *handler; 229 | char *directory; 230 | unsigned d_is_fnmatch : 1; 231 | unsigned d_is_location : 1; 232 | ap_regex_t *regex; 233 | } py_handler; 234 | 235 | apr_status_t python_cleanup(void *data); 236 | PyObject* python_interpreter_name(void); 237 | requestobject *python_get_request_object(request_rec *req, const char *phase); 238 | PyObject *_apache_module_init(); 239 | 240 | APR_DECLARE_OPTIONAL_FN(PyInterpreterState *, mp_acquire_interpreter, (const char *)); 241 | APR_DECLARE_OPTIONAL_FN(void, mp_release_interpreter, ()); 242 | APR_DECLARE_OPTIONAL_FN(PyObject *, mp_get_request_object, (request_rec *)); 243 | APR_DECLARE_OPTIONAL_FN(PyObject *, mp_get_server_object, (server_rec *)); 244 | APR_DECLARE_OPTIONAL_FN(PyObject *, mp_get_connection_object, (conn_rec *)); 245 | 246 | /* This macro assigns a C string representation of PyObject *obj to 247 | * char *str. obj must be a Unicode Latin1 or Bytes. It will try its 248 | * best to accomplish this with zero-copy. WARNING - it DECREFs 249 | * (unless obj_is_borrowed) and changes the value of obj when it is 250 | * unicode that must be recoded, so do not use obj afterwards other 251 | * than to DECREF it - it may not be what you expected. You MUST 252 | * Py_DECREF(obj) afterward (even if error), but not before you're 253 | * done with the value of str. Note that if the obj reference was 254 | * borrowed, and without the macro you wouldn't be DECREFing it, you 255 | * should indicate that by setting obj_is_borrowed to 1 and DECREF 256 | * it. If after this macro str is NULL, then a TypeError error has 257 | * been set by the macro. 258 | */ 259 | #if PY_MAJOR_VERSION < 3 260 | #define PyUnicode_1BYTE_KIND NULL 261 | #define PyUnicode_KIND(str) NULL 262 | #define PyUnicode_1BYTE_DATA(obj) "" 263 | #endif 264 | #define MP_ANYSTR_AS_STR(str, obj, obj_is_borrowed) do { \ 265 | str = NULL; \ 266 | if (PyUnicode_CheckExact(obj)) { \ 267 | if (PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 && \ 268 | PyUnicode_KIND(obj) == PyUnicode_1BYTE_KIND) { \ 269 | if (obj_is_borrowed) Py_INCREF(obj); /* so DECREF ok */ \ 270 | str = PyUnicode_1BYTE_DATA(obj); \ 271 | } else { \ 272 | PyObject *latin = PyUnicode_AsLatin1String(obj); \ 273 | if (latin) { \ 274 | str = PyBytes_AsString(latin); /* #define on 2.6 */ \ 275 | if (!obj_is_borrowed) Py_DECREF(obj); \ 276 | obj = latin; /* remember to DECREF me! */ \ 277 | } \ 278 | } \ 279 | } else if (PyBytes_CheckExact(obj)) { /* #define on 2.6 */ \ 280 | str = PyBytes_AsString(obj); /* #define on 2.6 */ \ 281 | if (obj_is_borrowed) Py_INCREF(obj); /* so DECREF ok */ \ 282 | } \ 283 | if (!str) { \ 284 | if (obj_is_borrowed) Py_INCREF(obj); /* so DECREF ok */ \ 285 | PyErr_SetString(PyExc_TypeError, \ 286 | "not an ISO-8859-1 string"); \ 287 | } \ 288 | } while (0) 289 | 290 | #ifndef MpObject_ReprAsBytes 291 | static inline PyObject *MpObject_ReprAsBytes(PyObject *o) { 292 | PyObject *result; 293 | PyObject *ucode = PyObject_Repr(o); 294 | /* we can do this because repr() should never have non-ascii characters XXX (really?) */ 295 | char *c = PyUnicode_1BYTE_DATA(ucode); 296 | if (c[0] == 'b') 297 | result = PyBytes_FromStringAndSize(PyUnicode_1BYTE_DATA(ucode)+1, PyUnicode_GET_LENGTH(ucode)-1); 298 | else 299 | result = PyBytes_FromStringAndSize(PyUnicode_1BYTE_DATA(ucode), PyUnicode_GET_LENGTH(ucode)); 300 | Py_DECREF(ucode); 301 | return result; 302 | } 303 | #endif 304 | 305 | #endif /* !Mp_MOD_PYTHON_H */ 306 | 307 | /* 308 | # makes emacs go into C mode 309 | ### Local Variables: 310 | ### mode:c 311 | ### End: 312 | */ 313 | -------------------------------------------------------------------------------- /src/include/mp_version.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * This is the one and only source of version information. 4 | * 5 | * Do not include this file directly, it is available via 6 | * auto-generated version.c, which also includes the git sha. 7 | */ 8 | 9 | #define MP_VERSION_MAJOR 3 10 | #define MP_VERSION_MINOR 5 11 | #define MP_VERSION_PATCH 0 12 | 13 | -------------------------------------------------------------------------------- /src/include/psp_parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef __PSP_PARSER_H 20 | #define __PSP_PARSER_H 21 | 22 | /* This is to remove a compiler warning. Ideall there should be a way to 23 | * to include Python.h before system headers, but because psp_parse.c is 24 | * autogenerated by flex, I couldn't think of a simple way to do it. 25 | */ 26 | #if defined(_POSIX_C_SOURCE) 27 | #undef _POSIX_C_SOURCE 28 | #endif 29 | 30 | #include 31 | #include "psp_string.h" 32 | 33 | #define STATIC_STR(s) s, sizeof(s)-1 34 | 35 | #define PSP_PG(v) (((psp_parser_t*)yyget_extra(yyscanner))->v) 36 | 37 | typedef struct { 38 | /* PyObject *files; XXX removed until cache is fixed */ 39 | psp_string whitespace; 40 | psp_string pycode; 41 | char * dir; 42 | unsigned is_psp_echo : 1; 43 | unsigned after_colon : 1; 44 | unsigned seen_newline : 1; 45 | } psp_parser_t; 46 | 47 | #endif /* __PSP_PARSER_H */ 48 | -------------------------------------------------------------------------------- /src/include/psp_string.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * 18 | */ 19 | 20 | #ifndef __PSP_STRING_H 21 | #define __PSP_STRING_H 22 | 23 | #include 24 | #include 25 | 26 | #ifndef PSP_STRING_BLOCK 27 | #define PSP_STRING_BLOCK 256 28 | #endif 29 | 30 | typedef struct { 31 | size_t allocated; 32 | size_t length; 33 | char *blob; 34 | } psp_string; 35 | 36 | void psp_string_0(psp_string *); 37 | void psp_string_appendl(psp_string *, char *, size_t); 38 | void psp_string_append(psp_string *, char *); 39 | void psp_string_appendc(psp_string *, char); 40 | void psp_string_clear(psp_string *); 41 | void psp_string_free(psp_string *); 42 | 43 | #endif /* __PSP_STRING_H */ 44 | -------------------------------------------------------------------------------- /src/include/requestobject.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * Originally developed by Gregory Trubetskoy. 18 | * 19 | * 20 | * requestobject.h 21 | * 22 | * 23 | */ 24 | 25 | #ifndef Mp_REQUESTOBJECT_H 26 | #define Mp_REQUESTOBJECT_H 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | typedef struct requestobject { 32 | PyObject_HEAD 33 | PyObject * dict; 34 | request_rec * request_rec; 35 | PyObject * connection; 36 | PyObject * server; 37 | PyObject * headers_in; 38 | PyObject * headers_out; 39 | PyObject * err_headers_out; 40 | PyObject * subprocess_env; 41 | PyObject * notes; 42 | PyObject * phase; 43 | PyObject * config; 44 | PyObject * options; 45 | char * extension; /* for | .ext syntax */ 46 | int content_type_set; 47 | apr_off_t bytes_queued; 48 | hlistobject * hlo; 49 | char * rbuff; /* read bufer */ 50 | int rbuff_len; /* read buffer size */ 51 | int rbuff_pos; /* position into the buffer */ 52 | PyObject * session; 53 | 54 | } requestobject; 55 | 56 | PyAPI_DATA(PyTypeObject) MpRequest_Type; 57 | 58 | #define MpRequest_Check(op) (Py_TYPE(op) == &MpRequest_Type) 59 | 60 | PyAPI_FUNC(PyObject *) MpRequest_FromRequest (request_rec *r); 61 | 62 | #ifndef ap_is_HTTP_VALID_RESPONSE 63 | #define ap_is_HTTP_VALID_RESPONSE(x) (((x) >= 100)&&((x) < 600)) 64 | #endif 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | #endif /* !Mp_REQUESTOBJECT_H */ 70 | -------------------------------------------------------------------------------- /src/include/serverobject.h: -------------------------------------------------------------------------------- 1 | #ifndef Mp_SERVEROBJECT_H 2 | #define Mp_SERVEROBJECT_H 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | /* 8 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 9 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); you 12 | * may not use this file except in compliance with the License. You 13 | * may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 20 | * implied. See the License for the specific language governing 21 | * permissions and limitations under the License. 22 | * 23 | * Originally developed by Gregory Trubetskoy. 24 | * 25 | * 26 | * serverobject.h 27 | * 28 | * 29 | */ 30 | 31 | typedef struct serverobject { 32 | PyObject_HEAD 33 | PyObject *dict; 34 | server_rec *server; 35 | PyObject *next; 36 | } serverobject; 37 | 38 | PyAPI_DATA(PyTypeObject) MpServer_Type; 39 | 40 | #define MpServer_Check(op) (Py_TYPE(op) == &MpServer_Type) 41 | 42 | PyAPI_FUNC(PyObject *) MpServer_FromServer (server_rec *s); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | #endif /* !Mp_SERVEROBJECT_H */ 48 | -------------------------------------------------------------------------------- /src/include/tableobject.h: -------------------------------------------------------------------------------- 1 | #ifndef Mp_TABLEOBJECT_H 2 | #define Mp_TABLEOBJECT_H 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | /* 8 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 9 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); you 12 | * may not use this file except in compliance with the License. You 13 | * may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 20 | * implied. See the License for the specific language governing 21 | * permissions and limitations under the License. 22 | * 23 | * Originally developed by Gregory Trubetskoy. 24 | * 25 | * 26 | * tableobject.h 27 | * 28 | * 29 | */ 30 | 31 | /* 32 | * This is a mapping of a Python object to an Apache table. 33 | * 34 | */ 35 | 36 | typedef struct tableobject { 37 | PyObject_VAR_HEAD 38 | apr_table_t *table; 39 | apr_pool_t *pool; 40 | } tableobject; 41 | 42 | PyAPI_DATA(PyTypeObject) MpTable_Type; 43 | PyAPI_DATA(PyTypeObject) MpTableIter_Type; 44 | 45 | #define MpTable_Check(op) (Py_TYPE(op) == &MpTable_Type) 46 | 47 | PyAPI_FUNC(PyObject *) MpTable_FromTable (apr_table_t *t); 48 | PyAPI_FUNC(PyObject *) MpTable_New (void); 49 | 50 | /* #define DEBUG_TABLES 1 */ 51 | #ifdef DEBUG_TABLES 52 | #define TABLE_DEBUG(str) printf("mp_table: %s\n", str) 53 | #else 54 | #define TABLE_DEBUG(str) 55 | #endif 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | #endif /* !Mp_TABLEOBJECT_H */ 61 | -------------------------------------------------------------------------------- /src/include/util.h: -------------------------------------------------------------------------------- 1 | #ifndef Mp_UTIL_H 2 | #define Mp_UTIL_H 3 | 4 | /* 5 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 6 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); you 9 | * may not use this file except in compliance with the License. You 10 | * may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 17 | * implied. See the License for the specific language governing 18 | * permissions and limitations under the License. 19 | * 20 | * Originally developed by Gregory Trubetskoy. 21 | * 22 | * 23 | * util.h 24 | * 25 | * 26 | * See accompanying documentation and source code comments 27 | * for details. 28 | * 29 | */ 30 | 31 | PyObject * tuple_from_array_header(const apr_array_header_t *ah); 32 | PyObject * tuple_from_method_list(const ap_method_list_t *l); 33 | PyObject *tuple_from_finfo(apr_finfo_t *f); 34 | PyObject *tuple_from_apr_uri(apr_uri_t *u); 35 | char * get_addhandler_extensions(request_rec *req); 36 | apr_status_t python_decref(void *object); 37 | PyMemberDef *find_memberdef(const PyMemberDef *mlist, const char *name); 38 | PyObject *cfgtree_walk(ap_directive_t *dir); 39 | PyObject *makesockaddr(struct apr_sockaddr_t *addr); 40 | 41 | #endif /* !Mp_UTIL_H */ 42 | -------------------------------------------------------------------------------- /src/mod_python.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 12 | 13 | 14 | 21 | 37 | 39 | 49 | 56 | 58 | 60 | 62 | 66 | 68 | 70 | 71 | 72 | 73 | 76 | 78 | 79 | 81 | 82 | 84 | 85 | 87 | 88 | 90 | 91 | 93 | 94 | 96 | 97 | 99 | 100 | 102 | 103 | 104 | 106 | 107 | 109 | 110 | 112 | 113 | 115 | 116 | 118 | 119 | 121 | 122 | 124 | 125 | 127 | 128 | 130 | 131 | 133 | 134 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/psp_parser.l: -------------------------------------------------------------------------------- 1 | %{ 2 | /* 3 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 4 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you 7 | * may not use this file except in compliance with the License. You 8 | * may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 | * implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | * 18 | * 19 | * This file originally written by Sterling Hughes. 20 | * 21 | */ 22 | 23 | /* NOTE The seemingly unusual generated Python code (sometime using 24 | * ";" to separate statements, newline placement, etc) is such that 25 | * for vast majority of cases the line number of the input file will 26 | * match the line number of the output! 27 | */ 28 | 29 | #include "psp_parser.h" 30 | 31 | #define OUTPUT_WHITESPACE(__wsstring) \ 32 | psp_string_0((__wsstring)); \ 33 | psp_string_append(&PSP_PG(pycode), (__wsstring)->blob) 34 | 35 | #define CLEAR_WHITESPACE(__wsstring) psp_string_clear((__wsstring)); 36 | 37 | %} 38 | 39 | %option noyywrap nounistd 40 | 41 | %x TEXT 42 | %x PYCODE 43 | %x INDENT 44 | %x DIR 45 | %x COMMENT 46 | 47 | %% 48 | 49 | \r\n|\n { 50 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("req.write(\"\"\"")); 51 | 52 | yyless(0); 53 | BEGIN TEXT; 54 | } 55 | 56 | . { 57 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("req.write(\"\"\"")); 58 | 59 | yyless(0); 60 | BEGIN TEXT; 61 | } 62 | 63 | "\\n" { 64 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("\\\\n")); 65 | } 66 | 67 | "\\r" { 68 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("\\\\r")); 69 | } 70 | 71 | "\\t" { 72 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("\\\\t")); 73 | } 74 | 75 | "<%=" { /* expression */ 76 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("\"\"\",0); req.write(str(")); 77 | PSP_PG(is_psp_echo) = 1; 78 | 79 | BEGIN PYCODE; 80 | } 81 | 82 | "<%" { /* python code */ 83 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("\"\"\",0);")); 84 | CLEAR_WHITESPACE(&PSP_PG(whitespace)); 85 | PSP_PG(seen_newline) = 0; 86 | BEGIN PYCODE; 87 | } 88 | 89 | "<%@" { /* directive */ 90 | BEGIN DIR; 91 | } 92 | 93 | "<%--" { /* comment */ 94 | BEGIN COMMENT; 95 | } 96 | 97 | \r\n|\n { 98 | psp_string_appendc(&PSP_PG(pycode), '\n'); 99 | } 100 | 101 | . { 102 | if (yytext[0] == '"') { 103 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("\\\"")); 104 | } else { 105 | psp_string_appendc(&PSP_PG(pycode), yytext[0]); 106 | } 107 | } 108 | 109 | <> { 110 | yypop_buffer_state(yyscanner); 111 | if (!YY_CURRENT_BUFFER) { 112 | /* this is really the end */ 113 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("\"\"\",0)\n")); 114 | yyterminate(); 115 | } 116 | else { 117 | /* we are inside include, continue scanning */ 118 | BEGIN DIR; 119 | } 120 | } 121 | 122 | \r\n|\n|\r { 123 | psp_string_appendc(&PSP_PG(pycode), '\n'); 124 | 125 | PSP_PG(seen_newline) = 1; 126 | BEGIN INDENT; 127 | } 128 | 129 | "%>" { 130 | 131 | if (PSP_PG(is_psp_echo)) { 132 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("),0); req.write(\"\"\"")); 133 | PSP_PG(is_psp_echo) = 0; 134 | } 135 | else { 136 | if (!PSP_PG(seen_newline)) { 137 | /* this will happen is you have <%%> */ 138 | psp_string_appendc(&PSP_PG(pycode), ';'); 139 | } 140 | 141 | if (PSP_PG(after_colon)) { 142 | /* this is dumb mistake-proof measure, if %> 143 | is immediately following where there should be an indent */ 144 | psp_string_appendc(&PSP_PG(whitespace), '\t'); 145 | PSP_PG(after_colon) = 0; 146 | } 147 | OUTPUT_WHITESPACE(&PSP_PG(whitespace)); 148 | psp_string_appendl(&PSP_PG(pycode), STATIC_STR("req.write(\"\"\"")); 149 | } 150 | 151 | BEGIN TEXT; 152 | } 153 | 154 | ":" { 155 | psp_string_appendc(&PSP_PG(pycode), yytext[0]); 156 | PSP_PG(after_colon) = 1; 157 | } 158 | 159 | . { 160 | psp_string_appendc(&PSP_PG(pycode), yytext[0]); 161 | PSP_PG(after_colon) = 0; 162 | } 163 | 164 | ^[\t ]* { 165 | 166 | CLEAR_WHITESPACE(&PSP_PG(whitespace)); 167 | psp_string_appendl(&PSP_PG(whitespace), yytext, yyleng); 168 | psp_string_appendl(&PSP_PG(pycode), yytext, yyleng); 169 | 170 | BEGIN PYCODE; 171 | } 172 | 173 | "%>" { 174 | yyless(0); 175 | BEGIN PYCODE; 176 | } 177 | 178 | \r\n|\n { 179 | CLEAR_WHITESPACE(&PSP_PG(whitespace)); 180 | yyless(0); 181 | BEGIN PYCODE; 182 | } 183 | 184 | . { 185 | CLEAR_WHITESPACE(&PSP_PG(whitespace)); 186 | yyless(0); 187 | BEGIN PYCODE; 188 | } 189 | 190 | "include"[ ]+"file"[ ]?=[ ]?"\""[^ ]+"\"" { 191 | 192 | char *filename; 193 | char *path; 194 | FILE *f; 195 | 196 | /* find a quote */ 197 | filename = strchr(yytext, '"') + 1; 198 | filename[strchr(filename, '"')-filename] = '\0'; 199 | 200 | /* XXX The absolute path check won't work on Windows, 201 | * needs to be corrected 202 | */ 203 | 204 | if (PSP_PG(dir) && filename[0] != '/') { 205 | path = malloc(strlen(filename)+strlen(PSP_PG(dir))+1); 206 | if (path == NULL) { 207 | PyErr_NoMemory(); 208 | yyterminate(); 209 | } 210 | strcpy(path, PSP_PG(dir)); 211 | strcat(path, filename); 212 | } 213 | else { 214 | path = filename; 215 | } 216 | 217 | Py_BEGIN_ALLOW_THREADS 218 | f = fopen(path, "rb"); 219 | Py_END_ALLOW_THREADS 220 | if (f == NULL) { 221 | PyErr_SetFromErrnoWithFilename(PyExc_IOError, path); 222 | } 223 | else { 224 | yypush_buffer_state(yy_create_buffer(f, YY_BUF_SIZE, yyscanner), 225 | yyscanner); 226 | BEGIN(TEXT); 227 | } 228 | 229 | if (PSP_PG(dir)) free(path); 230 | } 231 | 232 | "%>" { 233 | BEGIN TEXT; 234 | } 235 | 236 | "--%>" { 237 | BEGIN TEXT; 238 | } 239 | 240 | %% 241 | 242 | /* this is for emacs 243 | Local Variables: 244 | mode:C 245 | End: 246 | */ 247 | -------------------------------------------------------------------------------- /src/psp_string.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 3 | * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you 6 | * may not use this file except in compliance with the License. You 7 | * may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 | * implied. See the License for the specific language governing 15 | * permissions and limitations under the License. 16 | * 17 | * Originally developed by Gregory Trubetskoy. 18 | * 19 | * 20 | * 21 | * See accompanying documentation and source code comments 22 | * for details. 23 | * 24 | */ 25 | 26 | #include "psp_string.h" 27 | 28 | #define psp_string_alloc(__pspstring, __length) \ 29 | if ((size_t)(__length) > (__pspstring)->allocated) { \ 30 | (__pspstring)->blob = realloc((__pspstring)->blob, (__length) + PSP_STRING_BLOCK); \ 31 | (__pspstring)->allocated = (__length) + PSP_STRING_BLOCK; \ 32 | } 33 | 34 | void 35 | psp_string_0(psp_string *s) 36 | { 37 | if (!s->length) { 38 | return; 39 | } 40 | 41 | s->blob[s->length] = '\0'; 42 | } 43 | 44 | void 45 | psp_string_appendl(psp_string *s, char *text, size_t length) 46 | { 47 | int newlen = s->length + length; 48 | 49 | if (text == NULL) { 50 | return; 51 | } 52 | 53 | psp_string_alloc(s, newlen); 54 | memcpy(s->blob + s->length, text, length); 55 | s->length = newlen; 56 | } 57 | 58 | void 59 | psp_string_append(psp_string *s, char *text) 60 | { 61 | if (text == NULL) { 62 | return; 63 | } 64 | psp_string_appendl(s, text, strlen(text)); 65 | } 66 | 67 | void 68 | psp_string_appendc(psp_string *s, char c) 69 | { 70 | int newlen = s->length + 1; 71 | 72 | psp_string_alloc(s, newlen); 73 | s->blob[s->length] = c; 74 | s->length = newlen; 75 | } 76 | 77 | void 78 | psp_string_clear(psp_string *s) 79 | { 80 | memset(s->blob, 0, s->length); 81 | s->length = 0; 82 | } 83 | 84 | void 85 | psp_string_free(psp_string *s) 86 | { 87 | free(s->blob); 88 | s->blob = NULL; 89 | s->length = 0; 90 | s->allocated = 0; 91 | } 92 | -------------------------------------------------------------------------------- /test/Makefile.in: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy 2 | # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # 17 | PYTHON_BIN=@PYTHON_BIN@ 18 | 19 | test: 20 | $(PYTHON_BIN) test.py 21 | 22 | clean: 23 | rm -f *.pyc *.pyo 24 | cd conf && rm -f test.conf 25 | cd htdocs && rm -f *pyc *pyo 26 | rm -rf logs 27 | rm -rf tmp 28 | 29 | distclean: clean 30 | rm -f Makefile testconf.py 31 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | Running the test suite 2 | ====================== 3 | 4 | 1) Setting up Apache 5 | -------------------- 6 | 7 | - You must have a valid Apache 2 installation, responding to start and stop 8 | commands. The test suite will launch an Apache instance with its own 9 | configuration file, on its own TCP/IP port, so hopefully it won't break 10 | anything on your Apache setup. 11 | 12 | - If you're running the Apache Monitor under Win32 (the tray icon that shows the 13 | state of the Apache Server), you'll notice that it will show the status of the 14 | test server during the tests. Unfortunately once the tests are over the monitor 15 | does not revert back to showing the status of your Apache server. The server 16 | itself is still running, though. 17 | 18 | 2) Setting up mod_python source code 19 | ------------------------------------ 20 | 21 | - The source code is required because the unit test aren't provided in the 22 | binary distributions. 23 | 24 | - Get it from http://www.modpython.org/ or using SVN from : 25 | http://svn.apache.org/repos/asf/httpd/mod_python/trunk 26 | 27 | - We'll suppose you've extracted or checked it out in a directory 28 | named MOD_PYTHON. 29 | 30 | - Either you run the MOD_PYTHON/configure script... 31 | 32 | - ..or if you're under Win32, then running the configure script 33 | may be too much of a hassle. We advise you to copy 34 | MOD_PYTHON/test/testconf.py.in to MOD_PYTHON/test/testconf.py 35 | and edit this file as described in the comments. 36 | 37 | 3) Building and/or installing mod_python binaries 38 | ------------------------------------------------- 39 | 40 | - If you're really adventurous, you may want to build mod_python from source. 41 | Then it's either "./configure; make; make install" on Unix, or running 42 | MOD_PYTHON/dist/build_installer.bat on Win32. 43 | 44 | - The best way to ensure that the test suite will run is to install the target 45 | mod_python binaries on your system. The complete list of binaries (including 46 | Win32 ones) can be found following the "Other Binaries" link on : 47 | http://httpd.apache.org/modules/python-download.cgi 48 | 49 | - Running your own version of mod_python without installing it is possible and 50 | useful if you cannot be root, but you're a bit on your own, sorry. You'll have 51 | to make sure MOD_PYTHON/testconf.py points to the right version of 52 | mod_python.so and tweak Apache user's PYTHONPATH environment variable so that 53 | mod_python can find its Python modules. 54 | 55 | 4) Running the test suite 56 | ------------------------- 57 | 58 | In the current directory, just launch the test.py script : 59 | 60 | python test.py 61 | 62 | You should see a bunch of text scrolling by as the tests unfold. If everything 63 | goes well, you should see two lines around the end of the unput, one with : 64 | 65 | 8<---8<---8<---8<---8<--- 66 | Ran 43 tests in 17.955s 67 | 68 | OK 69 | 8<---8<---8<---8<---8<--- 70 | 71 | and the other with : 72 | 73 | 8<---8<---8<---8<---8<--- 74 | Ran 6 tests in 70.942s 75 | 76 | OK 77 | 8<---8<---8<---8<---8<--- 78 | 79 | Of course the number of tests will vary in the future, but what's important here 80 | are the two shiny "OK". This means the tests were successful. 81 | 82 | 5) Report the results 83 | --------------------- 84 | 85 | Now you can report your success to python-dev@httpd.apache.org ! Of course if 86 | it's not successful and you're sure it's not due to a problem in your setup, 87 | we're also interested. Send us the full output of the test suite, or at least 88 | the stack trace of the failed tests. 89 | 90 | Thanks for your time spent of running those tests ! -------------------------------------------------------------------------------- /test/htdocs/cgitest.py: -------------------------------------------------------------------------------- 1 | 2 | from __future__ import print_function 3 | 4 | print("Content-type: text/plain\n") 5 | print("test ok") 6 | 7 | -------------------------------------------------------------------------------- /test/htdocs/dummymodule.py: -------------------------------------------------------------------------------- 1 | from mod_python import apache 2 | 3 | apache.log_error("dummymodule / %s" % apache.interpreter) 4 | 5 | def function(): 6 | apache.log_error("dummymodule::function / %s" % apache.interpreter) 7 | apache.main_server.get_options()["dummymodule::function"] = "1" 8 | -------------------------------------------------------------------------------- /test/htdocs/index.py: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you 4 | # may not use this file except in compliance with the License. You 5 | # may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. See the License for the specific language governing 13 | # permissions and limitations under the License. 14 | # 15 | # 16 | # 17 | 18 | # mod_python tests 19 | 20 | def index(req): 21 | return "test 1 ok, interpreter=%s" % req.interpreter 22 | 23 | def foobar(req): 24 | return "test 2 ok, interpreter=%s" % req.interpreter 25 | -------------------------------------------------------------------------------- /test/htdocs/psp_parser.psp: -------------------------------------------------------------------------------- 1 | # format of this file 2 | # Comment lines are ignored (python or psp). 3 | # Lines beginning with test will be included, which 4 | # includes lines generated by python code. 5 | # Blank lines are ignored. 6 | # 7 | # The test format is "test:expected_result:test_string$" 8 | # where the string '$' is a token to indicate the end of the test line. 9 | # The '$' character was chosen as it's unlikely to be in the output of 10 | # the parser. 11 | # In processing the expected_result, the '-' character will be replace 12 | # by the '\' character. 13 | # The following substitutions will also be made in the expect_result 14 | # LF linefeed character 15 | # CR carriage-return character 16 | # TB tab character 17 | 18 | 19 | # BEGIN$ 20 | test:-n:\n$ 21 | test:-r:\r$ 22 | test:-t:\t$ 23 | test:-r-n:\r\n$ 24 | <% 25 | req.write("test:-n:\\n$") 26 | %> 27 | 28 | <% 29 | test_str='single_quotes' 30 | %> 31 | test:'single_quotes':'<%= test_str %>'$ 32 | <% 33 | test_str='double_quotes' 34 | %> 35 | test:"double_quotes":"<%= test_str %>"$ 36 | -------------------------------------------------------------------------------- /test/htdocs/psptest.psp: -------------------------------------------------------------------------------- 1 | <% 2 | if 1: 3 | req.write("t") 4 | %>est<%=" "+"ok"%> 5 | -------------------------------------------------------------------------------- /test/htdocs/psptest_fail.psp: -------------------------------------------------------------------------------- 1 | fail 2 | <% 3 | session['dummy'] = 1 4 | %> 5 | -------------------------------------------------------------------------------- /test/htdocs/psptest_main.psp: -------------------------------------------------------------------------------- 1 | okay 2 | <% 3 | psp.set_error_page('psptest_fail.psp') 4 | session['dummy'] = 1 5 | raise 'fail' 6 | %> 7 | -------------------------------------------------------------------------------- /test/htdocs/ssi.shtml: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /test/htdocs/subdir/dummy.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grisha/mod_python/188b55d24ae4590586dbf4f8750899f2ae3b83fa/test/htdocs/subdir/dummy.txt -------------------------------------------------------------------------------- /test/htdocs/wsgitest.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | 4 | def application(env, start_response): 5 | status = '200 OK' 6 | output = 'test fail\n' 7 | 8 | try: 9 | assert(env['wsgi.input'].__class__.__name__ == 'mp_request') 10 | assert(env['wsgi.errors'] == sys.stderr) 11 | assert(env['wsgi.version'] == (1,0)) 12 | assert(env['wsgi.multithread'] in (True, False)) 13 | assert(env['wsgi.multiprocess'] in (True, False)) 14 | assert(env['wsgi.url_scheme'] == 'http') 15 | assert(env['SCRIPT_NAME'] == '') 16 | assert(env['PATH_INFO'] == '/tests.py') 17 | output = 'test ok\n' 18 | except: 19 | pass 20 | 21 | env['wsgi.errors'].write('written_from_wsgi_test\n') 22 | env['wsgi.errors'].flush() 23 | 24 | response_headers = [('Content-type', 'text/plain'), 25 | ('Content-Length', str(len(output)))] 26 | start_response(status, response_headers) 27 | 28 | return [output] 29 | 30 | def base_uri(env, start_response): 31 | status = '200 OK' 32 | output = 'test fail\n' 33 | 34 | try: 35 | assert(env['wsgi.input'].__class__.__name__ == 'mp_request') 36 | assert(env['wsgi.errors'] == sys.stderr) 37 | assert(env['wsgi.version'] == (1,0)) 38 | assert(env['wsgi.multithread'] in (True, False)) 39 | assert(env['wsgi.multiprocess'] in (True, False)) 40 | assert(env['wsgi.url_scheme'] == 'http') 41 | assert(env['SCRIPT_NAME'] == '/foo') 42 | assert(env['PATH_INFO'] == '/bar') 43 | output = 'test ok\n' 44 | except: 45 | pass 46 | 47 | response_headers = [('Content-type', 'text/plain'), 48 | ('Content-Length', str(len(output)))] 49 | start_response(status, response_headers) 50 | 51 | return [output] 52 | 53 | 54 | --------------------------------------------------------------------------------