summaryrefslogtreecommitdiffstats
path: root/Doc/howto
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2009-01-03 20:55:06 (GMT)
committerGeorg Brandl <georg@python.org>2009-01-03 20:55:06 (GMT)
commitc62ef8b4d9648c36218cb0142a6395a00c11885e (patch)
tree74d90ea6215a37553bb1cddfc4c4eddf947958e9 /Doc/howto
parente92818f58c134549c8820073037a1655330bbea1 (diff)
downloadcpython-c62ef8b4d9648c36218cb0142a6395a00c11885e.zip
cpython-c62ef8b4d9648c36218cb0142a6395a00c11885e.tar.gz
cpython-c62ef8b4d9648c36218cb0142a6395a00c11885e.tar.bz2
Remove trailing whitespace.
Diffstat (limited to 'Doc/howto')
-rw-r--r--Doc/howto/curses.rst4
-rw-r--r--Doc/howto/doanddont.rst6
-rw-r--r--Doc/howto/functional.rst48
-rw-r--r--Doc/howto/regex.rst10
-rw-r--r--Doc/howto/sockets.rst14
-rw-r--r--Doc/howto/unicode.rst42
-rw-r--r--Doc/howto/urllib2.rst50
-rw-r--r--Doc/howto/webservers.rst8
8 files changed, 91 insertions, 91 deletions
diff --git a/Doc/howto/curses.rst b/Doc/howto/curses.rst
index 1e1e2f7..0600ea6 100644
--- a/Doc/howto/curses.rst
+++ b/Doc/howto/curses.rst
@@ -399,8 +399,8 @@ string. It can optionally be limited to a fixed number of characters. ::
curses.echo() # Enable echoing of characters
- # Get a 15-character string, with the cursor on the top line
- s = stdscr.getstr(0,0, 15)
+ # Get a 15-character string, with the cursor on the top line
+ s = stdscr.getstr(0,0, 15)
The Python :mod:`curses.textpad` module supplies something better. With it, you
can turn a window into a text box that supports an Emacs-like set of
diff --git a/Doc/howto/doanddont.rst b/Doc/howto/doanddont.rst
index a3a91ed..a56fb8c 100644
--- a/Doc/howto/doanddont.rst
+++ b/Doc/howto/doanddont.rst
@@ -1,5 +1,5 @@
************************************
- Idioms and Anti-Idioms in Python
+ Idioms and Anti-Idioms in Python
************************************
:Author: Moshe Zadka
@@ -127,7 +127,7 @@ Bad example::
# bar.py
from foo import a
if something():
- a = 2 # danger: foo.a != a
+ a = 2 # danger: foo.a != a
Good example::
@@ -303,6 +303,6 @@ It is usually much better to use the implicit continuation inside parenthesis:
This version is bulletproof::
- value = (foo.bar()['first'][0]*baz.quux(1, 2)[5:9]
+ value = (foo.bar()['first'][0]*baz.quux(1, 2)[5:9]
+ calculate_number(10, 20)*forbulate(500, 360))
diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst
index 6318e12..d589f36 100644
--- a/Doc/howto/functional.rst
+++ b/Doc/howto/functional.rst
@@ -145,7 +145,7 @@ than a large function that performs a complicated transformation. Small
functions are also easier to read and to check for errors.
-Ease of debugging and testing
+Ease of debugging and testing
-----------------------------
Testing and debugging a functional-style program is easier.
@@ -213,7 +213,7 @@ You can experiment with the iteration interface manually:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration
- >>>
+ >>>
Python expects iterable objects in several different contexts, the most
important being the ``for`` statement. In the statement ``for X in Y``, Y must
@@ -362,7 +362,7 @@ Generator expressions are surrounded by parentheses ("()") and list
comprehensions are surrounded by square brackets ("[]"). Generator expressions
have the form::
- ( expression for expr in sequence1
+ ( expression for expr in sequence1
if condition1
for expr2 in sequence2
if condition2
@@ -404,7 +404,7 @@ equivalent to the following Python code::
if not (conditionN):
continue # Skip this element
- # Output the value of
+ # Output the value of
# the expression.
This means that when there are multiple ``for...in`` clauses but no ``if``
@@ -418,8 +418,8 @@ list is 9 elements long:
>>> seq1 = 'abc'
>>> seq2 = (1,2,3)
>>> [(x,y) for x in seq1 for y in seq2]
- [('a', 1), ('a', 2), ('a', 3),
- ('b', 1), ('b', 2), ('b', 3),
+ [('a', 1), ('a', 2), ('a', 3),
+ ('b', 1), ('b', 2), ('b', 3),
('c', 1), ('c', 2), ('c', 3)]
To avoid introducing an ambiguity into Python's grammar, if ``expression`` is
@@ -759,7 +759,7 @@ values:
True
>>> all([0,1,0])
False
- >>> all([0,0,0])
+ >>> all([0,0,0])
False
>>> all([1,1,1])
True
@@ -845,7 +845,7 @@ Fredrik Lundh once suggested the following set of rules for refactoring uses of
4) Convert the lambda to a def statement, using that name.
5) Remove the comment.
-I really like these rules, but you're free to disagree
+I really like these rules, but you're free to disagree
about whether this lambda-free style is better.
@@ -970,7 +970,7 @@ operators. Some examples are ``operator.add(a, b)`` (adds two values),
``itertools.starmap(func, iter)`` assumes that the iterable will return a stream
of tuples, and calls ``f()`` using these tuples as the arguments::
- itertools.starmap(os.path.join,
+ itertools.starmap(os.path.join,
[('/usr', 'bin', 'java'), ('/bin', 'python'),
('/usr', 'bin', 'perl'),('/usr', 'bin', 'ruby')])
=>
@@ -1039,9 +1039,9 @@ value and an iterator for the elements with that key.
::
- city_list = [('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL'),
+ city_list = [('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL'),
('Anchorage', 'AK'), ('Nome', 'AK'),
- ('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ'),
+ ('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ'),
...
]
@@ -1056,7 +1056,7 @@ value and an iterator for the elements with that key.
where
iterator-1 =>
('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL')
- iterator-2 =>
+ iterator-2 =>
('Anchorage', 'AK'), ('Nome', 'AK')
iterator-3 =>
('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ')
@@ -1150,7 +1150,7 @@ is equivalent to ::
>>> double(add(5, 6))
22
-
+
The ``unpack`` keyword is provided to work around the fact that Python functions
are not always `fully curried <http://en.wikipedia.org/wiki/Currying>`__. By
default, it is expected that the ``inner`` function will return a single object
@@ -1159,15 +1159,15 @@ and that the ``outer`` function will take a single argument. Setting the
will be expanded before being passed to ``outer``. Put simply, ::
compose(f, g)(5, 6)
-
+
is equivalent to::
f(g(5, 6))
-
+
while ::
compose(f, g, unpack=True)(5, 6)
-
+
is equivalent to::
f(*g(5, 6))
@@ -1178,20 +1178,20 @@ version that will compose any number of functions. We'll use ``reduce()``,
``functional`` and ``functools``). ::
from functional import compose, partial
-
+
multi_compose = partial(reduce, compose)
-
-
+
+
We can also use ``map()``, ``compose()`` and ``partial()`` to craft a version of
``"".join(...)`` that converts its arguments to string::
from functional import compose, partial
-
+
join = compose("".join, partial(map, str))
``flip(func)``
-
+
``flip()`` wraps the callable in ``func`` and causes it to receive its
non-keyword arguments in reverse order. ::
@@ -1206,7 +1206,7 @@ non-keyword arguments in reverse order. ::
(7, 6, 5)
``foldl(func, start, iterable)``
-
+
``foldl()`` takes a binary function, a starting value (usually some kind of
'zero'), and an iterable. The function is applied to the starting value and the
first element of the list, then the result of that and the second element of the
@@ -1220,7 +1220,7 @@ is equivalent to::
f(f(f(0, 1), 2), 3)
-
+
``foldl()`` is roughly equivalent to the following recursive function::
def foldl(func, start, seq):
@@ -1298,7 +1298,7 @@ for text processing, in the section titled "Utilizing Higher-Order Functions in
Text Processing".
Mertz also wrote a 3-part series of articles on functional programming
-for IBM's DeveloperWorks site; see
+for IBM's DeveloperWorks site; see
`part 1 <http://www-128.ibm.com/developerworks/library/l-prog.html>`__,
`part 2 <http://www-128.ibm.com/developerworks/library/l-prog2.html>`__, and
`part 3 <http://www-128.ibm.com/developerworks/linux/library/l-prog3.html>`__,
diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst
index 2f085e5..4275ffb 100644
--- a/Doc/howto/regex.rst
+++ b/Doc/howto/regex.rst
@@ -1,7 +1,7 @@
.. _regex-howto:
****************************
- Regular Expression HOWTO
+ Regular Expression HOWTO
****************************
:Author: A.M. Kuchling
@@ -611,7 +611,7 @@ of each one.
is to read? ::
charref = re.compile(r"""
- &[#] # Start of a numeric entity reference
+ &[#] # Start of a numeric entity reference
(
0[0-7]+ # Octal form
| [0-9]+ # Decimal form
@@ -732,7 +732,7 @@ given location, they can obviously be matched an infinite number of times.
>>> p = re.compile('\bclass\b')
>>> print p.search('no class at all')
None
- >>> print p.search('\b' + 'class' + '\b')
+ >>> print p.search('\b' + 'class' + '\b')
<re.MatchObject instance at 80c3ee0>
Second, inside a character class, where there's no use for this assertion,
@@ -1236,9 +1236,9 @@ It's important to keep this distinction in mind. Remember, :func:`match` will
only report a successful match which will start at 0; if the match wouldn't
start at zero, :func:`match` will *not* report it. ::
- >>> print re.match('super', 'superstition').span()
+ >>> print re.match('super', 'superstition').span()
(0, 5)
- >>> print re.match('super', 'insuperable')
+ >>> print re.match('super', 'insuperable')
None
On the other hand, :func:`search` will scan forward through the string,
diff --git a/Doc/howto/sockets.rst b/Doc/howto/sockets.rst
index 2747f81..3734d69 100644
--- a/Doc/howto/sockets.rst
+++ b/Doc/howto/sockets.rst
@@ -1,5 +1,5 @@
****************************
- Socket Programming HOWTO
+ Socket Programming HOWTO
****************************
:Author: Gordon McMillan
@@ -63,7 +63,7 @@ your browser did something like the following::
#create an INET, STREAMing socket
s = socket.socket(
socket.AF_INET, socket.SOCK_STREAM)
- #now connect to the web server on port 80
+ #now connect to the web server on port 80
# - the normal http port
s.connect(("www.mcmillan-inc.com", 80))
@@ -78,7 +78,7 @@ creates a "server socket". ::
#create an INET, STREAMing socket
serversocket = socket.socket(
socket.AF_INET, socket.SOCK_STREAM)
- #bind the socket to a public host,
+ #bind the socket to a public host,
# and a well-known port
serversocket.bind((socket.gethostname(), 80))
#become a server socket
@@ -185,7 +185,7 @@ Assuming you don't want to end the connection, the simplest solution is a fixed
length message::
class mysocket:
- '''demonstration class only
+ '''demonstration class only
- coded for clarity, not efficiency
'''
@@ -343,9 +343,9 @@ you'll have little trouble with it in C. ::
ready_to_read, ready_to_write, in_error = \
select.select(
- potential_readers,
- potential_writers,
- potential_errs,
+ potential_readers,
+ potential_writers,
+ potential_errs,
timeout)
You pass ``select`` three lists: the first contains all sockets that you might
diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst
index d5dec63..7f246cc 100644
--- a/Doc/howto/unicode.rst
+++ b/Doc/howto/unicode.rst
@@ -122,8 +122,8 @@ The first encoding you might think of is an array of 32-bit integers. In this
representation, the string "Python" would look like this::
P y t h o n
- 0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00
- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
+ 0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
This representation is straightforward but using it presents a number of
problems.
@@ -181,7 +181,7 @@ UTF-8.) UTF-8 uses the following rules:
between 128 and 255.
3. Code points >0x7ff are turned into three- or four-byte sequences, where each
byte of the sequence is between 128 and 255.
-
+
UTF-8 has several convenient properties:
1. It can handle any Unicode code point.
@@ -252,7 +252,7 @@ characters greater than 127 will be treated as errors::
>>> unicode('abcdef' + chr(255))
Traceback (most recent call last):
File "<stdin>", line 1, in ?
- UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 6:
+ UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 6:
ordinal not in range(128)
The ``errors`` argument specifies the response when the input string can't be
@@ -264,7 +264,7 @@ Unicode result). The following examples show the differences::
>>> unicode('\x80abc', errors='strict')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
- UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0:
+ UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0:
ordinal not in range(128)
>>> unicode('\x80abc', errors='replace')
u'\ufffdabc'
@@ -350,7 +350,7 @@ interprets the string using the given encoding::
>>> u2 = utf8_version.decode('utf-8') # Decode using UTF-8
>>> u == u2 # The two strings match
True
-
+
The low-level routines for registering and accessing the available encodings are
found in the :mod:`codecs` module. However, the encoding and decoding functions
returned by this module are usually more low-level than is comfortable, so I'm
@@ -362,8 +362,8 @@ covered here. Consult the Python documentation to learn more about this module.
The most commonly used part of the :mod:`codecs` module is the
:func:`codecs.open` function which will be discussed in the section on input and
output.
-
-
+
+
Unicode Literals in Python Source Code
--------------------------------------
@@ -381,10 +381,10 @@ arbitrary code point. Octal escapes can go up to U+01ff, which is octal 777.
>>> s = u"a\xac\u1234\u20ac\U00008000"
^^^^ two-digit hex escape
- ^^^^^^ four-digit Unicode escape
+ ^^^^^^ four-digit Unicode escape
^^^^^^^^^^ eight-digit Unicode escape
>>> for c in s: print ord(c),
- ...
+ ...
97 172 4660 8364 32768
Using escape sequences for code points greater than 127 is fine in small doses,
@@ -404,10 +404,10 @@ either the first or second line of the source file::
#!/usr/bin/env python
# -*- coding: latin-1 -*-
-
+
u = u'abcdé'
print ord(u[-1])
-
+
The syntax is inspired by Emacs's notation for specifying variables local to a
file. Emacs supports many different variables, but Python only supports
'coding'. The ``-*-`` symbols indicate to Emacs that the comment is special;
@@ -427,10 +427,10 @@ encoding declaration::
When you run it with Python 2.4, it will output the following warning::
amk:~$ python p263.py
- sys:1: DeprecationWarning: Non-ASCII character '\xe9'
- in file p263.py on line 2, but no encoding declared;
+ sys:1: DeprecationWarning: Non-ASCII character '\xe9'
+ in file p263.py on line 2, but no encoding declared;
see http://www.python.org/peps/pep-0263.html for details
-
+
Unicode Properties
------------------
@@ -446,13 +446,13 @@ The following program displays some information about several characters, and
prints the numeric value of one particular character::
import unicodedata
-
+
u = unichr(233) + unichr(0x0bf2) + unichr(3972) + unichr(6000) + unichr(13231)
-
+
for i, c in enumerate(u):
print i, '%04x' % ord(c), unicodedata.category(c),
print unicodedata.name(c)
-
+
# Get numeric value of second character
print unicodedata.numeric(u[1])
@@ -615,7 +615,7 @@ The first list contains UTF-8-encoded filenames, and the second list contains
the Unicode versions.
-
+
Tips for Writing Unicode-aware Programs
---------------------------------------
@@ -661,7 +661,7 @@ this code::
unicode_name = filename.decode(encoding)
f = open(unicode_name, 'r')
# ... return contents of file ...
-
+
However, if an attacker could specify the ``'base64'`` encoding, they could pass
``'L2V0Yy9wYXNzd2Q='``, which is the base-64 encoded form of the string
``'/etc/passwd'``, to read a system file. The above code looks for ``'/'``
@@ -697,7 +697,7 @@ Version 1.02: posted August 16 2005. Corrects factual errors.
.. comment Describe obscure -U switch somewhere?
.. comment Describe use of codecs.StreamRecoder and StreamReaderWriter
-.. comment
+.. comment
Original outline:
- [ ] Unicode introduction
diff --git a/Doc/howto/urllib2.rst b/Doc/howto/urllib2.rst
index 6e1a2f3..96f2ce2 100644
--- a/Doc/howto/urllib2.rst
+++ b/Doc/howto/urllib2.rst
@@ -10,7 +10,7 @@
HOWTO, available at `urllib2 - Le Manuel manquant
<http://www.voidspace.org.uk/python/articles/urllib2_francais.shtml>`_.
-
+
Introduction
============
@@ -19,9 +19,9 @@ Introduction
You may also find useful the following article on fetching web resources
with Python :
-
+
* `Basic Authentication <http://www.voidspace.org.uk/python/articles/authentication.shtml>`_
-
+
A tutorial on *Basic Authentication*, with examples in Python.
**urllib2** is a `Python <http://www.python.org>`_ module for fetching URLs
@@ -98,7 +98,7 @@ argument. The encoding is done using a function from the ``urllib`` library
*not* from ``urllib2``. ::
import urllib
- import urllib2
+ import urllib2
url = 'http://www.someserver.com/cgi-bin/register.cgi'
values = {'name' : 'Michael Foord',
@@ -161,15 +161,15 @@ request as above, but identifies itself as a version of Internet
Explorer [#]_. ::
import urllib
- import urllib2
-
+ import urllib2
+
url = 'http://www.someserver.com/cgi-bin/register.cgi'
- user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
+ user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
values = {'name' : 'Michael Foord',
'location' : 'Northampton',
'language' : 'Python' }
headers = { 'User-Agent' : user_agent }
-
+
data = urllib.urlencode(values)
req = urllib2.Request(url, data, headers)
response = urllib2.urlopen(req)
@@ -183,7 +183,7 @@ Handling Exceptions
===================
*urlopen* raises :exc:`URLError` when it cannot handle a response (though as usual
-with Python APIs, builtin exceptions such as
+with Python APIs, builtin exceptions such as
:exc:`ValueError`, :exc:`TypeError` etc. may also
be raised).
@@ -309,18 +309,18 @@ page returned. This means that as well as the code attribute, it also has read,
geturl, and info, methods. ::
>>> req = urllib2.Request('http://www.python.org/fish.html')
- >>> try:
+ >>> try:
>>> urllib2.urlopen(req)
>>> except URLError, e:
>>> print e.code
>>> print e.read()
- >>>
+ >>>
404
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
- <?xml-stylesheet href="./css/ht2html.css"
+ <?xml-stylesheet href="./css/ht2html.css"
type="text/css"?>
- <html><head><title>Error 404: File Not Found</title>
+ <html><head><title>Error 404: File Not Found</title>
...... etc...
Wrapping it Up
@@ -372,7 +372,7 @@ Number 2
print 'Error code: ', e.code
else:
# everything is fine
-
+
info and geturl
===============
@@ -443,7 +443,7 @@ error code) requesting authentication. This specifies the authentication scheme
and a 'realm'. The header looks like : ``Www-authenticate: SCHEME
realm="REALM"``.
-e.g. ::
+e.g. ::
Www-authenticate: Basic realm="cPanel Users"
@@ -467,24 +467,24 @@ The top-level URL is the first URL that requires authentication. URLs "deeper"
than the URL you pass to .add_password() will also match. ::
# create a password manager
- password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
+ password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
# Add the username and password.
# If we knew the realm, we could use it instead of None.
top_level_url = "http://example.com/foo/"
password_mgr.add_password(None, top_level_url, username, password)
- handler = urllib2.HTTPBasicAuthHandler(password_mgr)
+ handler = urllib2.HTTPBasicAuthHandler(password_mgr)
# create "opener" (OpenerDirector instance)
- opener = urllib2.build_opener(handler)
+ opener = urllib2.build_opener(handler)
# use the opener to fetch a URL
- opener.open(a_url)
+ opener.open(a_url)
# Install the opener.
# Now all calls to urllib2.urlopen use our opener.
- urllib2.install_opener(opener)
+ urllib2.install_opener(opener)
.. note::
@@ -540,7 +540,7 @@ you can set the default timeout globally for all sockets using ::
# timeout in seconds
timeout = 10
- socket.setdefaulttimeout(timeout)
+ socket.setdefaulttimeout(timeout)
# this call to urllib2.urlopen now uses the default timeout
# we have set in the socket module
@@ -557,7 +557,7 @@ Footnotes
This document was reviewed and revised by John Lee.
.. [#] For an introduction to the CGI protocol see
- `Writing Web Applications in Python <http://www.pyzine.com/Issue008/Section_Articles/article_CGIOne.html>`_.
+ `Writing Web Applications in Python <http://www.pyzine.com/Issue008/Section_Articles/article_CGIOne.html>`_.
.. [#] Like Google for example. The *proper* way to use google from a program
is to use `PyGoogle <http://pygoogle.sourceforge.net>`_ of course. See
`Voidspace Google <http://www.voidspace.org.uk/python/recipebook.shtml#google>`_
@@ -574,6 +574,6 @@ This document was reviewed and revised by John Lee.
is set to use the proxy, which urllib2 picks up on. In order to test
scripts with a localhost server, I have to prevent urllib2 from using
the proxy.
-.. [#] urllib2 opener for SSL proxy (CONNECT method): `ASPN Cookbook Recipe
+.. [#] urllib2 opener for SSL proxy (CONNECT method): `ASPN Cookbook Recipe
<http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/456195>`_.
-
+
diff --git a/Doc/howto/webservers.rst b/Doc/howto/webservers.rst
index 97c2267..6e0c815 100644
--- a/Doc/howto/webservers.rst
+++ b/Doc/howto/webservers.rst
@@ -88,7 +88,7 @@ they can be run as CGI if no better option is available.
<http://wiki.python.org/moin/CgiScripts>`_ with some additional information
about CGI in Python.
-
+
Simple script for testing CGI
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -386,7 +386,7 @@ compared with other web techniques.
You might be interested in some WSGI-supporting modules already contained in
the standard library, namely:
-
+
* :mod:`wsgiref` -- some tiny utilities and servers for WSGI
@@ -499,7 +499,7 @@ using these is a good idea.
time in looking through the most popular ones. Some frameworks have their
own template engine or have a recommentation for one. It's wise to use
these.
-
+
Popular template engines include:
* Mako
@@ -687,7 +687,7 @@ And that's still not everything. The most up-to-date information can always be
found in the Python wiki.
.. seealso::
-
+
The Python wiki contains an extensive list of `web frameworks
<http://wiki.python.org/moin/WebFrameworks>`_.