merge heads

This commit is contained in:
Benjamin Peterson 2012-04-11 16:12:21 -04:00
commit ff67046791
6 changed files with 110 additions and 83 deletions

View File

@ -139,6 +139,7 @@ e189dc8fd66154ef46d9cd22584d56669b544ca3 v2.6.6rc2
9f8771e0905277f8b3c2799113a062fda4164995 v2.6.6
caab08cd2b3eb5a6f78479b2513b65d36c754f41 v2.6.8rc1
1d1b7b9fad48bd0dc60dc8a06cca4459ef273127 v2.6.8rc2
c9910fd022fc842e5578e1bf5a30ba55a37239fc v2.6.8
b4107eb00b4271fb73a9e1b736d4f23460950778 v2.7a1
adc85ebc7271cc22e24e816782bb2b8d7fa3a6b3 v2.7a2
4180557b7a9bb9dd5341a18af199f843f199e46e v2.7a3

View File

@ -642,6 +642,21 @@ You can see that the config file approach has a few advantages over the Python
code approach, mainly separation of configuration and code and the ability of
noncoders to easily modify the logging properties.
.. warning:: The :func:`fileConfig` function takes a default parameter,
``disable_existing_loggers``, which defaults to ``True`` for reasons of
backward compatibility. This may or may not be what you want, since it
will cause any loggers existing before the :func:`fileConfig` call to
be disabled unless they (or an ancestor) are explicitly named in the
configuration. Please refer to the reference documentation for more
information, and specify ``False`` for this parameter if you wish.
The dictionary passed to :func:`dictConfig` can also specify a Boolean
value with key ``disable_existing_loggers``, which if not specified
explicitly in the dictionary also defaults to being interpreted as
``True``. This leads to the logger-disabling behaviour described above,
which may not be what you want - in which case, provide the key
explicitly with a value of ``False``.
.. currentmodule:: logging
Note that the class names referenced in config files need to be either relative

View File

@ -53,7 +53,19 @@ Logger Objects
Loggers have the following attributes and methods. Note that Loggers are never
instantiated directly, but always through the module-level function
``logging.getLogger(name)``.
``logging.getLogger(name)``. Multiple calls to :func:`getLogger` with the same
name will always return a reference to the same Logger object.
The ``name`` is potentially a period-separated hierarchical value, like
``foo.bar.baz`` (though it could also be just plain ``foo``, for example).
Loggers that are further down in the hierarchical list are children of loggers
higher up in the list. For example, given a logger with a name of ``foo``,
loggers with names of ``foo.bar``, ``foo.bar.baz``, and ``foo.bam`` are all
descendants of ``foo``. The logger name hierarchy is analogous to the Python
package hierarchy, and identical to it if you organise your loggers on a
per-module basis using the recommended construction
``logging.getLogger(__name__)``. That's because in a module, ``__name__``
is the module's name in the Python package namespace.
.. class:: Logger

View File

@ -353,20 +353,20 @@ the second character. For example, ``\$`` matches the character ``'$'``.
character properties database.
``\s``
When the :const:`LOCALE` and :const:`UNICODE` flags are not specified, matches
any whitespace character; this is equivalent to the set ``[ \t\n\r\f\v]``. With
:const:`LOCALE`, it will match this set plus whatever characters are defined as
space for the current locale. If :const:`UNICODE` is set, this will match the
characters ``[ \t\n\r\f\v]`` plus whatever is classified as space in the Unicode
character properties database.
When the :const:`UNICODE` flag is not specified, it matches any whitespace
character, this is equivalent to the set ``[ \t\n\r\f\v]``. The
:const:`LOCALE` flag has no extra effect on matching of the space.
If :const:`UNICODE` is set, this will match the characters ``[ \t\n\r\f\v]``
plus whatever is classified as space in the Unicode character properties
database.
``\S``
When the :const:`LOCALE` and :const:`UNICODE` flags are not specified,
matches any non-whitespace character; this is equivalent to the set ``[^
\t\n\r\f\v]`` With :const:`LOCALE`, it will match the above set plus any
non-space character in the current locale. If :const:`UNICODE` is set, the
above set ``[^ \t\n\r\f\v]`` plus the characters not marked as space in the
Unicode character properties database.
When the :const:`UNICODE` flags is not specified, matches any non-whitespace
character; this is equivalent to the set ``[^ \t\n\r\f\v]`` The
:const:`LOCALE` flag has no extra effect on non-whitespace match. If
:const:`UNICODE` is set, then any character not marked as space in the
Unicode character properties database is matched.
``\w``
When the :const:`LOCALE` and :const:`UNICODE` flags are not specified, matches

View File

@ -84,9 +84,11 @@ class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
path begins with one of the strings in self.cgi_directories
(and the next character is a '/' or the end of the string).
"""
splitpath = _url_collapse_path_split(self.path)
if splitpath[0] in self.cgi_directories:
self.cgi_info = splitpath
collapsed_path = _url_collapse_path(self.path)
dir_sep = collapsed_path.find('/', 1)
head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
if head in self.cgi_directories:
self.cgi_info = head, tail
return True
return False
@ -298,51 +300,46 @@ class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
self.log_message("CGI script exited OK")
# TODO(gregory.p.smith): Move this into an appropriate library.
def _url_collapse_path_split(path):
def _url_collapse_path(path):
"""
Given a URL path, remove extra '/'s and '.' path elements and collapse
any '..' references.
any '..' references and returns a colllapsed path.
Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
The utility of this function is limited to is_cgi method and helps
preventing some security attacks.
Returns: A tuple of (head, tail) where tail is everything after the final /
and head is everything before it. Head will always start with a '/' and,
if it contains anything else, never have a trailing '/'.
Raises: IndexError if too many '..' occur within the path.
"""
# Similar to os.path.split(os.path.normpath(path)) but specific to URL
# path semantics rather than local operating system semantics.
path_parts = []
for part in path.split('/'):
if part == '.':
path_parts.append('')
else:
path_parts.append(part)
# Filter out blank non trailing parts before consuming the '..'.
path_parts = [part for part in path_parts[:-1] if part] + path_parts[-1:]
if path_parts:
# Special case for CGI's for PATH_INFO
if path.startswith('/cgi-bin') or path.startswith('/htbin'):
tail_part = []
while path_parts[-1] not in ('cgi-bin','htbin'):
tail_part.insert(0,path_parts.pop())
tail_part = "/".join(tail_part)
else:
tail_part = path_parts.pop()
else:
tail_part = ''
path_parts = path.split('/')
head_parts = []
for part in path_parts:
for part in path_parts[:-1]:
if part == '..':
head_parts.pop()
else:
head_parts.pop() # IndexError if more '..' than prior parts
elif part and part != '.':
head_parts.append( part )
if tail_part and tail_part == '..':
if path_parts:
tail_part = path_parts.pop()
if tail_part:
if tail_part == '..':
head_parts.pop()
tail_part = ''
return ('/' + '/'.join(head_parts), tail_part)
elif tail_part == '.':
tail_part = ''
else:
tail_part = ''
splitpath = ('/' + '/'.join(head_parts), tail_part)
collapsed_path = "/".join(splitpath)
return collapsed_path
nobody = None

View File

@ -4,11 +4,6 @@ Written by Cody A.W. Somerville <cody-somerville@ubuntu.com>,
Josip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest.
"""
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
from CGIHTTPServer import CGIHTTPRequestHandler
import CGIHTTPServer
import os
import sys
import re
@ -17,12 +12,17 @@ import shutil
import urllib
import httplib
import tempfile
import unittest
import CGIHTTPServer
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
from CGIHTTPServer import CGIHTTPRequestHandler
from StringIO import StringIO
from test import test_support
threading = test_support.import_module('threading')
@ -43,7 +43,7 @@ class SocketlessRequestHandler(SimpleHTTPRequestHandler):
self.end_headers()
self.wfile.write(b'<html><body>Data</body></html>\r\n')
def log_message(self, format, *args):
def log_message(self, fmt, *args):
pass
@ -97,9 +97,9 @@ class BaseHTTPRequestHandlerTestCase(unittest.TestCase):
self.handler = SocketlessRequestHandler()
def send_typical_request(self, message):
input = StringIO(message)
input_msg = StringIO(message)
output = StringIO()
self.handler.rfile = input
self.handler.rfile = input_msg
self.handler.wfile = output
self.handler.handle_one_request()
output.seek(0)
@ -296,7 +296,7 @@ class SimpleHTTPServerTestCase(BaseTestCase):
os.chdir(self.cwd)
try:
shutil.rmtree(self.tempdir)
except:
except OSError:
pass
finally:
BaseTestCase.tearDown(self)
@ -418,42 +418,44 @@ class CGIHTTPServerTestCase(BaseTestCase):
finally:
BaseTestCase.tearDown(self)
def test_url_collapse_path_split(self):
def test_url_collapse_path(self):
# verify tail is the last portion and head is the rest on proper urls
test_vectors = {
'': ('/', ''),
'': '//',
'..': IndexError,
'/.//..': IndexError,
'/': ('/', ''),
'//': ('/', ''),
'/\\': ('/', '\\'),
'/.//': ('/', ''),
'cgi-bin/file1.py': ('/cgi-bin', 'file1.py'),
'/cgi-bin/file1.py': ('/cgi-bin', 'file1.py'),
'/cgi-bin/file1.py/PATH-INFO': ('/cgi-bin', 'file1.py/PATH-INFO'),
'a': ('/', 'a'),
'/a': ('/', 'a'),
'//a': ('/', 'a'),
'./a': ('/', 'a'),
'./C:/': ('/C:', ''),
'/a/b': ('/a', 'b'),
'/a/b/': ('/a/b', ''),
'/a/b/c/..': ('/a/b', ''),
'/a/b/c/../d': ('/a/b', 'd'),
'/a/b/c/../d/e/../f': ('/a/b/d', 'f'),
'/a/b/c/../d/e/../../f': ('/a/b', 'f'),
'/a/b/c/../d/e/.././././..//f': ('/a/b', 'f'),
'/': '//',
'//': '//',
'/\\': '//\\',
'/.//': '//',
'cgi-bin/file1.py': '/cgi-bin/file1.py',
'/cgi-bin/file1.py': '/cgi-bin/file1.py',
'a': '//a',
'/a': '//a',
'//a': '//a',
'./a': '//a',
'./C:/': '/C:/',
'/a/b': '/a/b',
'/a/b/': '/a/b/',
'/a/b/.': '/a/b/',
'/a/b/c/..': '/a/b/',
'/a/b/c/../d': '/a/b/d',
'/a/b/c/../d/e/../f': '/a/b/d/f',
'/a/b/c/../d/e/../../f': '/a/b/f',
'/a/b/c/../d/e/.././././..//f': '/a/b/f',
'../a/b/c/../d/e/.././././..//f': IndexError,
'/a/b/c/../d/e/../../../f': ('/a', 'f'),
'/a/b/c/../d/e/../../../../f': ('/', 'f'),
'/a/b/c/../d/e/../../../f': '/a/f',
'/a/b/c/../d/e/../../../../f': '//f',
'/a/b/c/../d/e/../../../../../f': IndexError,
'/a/b/c/../d/e/../../../../f/..': ('/', ''),
'/a/b/c/../d/e/../../../../f/..': '//',
'/a/b/c/../d/e/../../../../f/../.': '//',
}
for path, expected in test_vectors.iteritems():
if isinstance(expected, type) and issubclass(expected, Exception):
self.assertRaises(expected,
CGIHTTPServer._url_collapse_path_split, path)
CGIHTTPServer._url_collapse_path, path)
else:
actual = CGIHTTPServer._url_collapse_path_split(path)
actual = CGIHTTPServer._url_collapse_path(path)
self.assertEqual(expected, actual,
msg='path = %r\nGot: %r\nWanted: %r' %
(path, actual, expected))