From 75076b4971b26cff889dc33e84a579dc3e91a32e Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 10 Apr 2012 10:56:26 -0400 Subject: update docs --- Lib/pydoc_topics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/pydoc_topics.py b/Lib/pydoc_topics.py index 1392d28..6355847 100644 --- a/Lib/pydoc_topics.py +++ b/Lib/pydoc_topics.py @@ -1,4 +1,4 @@ -# Autogenerated by Sphinx on Sat Mar 17 14:18:25 2012 +# Autogenerated by Sphinx on Tue Apr 10 10:39:11 2012 topics = {'assert': u'\nThe ``assert`` statement\n************************\n\nAssert statements are a convenient way to insert debugging assertions\ninto a program:\n\n assert_stmt ::= "assert" expression ["," expression]\n\nThe simple form, ``assert expression``, is equivalent to\n\n if __debug__:\n if not expression: raise AssertionError\n\nThe extended form, ``assert expression1, expression2``, is equivalent\nto\n\n if __debug__:\n if not expression1: raise AssertionError(expression2)\n\nThese equivalences assume that ``__debug__`` and ``AssertionError``\nrefer to the built-in variables with those names. In the current\nimplementation, the built-in variable ``__debug__`` is ``True`` under\nnormal circumstances, ``False`` when optimization is requested\n(command line option -O). The current code generator emits no code\nfor an assert statement when optimization is requested at compile\ntime. Note that it is unnecessary to include the source code for the\nexpression that failed in the error message; it will be displayed as\npart of the stack trace.\n\nAssignments to ``__debug__`` are illegal. The value for the built-in\nvariable is determined when the interpreter starts.\n', 'assignment': u'\nAssignment statements\n*********************\n\nAssignment statements are used to (re)bind names to values and to\nmodify attributes or items of mutable objects:\n\n assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression)\n target_list ::= target ("," target)* [","]\n target ::= identifier\n | "(" target_list ")"\n | "[" target_list "]"\n | attributeref\n | subscription\n | slicing\n\n(See section *Primaries* for the syntax definitions for the last three\nsymbols.)\n\nAn assignment statement evaluates the expression list (remember that\nthis can be a single expression or a comma-separated list, the latter\nyielding a tuple) and assigns the single resulting object to each of\nthe target lists, from left to right.\n\nAssignment is defined recursively depending on the form of the target\n(list). When a target is part of a mutable object (an attribute\nreference, subscription or slicing), the mutable object must\nultimately perform the assignment and decide about its validity, and\nmay raise an exception if the assignment is unacceptable. The rules\nobserved by various types and the exceptions raised are given with the\ndefinition of the object types (see section *The standard type\nhierarchy*).\n\nAssignment of an object to a target list is recursively defined as\nfollows.\n\n* If the target list is a single target: The object is assigned to\n that target.\n\n* If the target list is a comma-separated list of targets: The object\n must be an iterable with the same number of items as there are\n targets in the target list, and the items are assigned, from left to\n right, to the corresponding targets. (This rule is relaxed as of\n Python 1.5; in earlier versions, the object had to be a tuple.\n Since strings are sequences, an assignment like ``a, b = "xy"`` is\n now legal as long as the string has the right length.)\n\nAssignment of an object to a single target is recursively defined as\nfollows.\n\n* If the target is an identifier (name):\n\n * If the name does not occur in a ``global`` statement in the\n current code block: the name is bound to the object in the current\n local namespace.\n\n * Otherwise: the name is bound to the object in the current global\n namespace.\n\n The name is rebound if it was already bound. This may cause the\n reference count for the object previously bound to the name to reach\n zero, causing the object to be deallocated and its destructor (if it\n has one) to be called.\n\n* If the target is a target list enclosed in parentheses or in square\n brackets: The object must be an iterable with the same number of\n items as there are targets in the target list, and its items are\n assigned, from left to right, to the corresponding targets.\n\n* If the target is an attribute reference: The primary expression in\n the reference is evaluated. It should yield an object with\n assignable attributes; if this is not the case, ``TypeError`` is\n raised. That object is then asked to assign the assigned object to\n the given attribute; if it cannot perform the assignment, it raises\n an exception (usually but not necessarily ``AttributeError``).\n\n Note: If the object is a class instance and the attribute reference\n occurs on both sides of the assignment operator, the RHS expression,\n ``a.x`` can access either an instance attribute or (if no instance\n attribute exists) a class attribute. The LHS target ``a.x`` is\n always set as an instance attribute, creating it if necessary.\n Thus, the two occurrences of ``a.x`` do not necessarily refer to the\n same attribute: if the RHS expression refers to a class attribute,\n the LHS creates a new instance attribute as the target of the\n assignment:\n\n class Cls:\n x = 3 # class variable\n inst = Cls()\n inst.x = inst.x + 1 # writes inst.x as 4 leaving Cls.x as 3\n\n This description does not necessarily apply to descriptor\n attributes, such as properties created with ``property()``.\n\n* If the target is a subscription: The primary expression in the\n reference is evaluated. It should yield either a mutable sequence\n object (such as a list) or a mapping object (such as a dictionary).\n Next, the subscript expression is evaluated.\n\n If the primary is a mutable sequence object (such as a list), the\n subscript must yield a plain integer. If it is negative, the\n sequence\'s length is added to it. The resulting value must be a\n nonnegative integer less than the sequence\'s length, and the\n sequence is asked to assign the assigned object to its item with\n that index. If the index is out of range, ``IndexError`` is raised\n (assignment to a subscripted sequence cannot add new items to a\n list).\n\n If the primary is a mapping object (such as a dictionary), the\n subscript must have a type compatible with the mapping\'s key type,\n and the mapping is then asked to create a key/datum pair which maps\n the subscript to the assigned object. This can either replace an\n existing key/value pair with the same key value, or insert a new\n key/value pair (if no key with the same value existed).\n\n* If the target is a slicing: The primary expression in the reference\n is evaluated. It should yield a mutable sequence object (such as a\n list). The assigned object should be a sequence object of the same\n type. Next, the lower and upper bound expressions are evaluated,\n insofar they are present; defaults are zero and the sequence\'s\n length. The bounds should evaluate to (small) integers. If either\n bound is negative, the sequence\'s length is added to it. The\n resulting bounds are clipped to lie between zero and the sequence\'s\n length, inclusive. Finally, the sequence object is asked to replace\n the slice with the items of the assigned sequence. The length of\n the slice may be different from the length of the assigned sequence,\n thus changing the length of the target sequence, if the object\n allows it.\n\n**CPython implementation detail:** In the current implementation, the\nsyntax for targets is taken to be the same as for expressions, and\ninvalid syntax is rejected during the code generation phase, causing\nless detailed error messages.\n\nWARNING: Although the definition of assignment implies that overlaps\nbetween the left-hand side and the right-hand side are \'safe\' (for\nexample ``a, b = b, a`` swaps two variables), overlaps *within* the\ncollection of assigned-to variables are not safe! For instance, the\nfollowing program prints ``[0, 2]``:\n\n x = [0, 1]\n i = 0\n i, x[i] = 1, 2\n print x\n\n\nAugmented assignment statements\n===============================\n\nAugmented assignment is the combination, in a single statement, of a\nbinary operation and an assignment statement:\n\n augmented_assignment_stmt ::= augtarget augop (expression_list | yield_expression)\n augtarget ::= identifier | attributeref | subscription | slicing\n augop ::= "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**="\n | ">>=" | "<<=" | "&=" | "^=" | "|="\n\n(See section *Primaries* for the syntax definitions for the last three\nsymbols.)\n\nAn augmented assignment evaluates the target (which, unlike normal\nassignment statements, cannot be an unpacking) and the expression\nlist, performs the binary operation specific to the type of assignment\non the two operands, and assigns the result to the original target.\nThe target is only evaluated once.\n\nAn augmented assignment expression like ``x += 1`` can be rewritten as\n``x = x + 1`` to achieve a similar, but not exactly equal effect. In\nthe augmented version, ``x`` is only evaluated once. Also, when\npossible, the actual operation is performed *in-place*, meaning that\nrather than creating a new object and assigning that to the target,\nthe old object is modified instead.\n\nWith the exception of assigning to tuples and multiple targets in a\nsingle statement, the assignment done by augmented assignment\nstatements is handled the same way as normal assignments. Similarly,\nwith the exception of the possible *in-place* behavior, the binary\noperation performed by augmented assignment is the same as the normal\nbinary operations.\n\nFor targets which are attribute references, the same *caveat about\nclass and instance attributes* applies as for regular assignments.\n', 'atom-identifiers': u'\nIdentifiers (Names)\n*******************\n\nAn identifier occurring as an atom is a name. See section\n*Identifiers and keywords* for lexical definition and section *Naming\nand binding* for documentation of naming and binding.\n\nWhen the name is bound to an object, evaluation of the atom yields\nthat object. When a name is not bound, an attempt to evaluate it\nraises a ``NameError`` exception.\n\n**Private name mangling:** When an identifier that textually occurs in\na class definition begins with two or more underscore characters and\ndoes not end in two or more underscores, it is considered a *private\nname* of that class. Private names are transformed to a longer form\nbefore code is generated for them. The transformation inserts the\nclass name in front of the name, with leading underscores removed, and\na single underscore inserted in front of the class name. For example,\nthe identifier ``__spam`` occurring in a class named ``Ham`` will be\ntransformed to ``_Ham__spam``. This transformation is independent of\nthe syntactical context in which the identifier is used. If the\ntransformed name is extremely long (longer than 255 characters),\nimplementation defined truncation may happen. If the class name\nconsists only of underscores, no transformation is done.\n', -- cgit v0.12 From a12d0ccfbe189ba7f3ed99cb1feac69aa03d7fe0 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 10 Apr 2012 10:59:35 -0400 Subject: Bump to 2.6.8 --- Include/patchlevel.h | 6 +++--- Lib/distutils/__init__.py | 2 +- Lib/idlelib/idlever.py | 2 +- Misc/NEWS | 8 ++++++++ Misc/RPM/python-2.6.spec | 2 +- README | 4 ++-- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 955e650..f565752 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,11 +23,11 @@ #define PY_MAJOR_VERSION 2 #define PY_MINOR_VERSION 6 #define PY_MICRO_VERSION 8 -#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_GAMMA -#define PY_RELEASE_SERIAL 2 +#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL +#define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "2.6.8rc2" +#define PY_VERSION "2.6.8" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository) */ diff --git a/Lib/distutils/__init__.py b/Lib/distutils/__init__.py index 141b377..e5008c9 100644 --- a/Lib/distutils/__init__.py +++ b/Lib/distutils/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.8rc2" +__version__ = "2.6.8" #--end constants-- diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py index 4acb814..92525ae 100644 --- a/Lib/idlelib/idlever.py +++ b/Lib/idlelib/idlever.py @@ -1 +1 @@ -IDLE_VERSION = "2.6.8rc2" +IDLE_VERSION = "2.6.8" diff --git a/Misc/NEWS b/Misc/NEWS index a8e813d..cdd3cf5 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -2,6 +2,14 @@ Python News +++++++++++ +What's New in Python 2.6.8? +=========================== + +*Release date: 2012-04-10* + +No changes since 2.6.8rc2. + + What's New in Python 2.6.8 rc 2? ================================ diff --git a/Misc/RPM/python-2.6.spec b/Misc/RPM/python-2.6.spec index 41471bb..e80b952 100644 --- a/Misc/RPM/python-2.6.spec +++ b/Misc/RPM/python-2.6.spec @@ -39,7 +39,7 @@ %define name python #--start constants-- -%define version 2.6.8rc2 +%define version 2.6.8 %define libvers 2.6 #--end constants-- %define release 1pydotorg diff --git a/README b/README index 5656285..9cda724 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -This is Python version 2.6.8 rc 2 -================================= +This is Python version 2.6.8 +============================ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 -- cgit v0.12 From 27509ce8d99d897579fb7bd333d471eceed9a87a Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 10 Apr 2012 11:18:47 -0400 Subject: Added tag v2.6.8 for changeset c9910fd022fc --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index bf95bf6..5bcf0f0 100644 --- a/.hgtags +++ b/.hgtags @@ -139,3 +139,4 @@ e189dc8fd66154ef46d9cd22584d56669b544ca3 v2.6.6rc2 9f8771e0905277f8b3c2799113a062fda4164995 v2.6.6 caab08cd2b3eb5a6f78479b2513b65d36c754f41 v2.6.8rc1 1d1b7b9fad48bd0dc60dc8a06cca4459ef273127 v2.6.8rc2 +c9910fd022fc842e5578e1bf5a30ba55a37239fc v2.6.8 -- cgit v0.12 From cca96f09d3d561fc16a5634d4c91dfc846213ecb Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 10 Apr 2012 14:50:39 -0400 Subject: Post release twiddle. --- Include/patchlevel.h | 2 +- Misc/NEWS | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index f565752..36673ae 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -27,7 +27,7 @@ #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "2.6.8" +#define PY_VERSION "2.6.8+" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository) */ diff --git a/Misc/NEWS b/Misc/NEWS index cdd3cf5..3e84155 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -2,6 +2,18 @@ Python News +++++++++++ +What's New in Python 2.6.9 rc 1? +================================ + +*Release date: XXXX-XX-XX* + +Core and Builtins +----------------- + +Library +------- + + What's New in Python 2.6.8? =========================== -- cgit v0.12 From 2a1c13bb2d933f7166fd5739799a8f1543303e69 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Tue, 10 Apr 2012 19:52:06 +0100 Subject: Updated logging reference and HOWTO. --- Doc/howto/logging.rst | 15 +++++++++++++++ Doc/library/logging.rst | 16 ++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst index 029a0ab..79e4dc9 100644 --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -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 diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 665485d..b4185c3 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -51,9 +51,21 @@ listed below. Logger Objects -------------- -Loggers have the following attributes and methods. Note that Loggers are never +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 -- cgit v0.12 From fb2e874707b0817124a2427a9367276b13fbc4ad Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Wed, 11 Apr 2012 03:07:57 +0800 Subject: fix the incorrect changes made for PATH_INFO value - Issue10484 --- Lib/CGIHTTPServer.py | 16 ++++++---------- Lib/test/test_httpservers.py | 2 +- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Lib/CGIHTTPServer.py b/Lib/CGIHTTPServer.py index 2ca8217..61f34f8 100644 --- a/Lib/CGIHTTPServer.py +++ b/Lib/CGIHTTPServer.py @@ -85,8 +85,11 @@ class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): (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 + joined_path = '/'.join(splitpath) + dir_sep = joined_path.find('/', 1) + head, tail = joined_path[:dir_sep], joined_path[dir_sep+1:] + if head in self.cgi_directories: + self.cgi_info = head, tail return True return False @@ -323,14 +326,7 @@ def _url_collapse_path_split(path): # 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() + tail_part = path_parts.pop() else: tail_part = '' head_parts = [] diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index a7752d9..d1b924d 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -419,6 +419,7 @@ class CGIHTTPServerTestCase(BaseTestCase): BaseTestCase.tearDown(self) def test_url_collapse_path_split(self): + # verify tail is the last portion and head is the rest on proper urls test_vectors = { '': ('/', ''), '..': IndexError, @@ -429,7 +430,6 @@ class CGIHTTPServerTestCase(BaseTestCase): '/.//': ('/', ''), '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'), -- cgit v0.12 From dc0b324a0f73692e90fe999260f1d0f3c8371147 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Wed, 11 Apr 2012 03:22:58 +0800 Subject: Fix closes Issue14258 - Clarify the re.LOCALE and re.UNICODE flags for \S class --- Doc/library/re.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Doc/library/re.rst b/Doc/library/re.rst index d64604f..02251cf 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -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 -- cgit v0.12 From 5f7e7345cf4f583c127160aa43ee5c3fea7ba06f Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Thu, 12 Apr 2012 02:23:23 +0800 Subject: Issue 10484 - Incorporate improvements to CGI module - Suggested by Glenn Linderman. Refactor code and tests --- Lib/CGIHTTPServer.py | 51 +++++++++++++++--------------- Lib/test/test_httpservers.py | 74 +++++++++++++++++++++++--------------------- 2 files changed, 64 insertions(+), 61 deletions(-) diff --git a/Lib/CGIHTTPServer.py b/Lib/CGIHTTPServer.py index 61f34f8..47a994c 100644 --- a/Lib/CGIHTTPServer.py +++ b/Lib/CGIHTTPServer.py @@ -84,10 +84,9 @@ 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) - joined_path = '/'.join(splitpath) - dir_sep = joined_path.find('/', 1) - head, tail = joined_path[:dir_sep], joined_path[dir_sep+1:] + 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 @@ -301,44 +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:] + path_parts = path.split('/') + head_parts = [] + for part in path_parts[:-1]: + if part == '..': + head_parts.pop() # IndexError if more '..' than prior parts + elif part and part != '.': + head_parts.append( part ) if path_parts: tail_part = path_parts.pop() + if tail_part: + if tail_part == '..': + head_parts.pop() + tail_part = '' + elif tail_part == '.': + tail_part = '' else: tail_part = '' - head_parts = [] - for part in path_parts: - if part == '..': - head_parts.pop() - else: - head_parts.append(part) - if tail_part and tail_part == '..': - head_parts.pop() - tail_part = '' - return ('/' + '/'.join(head_parts), tail_part) + + splitpath = ('/' + '/'.join(head_parts), tail_part) + collapsed_path = "/".join(splitpath) + + return collapsed_path nobody = None diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index d1b924d..5dcedc0 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -4,11 +4,6 @@ Written by Cody A.W. Somerville , 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 StringIO import StringIO +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'Data\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'), - '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)) -- cgit v0.12