mirror of
https://github.com/servo/servo.git
synced 2025-08-12 00:45:33 +01:00
Update web-platform-tests to revision 0d318188757a9c996e20b82db201fd04de5aa255
This commit is contained in:
parent
b2a5225831
commit
1a81b18b9f
12321 changed files with 544385 additions and 6 deletions
153
tests/wpt/web-platform-tests/tools/wptserve/docs/Makefile
Normal file
153
tests/wpt/web-platform-tests/tools/wptserve/docs/Makefile
Normal file
|
@ -0,0 +1,153 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/wptserve.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/wptserve.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/wptserve"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/wptserve"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
243
tests/wpt/web-platform-tests/tools/wptserve/docs/conf.py
Normal file
243
tests/wpt/web-platform-tests/tools/wptserve/docs/conf.py
Normal file
|
@ -0,0 +1,243 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# wptserve documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed Aug 14 17:23:24 2013.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.abspath(".."))
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'wptserve'
|
||||
copyright = u'2013, Mozilla Foundation and other wptserve contributers'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'wptservedoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'wptserve.tex', u'wptserve Documentation',
|
||||
u'James Graham', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'wptserve', u'wptserve Documentation',
|
||||
[u'James Graham'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ------------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'wptserve', u'wptserve Documentation',
|
||||
u'James Graham', 'wptserve', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
111
tests/wpt/web-platform-tests/tools/wptserve/docs/handlers.rst
Normal file
111
tests/wpt/web-platform-tests/tools/wptserve/docs/handlers.rst
Normal file
|
@ -0,0 +1,111 @@
|
|||
Handlers
|
||||
========
|
||||
|
||||
Handlers are functions that have the general signature::
|
||||
|
||||
handler(request, response)
|
||||
|
||||
It is expected that the handler will use information from
|
||||
the request (e.g. the path) either to populate the response
|
||||
object with the data to send, or to directly write to the
|
||||
output stream via the ResponseWriter instance associated with
|
||||
the request. If a handler writes to the output stream then the
|
||||
server will not attempt additional writes, i.e. the choice to write
|
||||
directly in the handler or not is all-or-nothing.
|
||||
|
||||
A number of general-purpose handler functions are provided by default:
|
||||
|
||||
.. _handlers.Python:
|
||||
|
||||
Python Handlers
|
||||
---------------
|
||||
|
||||
Python handlers are functions which provide a higher-level API over
|
||||
manually updating the response object, by causing the return value of
|
||||
the function to provide (part of) the response. There are three
|
||||
possible sets of values that may be returned::
|
||||
|
||||
|
||||
(status, headers, content)
|
||||
(headers, content)
|
||||
content
|
||||
|
||||
Here `status` is either a tuple (status code, message) or simply a
|
||||
integer status code, `headers` is a list of (field name, value) pairs,
|
||||
and `content` is a string or an iterable returning strings. Such a
|
||||
function may also update the response manually. For example one may
|
||||
use `response.headers.set` to set a response header, and only return
|
||||
the content. One may even use this kind of handler, but manipulate
|
||||
the output socket directly, in which case the return value of the
|
||||
function, and the properties of the response object, will be ignored.
|
||||
|
||||
The most common way to make a user function into a python handler is
|
||||
to use the provided `wptserve.handlers.handler` decorator::
|
||||
|
||||
from wptserve.handlers import handler
|
||||
|
||||
@handler
|
||||
def test(request, response):
|
||||
return [("X-Test": "PASS"), ("Content-Type", "text/plain")], "test"
|
||||
|
||||
#Later, assuming we have a Router object called 'router'
|
||||
|
||||
router.register("GET", "/test", test)
|
||||
|
||||
JSON Handlers
|
||||
-------------
|
||||
|
||||
This is a specialisation of the python handler type specifically
|
||||
designed to facilitate providing JSON responses. The API is largely
|
||||
the same as for a normal python handler, but the `content` part of the
|
||||
return value is JSON encoded, and a default Content-Type header of
|
||||
`application/json` is added. Again this handler is usually used as a
|
||||
decorator::
|
||||
|
||||
from wptserve.handlers import json_handler
|
||||
|
||||
@json_handler
|
||||
def test(request, response):
|
||||
return {"test": "PASS"}
|
||||
|
||||
Python File Handlers
|
||||
--------------------
|
||||
|
||||
Python file handlers are designed to provide a vaguely PHP-like interface
|
||||
where each resource corresponds to a particular python file on the
|
||||
filesystem. Typically this is hooked up to a route like ``("*",
|
||||
"*.py", python_file_handler)``, meaning that any .py file will be
|
||||
treated as a handler file (note that this makes python files unsafe in
|
||||
much the same way that .php files are when using PHP).
|
||||
|
||||
Unlike PHP, the python files don't work by outputting text to stdout
|
||||
from the global scope. Instead they must define a single function
|
||||
`main` with the signature::
|
||||
|
||||
main(request, response)
|
||||
|
||||
This function then behaves just like those described in
|
||||
:ref:`handlers.Python` above.
|
||||
|
||||
asis Handlers
|
||||
-------------
|
||||
|
||||
These are used to serve files as literal byte streams including the
|
||||
HTTP status line, headers and body. In the default configuration this
|
||||
handler is invoked for all files with a .asis extension.
|
||||
|
||||
File Handlers
|
||||
-------------
|
||||
|
||||
File handlers are used to serve static files. By default the content
|
||||
type of these files is set by examining the file extension. However
|
||||
this can be overridden, or additional headers supplied, by providing a
|
||||
file with the same name as the file being served but an additional
|
||||
.headers suffix, i.e. test.html has its headers set from
|
||||
test.html.headers. The format of the .headers file is plaintext, with
|
||||
each line containing::
|
||||
|
||||
Header-Name: header_value
|
||||
|
||||
In addition headers can be set for a whole directory of files (but not
|
||||
subdirectories), using a file called `__dir__.headers`.
|
35
tests/wpt/web-platform-tests/tools/wptserve/docs/index.rst
Normal file
35
tests/wpt/web-platform-tests/tools/wptserve/docs/index.rst
Normal file
|
@ -0,0 +1,35 @@
|
|||
.. wptserve documentation master file, created by
|
||||
sphinx-quickstart on Wed Aug 14 17:23:24 2013.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Web Platform Test Server
|
||||
========================
|
||||
|
||||
A python-based HTTP server specifically targeted at being used for
|
||||
testing the web platform. This means that extreme flexibility —
|
||||
including the possibility of HTTP non-conformance — in the response is
|
||||
supported.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
introduction
|
||||
server
|
||||
router
|
||||
request
|
||||
response
|
||||
stash
|
||||
handlers
|
||||
pipes
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
Introduction
|
||||
============
|
||||
|
||||
wptserve has been designed with the specific goal of making a server
|
||||
that is suitable for writing tests for the web platform. This means
|
||||
that it cannot use common abstractions over HTTP such as WSGI, since
|
||||
these assume that the goal is to generate a well-formed HTTP
|
||||
response. Testcases, however, often require precise control of the
|
||||
exact bytes sent over the wire and their timing. The full list of
|
||||
design goals for the server are:
|
||||
|
||||
* Suitable to run on individual test machines and over the public internet.
|
||||
|
||||
* Support plain TCP and SSL servers.
|
||||
|
||||
* Serve static files with the minimum of configuration.
|
||||
|
||||
* Allow headers to be overwritten on a per-file and per-directory
|
||||
basis.
|
||||
|
||||
* Full customisation of headers sent (e.g. altering or omitting
|
||||
"mandatory" headers).
|
||||
|
||||
* Simple per-client state.
|
||||
|
||||
* Complex logic in tests, up to precise control over the individual
|
||||
bytes sent and the timing of sending them.
|
||||
|
||||
Request Handling
|
||||
----------------
|
||||
|
||||
At the high level, the design of the server is based around similar
|
||||
concepts to those found in common web frameworks like Django, Pyramid
|
||||
or Flask. In particular the lifecycle of a typical request will be
|
||||
familiar to users of these systems. Incoming requests are parsed and a
|
||||
:doc:`Request <request>` object is constructed. This object is passed
|
||||
to a :ref:`Router <router.Interface>` instance, which is
|
||||
responsible for mapping the request method and path to a handler
|
||||
function. This handler is passed two arguments; the request object and
|
||||
a :doc:`Response <response>` object. In cases where only simple
|
||||
responses are required, the handler function may fill in the
|
||||
properties of the response object and the server will take care of
|
||||
constructing the response. However each Response also contains a
|
||||
:ref:`ResponseWriter <response.Interface>` which can be
|
||||
used to directly control the TCP socket.
|
||||
|
||||
By default there are several built-in handler functions that provide a
|
||||
higher level API than direct manipulation of the Response
|
||||
object. These are documented in :doc:`handlers`.
|
||||
|
||||
|
190
tests/wpt/web-platform-tests/tools/wptserve/docs/make.bat
Normal file
190
tests/wpt/web-platform-tests/tools/wptserve/docs/make.bat
Normal file
|
@ -0,0 +1,190 @@
|
|||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\wptserve.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\wptserve.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
157
tests/wpt/web-platform-tests/tools/wptserve/docs/pipes.rst
Normal file
157
tests/wpt/web-platform-tests/tools/wptserve/docs/pipes.rst
Normal file
|
@ -0,0 +1,157 @@
|
|||
Pipes
|
||||
======
|
||||
|
||||
Pipes are functions that may be used when serving files to alter parts
|
||||
of the response. These are invoked by adding a pipe= query parameter
|
||||
taking a | separated list of pipe functions and parameters. The pipe
|
||||
functions are applied to the response from left to right. For example::
|
||||
|
||||
GET /sample.txt?pipe=slice(1,200)|status(404).
|
||||
|
||||
This would serve bytes 1 to 199, inclusive, of foo.txt with the HTTP status
|
||||
code 404.
|
||||
|
||||
There are several built-in pipe functions, and it is possible to add
|
||||
more using the `@pipe` decorator on a function, if required.
|
||||
|
||||
.. note::
|
||||
Because of the way pipes compose, using some pipe functions prevents the
|
||||
content-length of the response from being known in advance. In these cases
|
||||
the server will close the connection to indicate the end of the response,
|
||||
preventing the use of HTTP 1.1 keepalive.
|
||||
|
||||
Built-In Pipes
|
||||
--------------
|
||||
|
||||
sub
|
||||
~~~
|
||||
|
||||
Used to substitute variables from the server environment, or from the
|
||||
request into the response.
|
||||
|
||||
Substitutions are marked in a file using a block delimited by `{{`
|
||||
and `}}`. Inside the block the following variables are available:
|
||||
|
||||
`{{host}}`
|
||||
The host name of the server excluding any subdomain part.
|
||||
|
||||
`{{domains[]}}`
|
||||
The domain name of a particular subdomain
|
||||
e.g. `{{domains[www]}}` for the `www` subdomain.
|
||||
|
||||
`{{ports[][]}}`
|
||||
The port number of servers, by protocol
|
||||
e.g. `{{ports[http][0]}}` for the first (and, depending on setup,
|
||||
possibly only) http server
|
||||
|
||||
`{{headers[]}}`
|
||||
The HTTP headers in the request
|
||||
e.g. `{{headers[X-Test]}}` for a hypothetical `X-Test` header.
|
||||
|
||||
`{{GET[]}}`
|
||||
The query parameters for the request
|
||||
e.g. `{{GET[id]}}` for an id parameter sent with the request.
|
||||
|
||||
So, for example, to write a javascript file called `xhr.js` that
|
||||
depends on the host name of the server, without hardcoding, one might
|
||||
write::
|
||||
|
||||
var server_url = http://{{host}}:{{ports[http][0]}}/path/to/resource;
|
||||
//Create the actual XHR and so on
|
||||
|
||||
The file would then be included as:
|
||||
|
||||
<script src="xhr.js?pipe=sub"></script>
|
||||
|
||||
This pipe can also be enabled by using a filename `*.sub.ext`, e.g. the file above could be called `xhr.sub.js`.
|
||||
|
||||
status
|
||||
~~~~~~
|
||||
|
||||
Used to set the HTTP status of the response, for example::
|
||||
|
||||
example.js?pipe=status(410)
|
||||
|
||||
headers
|
||||
~~~~~~~
|
||||
|
||||
Used to add or replace http headers in the response. Takes two or
|
||||
three arguments; the header name, the header value and whether to
|
||||
append the header rather than replace an existing header (default:
|
||||
False). So, for example, a request for::
|
||||
|
||||
example.html?pipe=header(Content-Type,text/plain)
|
||||
|
||||
causes example.html to be returned with a text/plain content type
|
||||
whereas::
|
||||
|
||||
example.html?pipe=header(Content-Type,text/plain,True)
|
||||
|
||||
Will cause example.html to be returned with both text/html and
|
||||
text/plain content-type headers.
|
||||
|
||||
slice
|
||||
~~~~~
|
||||
|
||||
Used to send only part of a response body. Takes the start and,
|
||||
optionally, end bytes as arguments, although either can be null to
|
||||
indicate the start or end of the file, respectively. So for example::
|
||||
|
||||
example.txt?pipe=slice(10,20)
|
||||
|
||||
Would result in a response with a body containing 10 bytes of
|
||||
example.txt including byte 10 but excluding byte 20.
|
||||
|
||||
::
|
||||
|
||||
example.txt?pipe=slice(10)
|
||||
|
||||
Would cause all bytes from byte 10 of example.txt to be sent, but::
|
||||
|
||||
example.txt?pipe=slice(null,20)
|
||||
|
||||
Would send the first 20 bytes of example.txt.
|
||||
|
||||
trickle
|
||||
~~~~~~~
|
||||
|
||||
.. note::
|
||||
Using this function will force a connection close.
|
||||
|
||||
Used to send the body of a response in chunks with delays. Takes a
|
||||
single argument that is a microsyntax consisting of colon-separated
|
||||
commands. There are three types of commands:
|
||||
|
||||
* Bare numbers represent a number of bytes to send
|
||||
|
||||
* Numbers prefixed `d` indicate a delay in seconds
|
||||
|
||||
* Numbers prefixed `r` must only appear at the end of the command, and
|
||||
indicate that the preceding N items must be repeated until there is
|
||||
no more content to send. The number of items to repeat must be even.
|
||||
|
||||
In the absence of a repetition command, the entire remainder of the content is
|
||||
sent at once when the command list is exhausted. So for example::
|
||||
|
||||
example.txt?pipe=trickle(d1)
|
||||
|
||||
causes a 1s delay before sending the entirety of example.txt.
|
||||
|
||||
::
|
||||
|
||||
example.txt?pipe=trickle(100:d1)
|
||||
|
||||
causes 100 bytes of example.txt to be sent, followed by a 1s delay,
|
||||
and then the remainder of the file to be sent. On the other hand::
|
||||
|
||||
example.txt?pipe=trickle(100:d1:r2)
|
||||
|
||||
Will cause the file to be sent in 100 byte chunks separated by a 1s
|
||||
delay until the whole content has been sent.
|
||||
|
||||
|
||||
:mod:`Interface <pipes>`
|
||||
------------------------
|
||||
|
||||
.. automodule:: wptserve.pipes
|
||||
:members:
|
10
tests/wpt/web-platform-tests/tools/wptserve/docs/request.rst
Normal file
10
tests/wpt/web-platform-tests/tools/wptserve/docs/request.rst
Normal file
|
@ -0,0 +1,10 @@
|
|||
Request
|
||||
=======
|
||||
|
||||
Request object.
|
||||
|
||||
:mod:`Interface <request>`
|
||||
--------------------------
|
||||
|
||||
.. automodule:: wptserve.request
|
||||
:members:
|
|
@ -0,0 +1,41 @@
|
|||
Response
|
||||
========
|
||||
|
||||
Response object. This object is used to control the response that will
|
||||
be sent to the HTTP client. A handler function will take the response
|
||||
object and fill in various parts of the response. For example, a plain
|
||||
text response with the body 'Some example content' could be produced as::
|
||||
|
||||
def handler(request, response):
|
||||
response.headers.set("Content-Type", "text/plain")
|
||||
response.content = "Some example content"
|
||||
|
||||
The response object also gives access to a ResponseWriter, which
|
||||
allows direct access to the response socket. For example, one could
|
||||
write a similar response but with more explicit control as follows::
|
||||
|
||||
import time
|
||||
|
||||
def handler(request, response):
|
||||
response.add_required_headers = False # Don't implicitly add HTTP headers
|
||||
response.writer.write_status(200)
|
||||
response.writer.write_header("Content-Type", "text/plain")
|
||||
response.writer.write_header("Content-Length", len("Some example content"))
|
||||
response.writer.end_headers()
|
||||
response.writer.write("Some ")
|
||||
time.sleep(1)
|
||||
response.writer.write("example content")
|
||||
|
||||
Note that when writing the response directly like this it is always
|
||||
necessary to either set the Content-Length header or set
|
||||
`response.close_connection = True`. Without one of these, the client
|
||||
will not be able to determine where the response body ends and will
|
||||
continue to load indefinitely.
|
||||
|
||||
.. _response.Interface:
|
||||
|
||||
:mod:`Interface <response>`
|
||||
---------------------------
|
||||
|
||||
.. automodule:: wptserve.response
|
||||
:members:
|
78
tests/wpt/web-platform-tests/tools/wptserve/docs/router.rst
Normal file
78
tests/wpt/web-platform-tests/tools/wptserve/docs/router.rst
Normal file
|
@ -0,0 +1,78 @@
|
|||
Router
|
||||
======
|
||||
|
||||
The router is used to match incoming requests to request handler
|
||||
functions. Typically users don't interact with the router directly,
|
||||
but instead send a list of routes to register when starting the
|
||||
server. However it is also possible to add routes after starting the
|
||||
server by calling the `register` method on the server's `router`
|
||||
property.
|
||||
|
||||
Routes are represented by a three item tuple::
|
||||
|
||||
(methods, path_match, handler)
|
||||
|
||||
`methods` is either a string or a list of strings indicating the HTTP
|
||||
methods to match. In cases where all methods should match there is a
|
||||
special sentinel value `any_method` provided as a property of the
|
||||
`router` module that can be used.
|
||||
|
||||
`path_match` is an expression that will be evaluated against the
|
||||
request path to decide if the handler should match. These expressions
|
||||
follow a custom syntax intended to make matching URLs straightforward
|
||||
and, in particular, to be easier to use than raw regexp for URL
|
||||
matching. There are three possible components of a match expression:
|
||||
|
||||
* Literals. These match any character. The special characters \*, \{
|
||||
and \} must be escaped by prefixing them with a \\.
|
||||
|
||||
* Match groups. These match any character other than / and save the
|
||||
result as a named group. They are delimited by curly braces; for
|
||||
example::
|
||||
|
||||
{abc}
|
||||
|
||||
would create a match group with the name `abc`.
|
||||
|
||||
* Stars. These are denoted with a `*` and match any character
|
||||
including /. There can be at most one star
|
||||
per pattern and it must follow any match groups.
|
||||
|
||||
Path expressions always match the entire request path and a leading /
|
||||
in the expression is implied even if it is not explicitly
|
||||
provided. This means that `/foo` and `foo` are equivalent.
|
||||
|
||||
For example, the following pattern matches all requests for resources with the
|
||||
extension `.py`::
|
||||
|
||||
*.py
|
||||
|
||||
The following expression matches anything directly under `/resources`
|
||||
with a `.html` extension, and places the "filename" in the `name`
|
||||
group::
|
||||
|
||||
/resources/{name}.html
|
||||
|
||||
The groups, including anything that matches a `*` are available in the
|
||||
request object through the `route_match` property. This is a
|
||||
dictionary mapping the group names, and any match for `*` to the
|
||||
matching part of the route. For example, given a route::
|
||||
|
||||
/api/{sub_api}/*
|
||||
|
||||
and the request path `/api/test/html/test.html`, `route_match` would
|
||||
be::
|
||||
|
||||
{"sub_api": "html", "*": "html/test.html"}
|
||||
|
||||
`handler` is a function taking a request and a response object that is
|
||||
responsible for constructing the response to the HTTP request. See
|
||||
:doc:`handlers` for more details on handler functions.
|
||||
|
||||
.. _router.Interface:
|
||||
|
||||
:mod:`Interface <wptserve>`
|
||||
---------------------------
|
||||
|
||||
.. automodule:: wptserve.router
|
||||
:members:
|
20
tests/wpt/web-platform-tests/tools/wptserve/docs/server.rst
Normal file
20
tests/wpt/web-platform-tests/tools/wptserve/docs/server.rst
Normal file
|
@ -0,0 +1,20 @@
|
|||
Server
|
||||
======
|
||||
|
||||
Basic server classes and router.
|
||||
|
||||
The following example creates a server that serves static files from
|
||||
the `files` subdirectory of the current directory and causes it to
|
||||
run on port 8080 until it is killed::
|
||||
|
||||
from wptserve import server, handlers
|
||||
|
||||
httpd = server.WebTestHttpd(port=8080, doc_root="./files/",
|
||||
routes=[("GET", "*", handlers.file_handler)])
|
||||
httpd.start(block=True)
|
||||
|
||||
:mod:`Interface <wptserve>`
|
||||
---------------------------
|
||||
|
||||
.. automodule:: wptserve.server
|
||||
:members:
|
31
tests/wpt/web-platform-tests/tools/wptserve/docs/stash.rst
Normal file
31
tests/wpt/web-platform-tests/tools/wptserve/docs/stash.rst
Normal file
|
@ -0,0 +1,31 @@
|
|||
Stash
|
||||
=====
|
||||
|
||||
Object for storing cross-request state. This is unusual in that keys
|
||||
must be UUIDs, in order to prevent different clients setting the same
|
||||
key, and values are write-once, read-once to minimise the chances of
|
||||
state persisting indefinitely. The stash defines two operations;
|
||||
`put`, to add state and `take` to remove state. Furthermore, the view
|
||||
of the stash is path-specific; by default a request will only see the
|
||||
part of the stash corresponding to its own path.
|
||||
|
||||
A typical example of using a stash to store state might be::
|
||||
|
||||
@handler
|
||||
def handler(request, response):
|
||||
# We assume this is a string representing a UUID
|
||||
key = request.GET.first("id")
|
||||
|
||||
if request.method == "POST":
|
||||
request.server.stash.put(key, "Some sample value")
|
||||
return "Added value to stash"
|
||||
else:
|
||||
value = request.server.stash.take(key)
|
||||
assert request.server.stash.take(key) is None
|
||||
return key
|
||||
|
||||
:mod:`Interface <stash>`
|
||||
------------------------
|
||||
|
||||
.. automodule:: wptserve.stash
|
||||
:members:
|
Loading…
Add table
Add a link
Reference in a new issue