From solipsis at pitrou.net Wed Sep 1 05:00:24 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 01 Sep 2010 05:00:24 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84381): sum=0 Message-ID: py3k results for svn r84381 (hg cset 6f6672d442bc) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogyXGzk9', '-x'] From python-checkins at python.org Wed Sep 1 08:58:25 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 08:58:25 +0200 (CEST) Subject: [Python-checkins] r84383 - python/branches/py3k/Doc/library/bisect.rst Message-ID: <20100901065825.C5226EEBB3@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 08:58:25 2010 New Revision: 84383 Log: Clean-up bisect docs. * Document the post conditions for bisect_left() and bisect_right(). * Fix the grades example to use more obvious breakpoints and to use a list comprehension instead of map() which returns an iterator in Py3.x. * Improve and expand the examples of searching sorted lists. * Issue 4356 -- move up reference to the SortedCollection recipe. Modified: python/branches/py3k/Doc/library/bisect.rst Modified: python/branches/py3k/Doc/library/bisect.rst ============================================================================== --- python/branches/py3k/Doc/library/bisect.rst (original) +++ python/branches/py3k/Doc/library/bisect.rst Wed Sep 1 08:58:25 2010 @@ -4,6 +4,7 @@ .. module:: bisect :synopsis: Array bisection algorithms for binary searching. .. sectionauthor:: Fred L. Drake, Jr. +.. sectionauthor:: Raymond Hettinger .. example based on the PyModules FAQ entry by Aaron Watters This module provides support for maintaining a list in sorted order without @@ -18,13 +19,16 @@ .. function:: bisect_left(a, x, lo=0, hi=len(a)) - Locate the proper insertion point for *x* in *a* to maintain sorted order. + Locate the insertion point for *x* in *a* to maintain sorted order. The parameters *lo* and *hi* may be used to specify a subset of the list which should be considered; by default the entire list is used. If *x* is already present in *a*, the insertion point will be before (to the left of) any existing entries. The return value is suitable for use as the first - parameter to ``list.insert()``. This assumes that *a* is already sorted. + parameter to ``list.insert()`` assuming that *a* is already sorted. + The returned insertion point *i* partitions the array *a* into two halves so + that ``all(val < x for val in a[lo:i])`` for the left side and + ``all(val >= x for val in a[i:hi])`` for the right side. .. function:: bisect_right(a, x, lo=0, hi=len(a)) bisect(a, x, lo=0, hi=len(a)) @@ -32,16 +36,16 @@ Similar to :func:`bisect_left`, but returns an insertion point which comes after (to the right of) any existing entries of *x* in *a*. + The returned insertion point *i* partitions the array *a* into two halves so + that ``all(val <= x for val in a[lo:i])`` for the left side and + ``all(val > x for val in a[i:hi])`` for the right side. .. function:: insort_left(a, x, lo=0, hi=len(a)) Insert *x* in *a* in sorted order. This is equivalent to - ``a.insert(bisect.bisect_left(a, x, lo, hi), x)``. This assumes that *a* is - already sorted. - - Also note that while the fast search step is O(log n), the slower insertion - step is O(n), so the overall operation is slow. - + ``a.insert(bisect.bisect_left(a, x, lo, hi), x)`` assuming that *a* is + already sorted. Keep in mind that the O(log n) search is dominated by + the slow O(n) insertion step. .. function:: insort_right(a, x, lo=0, hi=len(a)) insort(a, x, lo=0, hi=len(a)) @@ -49,71 +53,75 @@ Similar to :func:`insort_left`, but inserting *x* in *a* after any existing entries of *x*. - Also note that while the fast search step is O(log n), the slower insertion - step is O(n), so the overall operation is slow. +.. seealso:: + + `SortedCollection recipe + `_ that uses + bisect to build a full-featured collection class with straight-forward search + methods and support for a key-function. The keys are precomputed to save + unnecessary calls to the key function during searches. + Searching Sorted Lists ---------------------- -The above :func:`bisect` functions are useful for finding insertion points, but -can be tricky or awkward to use for common searching tasks. The following three +The above :func:`bisect` functions are useful for finding insertion points but +can be tricky or awkward to use for common searching tasks. The following five functions show how to transform them into the standard lookups for sorted lists:: - def find(a, key): - '''Find leftmost item exact equal to the key. - Raise ValueError if no such item exists. - - ''' - i = bisect_left(a, key) - if i < len(a) and a[i] == key: + def index(a, x): + 'Locate the leftmost value exactly equal to x' + i = bisect_left(a, x) + if i != len(a) and a[i] == x: + return i + raise ValueError + + def find_lt(a, x): + 'Find rightmost value less than x' + i = bisect_left(a, x) + if i: + return a[i-1] + raise ValueError + + def find_le(a, x): + 'Find rightmost value less than or equal to x' + i = bisect_right(a, x) + if i: + return a[i-1] + raise ValueError + + def find_gt(a, x): + 'Find leftmost value greater than x' + i = bisect_right(a, x) + if i != len(a): return a[i] - raise ValueError('No item found with key equal to: %r' % (key,)) + raise ValueError - def find_le(a, key): - '''Find largest item less-than or equal to key. - Raise ValueError if no such item exists. - If multiple keys are equal, return the leftmost. - - ''' - i = bisect_left(a, key) - if i < len(a) and a[i] == key: + def find_ge(a, x): + 'Find leftmost item greater than or equal to x' + i = bisect_left(a, x) + if i != len(a): return a[i] - if i == 0: - raise ValueError('No item found with key at or below: %r' % (key,)) - return a[i-1] - - def find_ge(a, key): - '''Find smallest item greater-than or equal to key. - Raise ValueError if no such item exists. - If multiple keys are equal, return the leftmost. - - ''' - i = bisect_left(a, key) - if i == len(a): - raise ValueError('No item found with key at or above: %r' % (key,)) - return a[i] + raise ValueError + Other Examples -------------- .. _bisect-example: -The :func:`bisect` function is generally useful for categorizing numeric data. -This example uses :func:`bisect` to look up a letter grade for an exam total -(say) based on a set of ordered numeric breakpoints: 85 and up is an 'A', 75..84 -is a 'B', etc. - - >>> grades = "FEDCBA" - >>> breakpoints = [30, 44, 66, 75, 85] - >>> from bisect import bisect - >>> def grade(total): - ... return grades[bisect(breakpoints, total)] +The :func:`bisect` function can be useful for numeric table lookups. This +example uses :func:`bisect` to look up a letter grade for an exam score (say) +based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is +a 'B', and so on:: + + >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'): + ... i = bisect(breakpoints, score) + ... return grades[i] ... - >>> grade(66) - 'C' - >>> map(grade, [33, 99, 77, 44, 12, 88]) - ['E', 'A', 'B', 'D', 'F', 'A'] + >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]] + ['F', 'A', 'C', 'C', 'B', 'A', 'A'] Unlike the :func:`sorted` function, it does not make sense for the :func:`bisect` functions to have *key* or *reversed* arguments because that would lead to an @@ -135,9 +143,3 @@ >>> data[bisect_left(keys, 8)] ('yellow', 8) -.. seealso:: - - `SortedCollection recipe - `_ that - encapsulates precomputed keys, allowing straight-forward insertion and - searching using a *key* function. From python-checkins at python.org Wed Sep 1 09:46:54 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 09:46:54 +0200 (CEST) Subject: [Python-checkins] r84384 - python/branches/py3k/Doc/library/operator.rst Message-ID: <20100901074654.6ADA5EE9B8@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 09:46:54 2010 New Revision: 84384 Log: 2-to-3 fixup for map() example in docs Modified: python/branches/py3k/Doc/library/operator.rst Modified: python/branches/py3k/Doc/library/operator.rst ============================================================================== --- python/branches/py3k/Doc/library/operator.rst (original) +++ python/branches/py3k/Doc/library/operator.rst Wed Sep 1 09:46:54 2010 @@ -387,7 +387,7 @@ >>> inventory = [('apple', 3), ('banana', 2), ('pear', 5), ('orange', 1)] >>> getcount = itemgetter(1) - >>> map(getcount, inventory) + >>> list(map(getcount, inventory)) [3, 2, 5, 1] >>> sorted(inventory, key=getcount) [('orange', 1), ('banana', 2), ('apple', 3), ('pear', 5)] From python-checkins at python.org Wed Sep 1 10:24:40 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 10:24:40 +0200 (CEST) Subject: [Python-checkins] r84385 - python/branches/release31-maint/Doc/library/bisect.rst Message-ID: <20100901082440.6D77FF93D@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 10:24:40 2010 New Revision: 84385 Log: Clean-up bisect docs Modified: python/branches/release31-maint/Doc/library/bisect.rst Modified: python/branches/release31-maint/Doc/library/bisect.rst ============================================================================== --- python/branches/release31-maint/Doc/library/bisect.rst (original) +++ python/branches/release31-maint/Doc/library/bisect.rst Wed Sep 1 10:24:40 2010 @@ -4,6 +4,7 @@ .. module:: bisect :synopsis: Array bisection algorithms for binary searching. .. sectionauthor:: Fred L. Drake, Jr. +.. sectionauthor:: Raymond Hettinger .. example based on the PyModules FAQ entry by Aaron Watters This module provides support for maintaining a list in sorted order without @@ -18,13 +19,16 @@ .. function:: bisect_left(a, x, lo=0, hi=len(a)) - Locate the proper insertion point for *x* in *a* to maintain sorted order. + Locate the insertion point for *x* in *a* to maintain sorted order. The parameters *lo* and *hi* may be used to specify a subset of the list which should be considered; by default the entire list is used. If *x* is already present in *a*, the insertion point will be before (to the left of) any existing entries. The return value is suitable for use as the first - parameter to ``list.insert()``. This assumes that *a* is already sorted. + parameter to ``list.insert()`` assuming that *a* is already sorted. + The returned insertion point *i* partitions the array *a* into two halves so + that ``all(val < x for val in a[lo:i])`` for the left side and + ``all(val >= x for val in a[i:hi])`` for the right side. .. function:: bisect_right(a, x, lo=0, hi=len(a)) bisect(a, x, lo=0, hi=len(a)) @@ -32,16 +36,16 @@ Similar to :func:`bisect_left`, but returns an insertion point which comes after (to the right of) any existing entries of *x* in *a*. + The returned insertion point *i* partitions the array *a* into two halves so + that ``all(val <= x for val in a[lo:i])`` for the left side and + ``all(val > x for val in a[i:hi])`` for the right side. .. function:: insort_left(a, x, lo=0, hi=len(a)) Insert *x* in *a* in sorted order. This is equivalent to - ``a.insert(bisect.bisect_left(a, x, lo, hi), x)``. This assumes that *a* is - already sorted. - - Also note that while the fast search step is O(log n), the slower insertion - step is O(n), so the overall operation is slow. - + ``a.insert(bisect.bisect_left(a, x, lo, hi), x)`` assuming that *a* is + already sorted. Keep in mind that the O(log n) search is dominated by + the slow O(n) insertion step. .. function:: insort_right(a, x, lo=0, hi=len(a)) insort(a, x, lo=0, hi=len(a)) @@ -49,71 +53,75 @@ Similar to :func:`insort_left`, but inserting *x* in *a* after any existing entries of *x*. - Also note that while the fast search step is O(log n), the slower insertion - step is O(n), so the overall operation is slow. +.. seealso:: + + `SortedCollection recipe + `_ that uses + bisect to build a full-featured collection class with straight-forward search + methods and support for a key-function. The keys are precomputed to save + unnecessary calls to the key function during searches. + Searching Sorted Lists ---------------------- -The above :func:`bisect` functions are useful for finding insertion points, but -can be tricky or awkward to use for common searching tasks. The following three +The above :func:`bisect` functions are useful for finding insertion points but +can be tricky or awkward to use for common searching tasks. The following five functions show how to transform them into the standard lookups for sorted lists:: - def find(a, key): - '''Find leftmost item exact equal to the key. - Raise ValueError if no such item exists. - - ''' - i = bisect_left(a, key) - if i < len(a) and a[i] == key: + def index(a, x): + 'Locate the leftmost value exactly equal to x' + i = bisect_left(a, x) + if i != len(a) and a[i] == x: + return i + raise ValueError + + def find_lt(a, x): + 'Find rightmost value less than x' + i = bisect_left(a, x) + if i: + return a[i-1] + raise ValueError + + def find_le(a, x): + 'Find rightmost value less than or equal to x' + i = bisect_right(a, x) + if i: + return a[i-1] + raise ValueError + + def find_gt(a, x): + 'Find leftmost value greater than x' + i = bisect_right(a, x) + if i != len(a): return a[i] - raise ValueError('No item found with key equal to: %r' % (key,)) + raise ValueError - def find_le(a, key): - '''Find largest item less-than or equal to key. - Raise ValueError if no such item exists. - If multiple keys are equal, return the leftmost. - - ''' - i = bisect_left(a, key) - if i < len(a) and a[i] == key: + def find_ge(a, x): + 'Find leftmost item greater than or equal to x' + i = bisect_left(a, x) + if i != len(a): return a[i] - if i == 0: - raise ValueError('No item found with key at or below: %r' % (key,)) - return a[i-1] - - def find_ge(a, key): - '''Find smallest item greater-than or equal to key. - Raise ValueError if no such item exists. - If multiple keys are equal, return the leftmost. - - ''' - i = bisect_left(a, key) - if i == len(a): - raise ValueError('No item found with key at or above: %r' % (key,)) - return a[i] + raise ValueError + Other Examples -------------- .. _bisect-example: -The :func:`bisect` function is generally useful for categorizing numeric data. -This example uses :func:`bisect` to look up a letter grade for an exam total -(say) based on a set of ordered numeric breakpoints: 85 and up is an 'A', 75..84 -is a 'B', etc. - - >>> grades = "FEDCBA" - >>> breakpoints = [30, 44, 66, 75, 85] - >>> from bisect import bisect - >>> def grade(total): - ... return grades[bisect(breakpoints, total)] +The :func:`bisect` function can be useful for numeric table lookups. This +example uses :func:`bisect` to look up a letter grade for an exam score (say) +based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is +a 'B', and so on:: + + >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'): + ... i = bisect(breakpoints, score) + ... return grades[i] ... - >>> grade(66) - 'C' - >>> map(grade, [33, 99, 77, 44, 12, 88]) - ['E', 'A', 'B', 'D', 'F', 'A'] + >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]] + ['F', 'A', 'C', 'C', 'B', 'A', 'A'] Unlike the :func:`sorted` function, it does not make sense for the :func:`bisect` functions to have *key* or *reversed* arguments because that would lead to an @@ -135,9 +143,3 @@ >>> data[bisect_left(keys, 8)] ('yellow', 8) -.. seealso:: - - `SortedCollection recipe - `_ that - encapsulates precomputed keys, allowing straight-forward insertion and - searching using a *key* function. From python-checkins at python.org Wed Sep 1 10:27:16 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 10:27:16 +0200 (CEST) Subject: [Python-checkins] r84386 - python/branches/release31-maint/Doc/library/operator.rst Message-ID: <20100901082716.8856CEE985@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 10:27:16 2010 New Revision: 84386 Log: Fixup broken example Modified: python/branches/release31-maint/Doc/library/operator.rst Modified: python/branches/release31-maint/Doc/library/operator.rst ============================================================================== --- python/branches/release31-maint/Doc/library/operator.rst (original) +++ python/branches/release31-maint/Doc/library/operator.rst Wed Sep 1 10:27:16 2010 @@ -387,7 +387,7 @@ >>> inventory = [('apple', 3), ('banana', 2), ('pear', 5), ('orange', 1)] >>> getcount = itemgetter(1) - >>> map(getcount, inventory) + >>> list(map(getcount, inventory)) [3, 2, 5, 1] >>> sorted(inventory, key=getcount) [('orange', 1), ('banana', 2), ('apple', 3), ('pear', 5)] From python-checkins at python.org Wed Sep 1 10:56:10 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 10:56:10 +0200 (CEST) Subject: [Python-checkins] r84387 - python/branches/py3k/Modules/_bisectmodule.c Message-ID: <20100901085610.BCD1DEE9C6@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 10:56:10 2010 New Revision: 84387 Log: Fix line wrapping Modified: python/branches/py3k/Modules/_bisectmodule.c Modified: python/branches/py3k/Modules/_bisectmodule.c ============================================================================== --- python/branches/py3k/Modules/_bisectmodule.c (original) +++ python/branches/py3k/Modules/_bisectmodule.c Wed Sep 1 10:56:10 2010 @@ -86,8 +86,7 @@ if (PyList_Insert(list, index, item) < 0) return NULL; } else { - result = PyObject_CallMethod(list, "insert", "nO", - index, item); + result = PyObject_CallMethod(list, "insert", "nO", index, item); if (result == NULL) return NULL; Py_DECREF(result); @@ -187,8 +186,7 @@ if (PyList_Insert(list, index, item) < 0) return NULL; } else { - result = PyObject_CallMethod(list, "insert", "iO", - index, item); + result = PyObject_CallMethod(list, "insert", "iO", index, item); if (result == NULL) return NULL; Py_DECREF(result); From python-checkins at python.org Wed Sep 1 10:57:16 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 10:57:16 +0200 (CEST) Subject: [Python-checkins] r84388 - in python/branches/py3k/Doc: conf.py using/index.rst Message-ID: <20100901085716.683C2EE9E8@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 10:57:16 2010 New Revision: 84388 Log: More descriptive title. Modified: python/branches/py3k/Doc/conf.py python/branches/py3k/Doc/using/index.rst Modified: python/branches/py3k/Doc/conf.py ============================================================================== --- python/branches/py3k/Doc/conf.py (original) +++ python/branches/py3k/Doc/conf.py Wed Sep 1 10:57:16 2010 @@ -131,7 +131,7 @@ ('tutorial/index', 'tutorial.tex', 'Python Tutorial', _stdauthor, 'manual'), ('using/index', 'using.tex', - 'Using Python', _stdauthor, 'manual'), + 'Python Setup', _stdauthor, 'manual'), ('whatsnew/' + version, 'whatsnew.tex', 'What\'s New in Python', 'A. M. Kuchling', 'howto'), ] Modified: python/branches/py3k/Doc/using/index.rst ============================================================================== --- python/branches/py3k/Doc/using/index.rst (original) +++ python/branches/py3k/Doc/using/index.rst Wed Sep 1 10:57:16 2010 @@ -1,7 +1,7 @@ .. _using-index: ################ - Using Python + Python Setup ################ From python-checkins at python.org Wed Sep 1 11:15:42 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 11:15:42 +0200 (CEST) Subject: [Python-checkins] r84389 - in python/branches/py3k/Doc/howto: index.rst sorting.rst Message-ID: <20100901091542.C7137EE985@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 11:15:42 2010 New Revision: 84389 Log: Forward port sorting howto Added: python/branches/py3k/Doc/howto/sorting.rst (contents, props changed) Modified: python/branches/py3k/Doc/howto/index.rst Modified: python/branches/py3k/Doc/howto/index.rst ============================================================================== --- python/branches/py3k/Doc/howto/index.rst (original) +++ python/branches/py3k/Doc/howto/index.rst Wed Sep 1 11:15:42 2010 @@ -21,6 +21,7 @@ functional.rst regex.rst sockets.rst + sorting.rst unicode.rst urllib2.rst webservers.rst Added: python/branches/py3k/Doc/howto/sorting.rst ============================================================================== --- (empty file) +++ python/branches/py3k/Doc/howto/sorting.rst Wed Sep 1 11:15:42 2010 @@ -0,0 +1,281 @@ +Sorting HOW TO +************** + +:Author: Andrew Dalke and Raymond Hettinger +:Release: 0.1 + + +Python lists have a built-in :meth:`list.sort` method that modifies the list +in-place and a :func:`sorted` built-in function that builds a new sorted list +from an iterable. + +In this document, we explore the various techniques for sorting data using Python. + + +Sorting Basics +============== + +A simple ascending sort is very easy: just call the :func:`sorted` function. It +returns a new sorted list:: + + >>> sorted([5, 2, 3, 1, 4]) + [1, 2, 3, 4, 5] + +You can also use the :meth:`list.sort` method of a list. It modifies the list +in-place (and returns *None* to avoid confusion). Usually it's less convenient +than :func:`sorted` - but if you don't need the original list, it's slightly +more efficient. + + >>> a = [5, 2, 3, 1, 4] + >>> a.sort() + >>> a + [1, 2, 3, 4, 5] + +Another difference is that the :meth:`list.sort` method is only defined for +lists. In contrast, the :func:`sorted` function accepts any iterable. + + >>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'}) + [1, 2, 3, 4, 5] + +Key Functions +============= + +Both :meth:`list.sort` and :func:`sorted` have *key* parameter to specify a +function to be called on each list element prior to making comparisons. + +For example, here's a case-insensitive string comparison: + + >>> sorted("This is a test string from Andrew".split(), key=str.lower) + ['a', 'Andrew', 'from', 'is', 'string', 'test', 'This'] + +The value of the *key* parameter should be a function that takes a single argument +and returns a key to use for sorting purposes. This technique is fast because +the key function is called exactly once for each input record. + +A common pattern is to sort complex objects using some of the object's indices +as keys. For example: + + >>> student_tuples = [ + ('john', 'A', 15), + ('jane', 'B', 12), + ('dave', 'B', 10), + ] + >>> sorted(student_tuples, key=lambda student: student[2]) # sort by age + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +The same technique works for objects with named attributes. For example: + + >>> class Student: + def __init__(self, name, grade, age): + self.name = name + self.grade = grade + self.age = age + def __repr__(self): + return repr((self.name, self.grade, self.age)) + + >>> student_objects = [ + Student('john', 'A', 15), + Student('jane', 'B', 12), + Student('dave', 'B', 10), + ] + >>> sorted(student_objects, key=lambda student: student.age) # sort by age + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +Operator Module Functions +========================= + +The key-function patterns shown above are very common, so Python provides +convenience functions to make accessor functions easier and faster. The operator +module has :func:`operator.itemgetter`, :func:`operator.attrgetter`, and +an :func:`operator.methodcaller` function. + +Using those functions, the above examples become simpler and faster: + + >>> from operator import itemgetter, attrgetter + + >>> sorted(student_tuples, key=itemgetter(2)) + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + + >>> sorted(student_objects, key=attrgetter('age')) + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +The operator module functions allow multiple levels of sorting. For example, to +sort by *grade* then by *age*: + + >>> sorted(student_tuples, key=itemgetter(1,2)) + [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)] + + >>> sorted(student_objects, key=attrgetter('grade', 'age')) + [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)] + +Ascending and Descending +======================== + +Both :meth:`list.sort` and :func:`sorted` accept a *reverse* parameter with a +boolean value. This is using to flag descending sorts. For example, to get the +student data in reverse *age* order: + + >>> sorted(student_tuples, key=itemgetter(2), reverse=True) + [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)] + + >>> sorted(student_objects, key=attrgetter('age'), reverse=True) + [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)] + +Sort Stability and Complex Sorts +================================ + +Sorts are guaranteed to be `stable +`_\. That means that +when multiple records have the same key, their original order is preserved. + + >>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)] + >>> sorted(data, key=itemgetter(0)) + [('blue', 1), ('blue', 2), ('red', 1), ('red', 2)] + +Notice how the two records for *blue* retain their original order so that +``('blue', 1)`` is guaranteed to precede ``('blue', 2)``. + +This wonderful property lets you build complex sorts in a series of sorting +steps. For example, to sort the student data by descending *grade* and then +ascending *age*, do the *age* sort first and then sort again using *grade*: + + >>> s = sorted(student_objects, key=attrgetter('age')) # sort on secondary key + >>> sorted(s, key=attrgetter('grade'), reverse=True) # now sort on primary key, descending + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +The `Timsort `_ algorithm used in Python +does multiple sorts efficiently because it can take advantage of any ordering +already present in a dataset. + +The Old Way Using Decorate-Sort-Undecorate +========================================== + +This idiom is called Decorate-Sort-Undecorate after its three steps: + +* First, the initial list is decorated with new values that control the sort order. + +* Second, the decorated list is sorted. + +* Finally, the decorations are removed, creating a list that contains only the + initial values in the new order. + +For example, to sort the student data by *grade* using the DSU approach: + + >>> decorated = [(student.grade, i, student) for i, student in enumerate(student_objects)] + >>> decorated.sort() + >>> [student for grade, i, student in decorated] # undecorate + [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)] + +This idiom works because tuples are compared lexicographically; the first items +are compared; if they are the same then the second items are compared, and so +on. + +It is not strictly necessary in all cases to include the index *i* in the +decorated list, but including it gives two benefits: + +* The sort is stable -- if two items have the same key, their order will be + preserved in the sorted list. + +* The original items do not have to be comparable because the ordering of the + decorated tuples will be determined by at most the first two items. So for + example the original list could contain complex numbers which cannot be sorted + directly. + +Another name for this idiom is +`Schwartzian transform `_\, +after Randal L. Schwartz, who popularized it among Perl programmers. + +Now that Python sorting provides key-functions, this technique is not often needed. + + +The Old Way Using the *cmp* Parameter +===================================== + +Many constructs given in this HOWTO assume Python 2.4 or later. Before that, +there was no :func:`sorted` builtin and :meth:`list.sort` took no keyword +arguments. Instead, all of the Py2.x versions supported a *cmp* parameter to +handle user specified comparison functions. + +In Py3.0, the *cmp* parameter was removed entirely (as part of a larger effort to +simplify and unify the language, eliminating the conflict between rich +comparisons and the :meth:`__cmp__` magic method). + +In Py2.x, sort allowed an optional function which can be called for doing the +comparisons. That function should take two arguments to be compared and then +return a negative value for less-than, return zero if they are equal, or return +a positive value for greater-than. For example, we can do: + + >>> def numeric_compare(x, y): + return x - y + >>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare) + [1, 2, 3, 4, 5] + +Or you can reverse the order of comparison with: + + >>> def reverse_numeric(x, y): + return y - x + >>> sorted([5, 2, 4, 1, 3], cmp=reverse_numeric) + [5, 4, 3, 2, 1] + +When porting code from Python 2.x to 3.x, the situation can arise when you have +the user supplying a comparison function and you need to convert that to a key +function. The following wrapper makes that easy to do:: + + def cmp_to_key(mycmp): + 'Convert a cmp= function into a key= function' + class K(object): + def __init__(self, obj, *args): + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) < 0 + def __gt__(self, other): + return mycmp(self.obj, other.obj) > 0 + def __eq__(self, other): + return mycmp(self.obj, other.obj) == 0 + def __le__(self, other): + return mycmp(self.obj, other.obj) <= 0 + def __ge__(self, other): + return mycmp(self.obj, other.obj) >= 0 + def __ne__(self, other): + return mycmp(self.obj, other.obj) != 0 + return K + +To convert to a key function, just wrap the old comparison function: + + >>> sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric)) + [5, 4, 3, 2, 1] + +In Python 3.2, the :func:`functools.cmp_to_key` function was added to the +functools module in the standard library. + +Odd and Ends +============ + +* For locale aware sorting, use :func:`locale.strxfrm` for a key function or + :func:`locale.strcoll` for a comparison function. + +* The *reverse* parameter still maintains sort stability (i.e. records with + equal keys retain the original order). Interestingly, that effect can be + simulated without the parameter by using the builtin :func:`reversed` function + twice: + + >>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)] + >>> assert sorted(data, reverse=True) == list(reversed(sorted(reversed(data)))) + +* The sort routines are guaranteed to use :meth:`__lt__` when making comparisons + between two objects. So, it is easy to add a standard sort order to a class by + defining an :meth:`__lt__` method:: + + >>> Student.__lt__ = lambda self, other: self.age < other.age + >>> sorted(student_objects) + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +* Key functions need not depend directly on the objects being sorted. A key + function can also access external resources. For instance, if the student grades + are stored in a dictionary, they can be used to sort a separate list of student + names: + + >>> students = ['dave', 'john', 'jane'] + >>> newgrades = {'john': 'F', 'jane':'A', 'dave': 'C'} + >>> sorted(students, key=newgrades.__getitem__) + ['jane', 'dave', 'john'] From python-checkins at python.org Wed Sep 1 11:17:24 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 11:17:24 +0200 (CEST) Subject: [Python-checkins] r84390 - in python/branches/release31-maint/Doc/howto: index.rst sorting.rst Message-ID: <20100901091724.28827EE985@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 11:17:24 2010 New Revision: 84390 Log: Forward port sorting howto Added: python/branches/release31-maint/Doc/howto/sorting.rst (contents, props changed) Modified: python/branches/release31-maint/Doc/howto/index.rst Modified: python/branches/release31-maint/Doc/howto/index.rst ============================================================================== --- python/branches/release31-maint/Doc/howto/index.rst (original) +++ python/branches/release31-maint/Doc/howto/index.rst Wed Sep 1 11:17:24 2010 @@ -20,6 +20,7 @@ functional.rst regex.rst sockets.rst + sorting.rst unicode.rst urllib2.rst webservers.rst Added: python/branches/release31-maint/Doc/howto/sorting.rst ============================================================================== --- (empty file) +++ python/branches/release31-maint/Doc/howto/sorting.rst Wed Sep 1 11:17:24 2010 @@ -0,0 +1,279 @@ +Sorting HOW TO +************** + +:Author: Andrew Dalke and Raymond Hettinger +:Release: 0.1 + + +Python lists have a built-in :meth:`list.sort` method that modifies the list +in-place and a :func:`sorted` built-in function that builds a new sorted list +from an iterable. + +In this document, we explore the various techniques for sorting data using Python. + + +Sorting Basics +============== + +A simple ascending sort is very easy: just call the :func:`sorted` function. It +returns a new sorted list:: + + >>> sorted([5, 2, 3, 1, 4]) + [1, 2, 3, 4, 5] + +You can also use the :meth:`list.sort` method of a list. It modifies the list +in-place (and returns *None* to avoid confusion). Usually it's less convenient +than :func:`sorted` - but if you don't need the original list, it's slightly +more efficient. + + >>> a = [5, 2, 3, 1, 4] + >>> a.sort() + >>> a + [1, 2, 3, 4, 5] + +Another difference is that the :meth:`list.sort` method is only defined for +lists. In contrast, the :func:`sorted` function accepts any iterable. + + >>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'}) + [1, 2, 3, 4, 5] + +Key Functions +============= + +Both :meth:`list.sort` and :func:`sorted` have *key* parameter to specify a +function to be called on each list element prior to making comparisons. + +For example, here's a case-insensitive string comparison: + + >>> sorted("This is a test string from Andrew".split(), key=str.lower) + ['a', 'Andrew', 'from', 'is', 'string', 'test', 'This'] + +The value of the *key* parameter should be a function that takes a single argument +and returns a key to use for sorting purposes. This technique is fast because +the key function is called exactly once for each input record. + +A common pattern is to sort complex objects using some of the object's indices +as keys. For example: + + >>> student_tuples = [ + ('john', 'A', 15), + ('jane', 'B', 12), + ('dave', 'B', 10), + ] + >>> sorted(student_tuples, key=lambda student: student[2]) # sort by age + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +The same technique works for objects with named attributes. For example: + + >>> class Student: + def __init__(self, name, grade, age): + self.name = name + self.grade = grade + self.age = age + def __repr__(self): + return repr((self.name, self.grade, self.age)) + + >>> student_objects = [ + Student('john', 'A', 15), + Student('jane', 'B', 12), + Student('dave', 'B', 10), + ] + >>> sorted(student_objects, key=lambda student: student.age) # sort by age + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +Operator Module Functions +========================= + +The key-function patterns shown above are very common, so Python provides +convenience functions to make accessor functions easier and faster. The operator +module has :func:`operator.itemgetter`, :func:`operator.attrgetter`, and +an :func:`operator.methodcaller` function. + +Using those functions, the above examples become simpler and faster: + + >>> from operator import itemgetter, attrgetter + + >>> sorted(student_tuples, key=itemgetter(2)) + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + + >>> sorted(student_objects, key=attrgetter('age')) + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +The operator module functions allow multiple levels of sorting. For example, to +sort by *grade* then by *age*: + + >>> sorted(student_tuples, key=itemgetter(1,2)) + [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)] + + >>> sorted(student_objects, key=attrgetter('grade', 'age')) + [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)] + +Ascending and Descending +======================== + +Both :meth:`list.sort` and :func:`sorted` accept a *reverse* parameter with a +boolean value. This is using to flag descending sorts. For example, to get the +student data in reverse *age* order: + + >>> sorted(student_tuples, key=itemgetter(2), reverse=True) + [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)] + + >>> sorted(student_objects, key=attrgetter('age'), reverse=True) + [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)] + +Sort Stability and Complex Sorts +================================ + +Sorts are guaranteed to be `stable +`_\. That means that +when multiple records have the same key, their original order is preserved. + + >>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)] + >>> sorted(data, key=itemgetter(0)) + [('blue', 1), ('blue', 2), ('red', 1), ('red', 2)] + +Notice how the two records for *blue* retain their original order so that +``('blue', 1)`` is guaranteed to precede ``('blue', 2)``. + +This wonderful property lets you build complex sorts in a series of sorting +steps. For example, to sort the student data by descending *grade* and then +ascending *age*, do the *age* sort first and then sort again using *grade*: + + >>> s = sorted(student_objects, key=attrgetter('age')) # sort on secondary key + >>> sorted(s, key=attrgetter('grade'), reverse=True) # now sort on primary key, descending + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +The `Timsort `_ algorithm used in Python +does multiple sorts efficiently because it can take advantage of any ordering +already present in a dataset. + +The Old Way Using Decorate-Sort-Undecorate +========================================== + +This idiom is called Decorate-Sort-Undecorate after its three steps: + +* First, the initial list is decorated with new values that control the sort order. + +* Second, the decorated list is sorted. + +* Finally, the decorations are removed, creating a list that contains only the + initial values in the new order. + +For example, to sort the student data by *grade* using the DSU approach: + + >>> decorated = [(student.grade, i, student) for i, student in enumerate(student_objects)] + >>> decorated.sort() + >>> [student for grade, i, student in decorated] # undecorate + [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)] + +This idiom works because tuples are compared lexicographically; the first items +are compared; if they are the same then the second items are compared, and so +on. + +It is not strictly necessary in all cases to include the index *i* in the +decorated list, but including it gives two benefits: + +* The sort is stable -- if two items have the same key, their order will be + preserved in the sorted list. + +* The original items do not have to be comparable because the ordering of the + decorated tuples will be determined by at most the first two items. So for + example the original list could contain complex numbers which cannot be sorted + directly. + +Another name for this idiom is +`Schwartzian transform `_\, +after Randal L. Schwartz, who popularized it among Perl programmers. + +Now that Python sorting provides key-functions, this technique is not often needed. + + +The Old Way Using the *cmp* Parameter +===================================== + +Many constructs given in this HOWTO assume Python 2.4 or later. Before that, +there was no :func:`sorted` builtin and :meth:`list.sort` took no keyword +arguments. Instead, all of the Py2.x versions supported a *cmp* parameter to +handle user specified comparison functions. + +In Py3.0, the *cmp* parameter was removed entirely (as part of a larger effort to +simplify and unify the language, eliminating the conflict between rich +comparisons and the :meth:`__cmp__` magic method). + +In Py2.x, sort allowed an optional function which can be called for doing the +comparisons. That function should take two arguments to be compared and then +return a negative value for less-than, return zero if they are equal, or return +a positive value for greater-than. For example, we can do: + + >>> def numeric_compare(x, y): + return x - y + >>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare) + [1, 2, 3, 4, 5] + +Or you can reverse the order of comparison with: + + >>> def reverse_numeric(x, y): + return y - x + >>> sorted([5, 2, 4, 1, 3], cmp=reverse_numeric) + [5, 4, 3, 2, 1] + +When porting code from Python 2.x to 3.x, the situation can arise when you have +the user supplying a comparison function and you need to convert that to a key +function. The following wrapper makes that easy to do:: + + def cmp_to_key(mycmp): + 'Convert a cmp= function into a key= function' + class K(object): + def __init__(self, obj, *args): + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) < 0 + def __gt__(self, other): + return mycmp(self.obj, other.obj) > 0 + def __eq__(self, other): + return mycmp(self.obj, other.obj) == 0 + def __le__(self, other): + return mycmp(self.obj, other.obj) <= 0 + def __ge__(self, other): + return mycmp(self.obj, other.obj) >= 0 + def __ne__(self, other): + return mycmp(self.obj, other.obj) != 0 + return K + +To convert to a key function, just wrap the old comparison function: + + >>> sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric)) + [5, 4, 3, 2, 1] + + +Odd and Ends +============ + +* For locale aware sorting, use :func:`locale.strxfrm` for a key function or + :func:`locale.strcoll` for a comparison function. + +* The *reverse* parameter still maintains sort stability (i.e. records with + equal keys retain the original order). Interestingly, that effect can be + simulated without the parameter by using the builtin :func:`reversed` function + twice: + + >>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)] + >>> assert sorted(data, reverse=True) == list(reversed(sorted(reversed(data)))) + +* The sort routines are guaranteed to use :meth:`__lt__` when making comparisons + between two objects. So, it is easy to add a standard sort order to a class by + defining an :meth:`__lt__` method:: + + >>> Student.__lt__ = lambda self, other: self.age < other.age + >>> sorted(student_objects) + [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] + +* Key functions need not depend directly on the objects being sorted. A key + function can also access external resources. For instance, if the student grades + are stored in a dictionary, they can be used to sort a separate list of student + names: + + >>> students = ['dave', 'john', 'jane'] + >>> newgrades = {'john': 'F', 'jane':'A', 'dave': 'C'} + >>> sorted(students, key=newgrades.__getitem__) + ['jane', 'dave', 'john'] From python-checkins at python.org Wed Sep 1 14:58:25 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 14:58:25 +0200 (CEST) Subject: [Python-checkins] r84391 - in python/branches/py3k: Include/abstract.h Misc/NEWS Objects/abstract.c Objects/memoryobject.c Message-ID: <20100901125825.55BB5EE98A@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 14:58:21 2010 New Revision: 84391 Log: Issue #3101: Helper functions _add_one_to_C() and _add_one_to_F() become _Py_add_one_to_C() and _Py_add_one_to_F(), respectively. Modified: python/branches/py3k/Include/abstract.h python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/abstract.c python/branches/py3k/Objects/memoryobject.c Modified: python/branches/py3k/Include/abstract.h ============================================================================== --- python/branches/py3k/Include/abstract.h (original) +++ python/branches/py3k/Include/abstract.h Wed Sep 1 14:58:21 2010 @@ -1236,6 +1236,13 @@ PyAPI_FUNC(void) _Py_FreeCharPArray(char *const array[]); +/* For internal use by buffer API functions */ +PyAPI_FUNC(void) _Py_add_one_to_index_F(int nd, Py_ssize_t *index, + const Py_ssize_t *shape); +PyAPI_FUNC(void) _Py_add_one_to_index_C(int nd, Py_ssize_t *index, + const Py_ssize_t *shape); + + #ifdef __cplusplus } #endif Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 1 14:58:21 2010 @@ -354,6 +354,9 @@ Build ----- +- Issue #3101: Helper functions _add_one_to_C() and _add_one_to_F() become + _Py_add_one_to_C() and _Py_add_one_to_F(), respectively. + - Issue #9700: define HAVE_BROKEN_POSIX_SEMAPHORES under AIX 6.x. Patch by S?bastien Sabl?. Modified: python/branches/py3k/Objects/abstract.c ============================================================================== --- python/branches/py3k/Objects/abstract.c (original) +++ python/branches/py3k/Objects/abstract.c Wed Sep 1 14:58:21 2010 @@ -413,7 +413,7 @@ void -_add_one_to_index_F(int nd, Py_ssize_t *index, Py_ssize_t *shape) +_Py_add_one_to_index_F(int nd, Py_ssize_t *index, const Py_ssize_t *shape) { int k; @@ -429,7 +429,7 @@ } void -_add_one_to_index_C(int nd, Py_ssize_t *index, Py_ssize_t *shape) +_Py_add_one_to_index_C(int nd, Py_ssize_t *index, const Py_ssize_t *shape) { int k; @@ -453,7 +453,7 @@ PyBuffer_ToContiguous(void *buf, Py_buffer *view, Py_ssize_t len, char fort) { int k; - void (*addone)(int, Py_ssize_t *, Py_ssize_t *); + void (*addone)(int, Py_ssize_t *, const Py_ssize_t *); Py_ssize_t *indices, elements; char *dest, *ptr; @@ -480,10 +480,10 @@ } if (fort == 'F') { - addone = _add_one_to_index_F; + addone = _Py_add_one_to_index_F; } else { - addone = _add_one_to_index_C; + addone = _Py_add_one_to_index_C; } dest = buf; /* XXX : This is not going to be the fastest code in the world @@ -504,7 +504,7 @@ PyBuffer_FromContiguous(Py_buffer *view, void *buf, Py_ssize_t len, char fort) { int k; - void (*addone)(int, Py_ssize_t *, Py_ssize_t *); + void (*addone)(int, Py_ssize_t *, const Py_ssize_t *); Py_ssize_t *indices, elements; char *src, *ptr; @@ -531,10 +531,10 @@ } if (fort == 'F') { - addone = _add_one_to_index_F; + addone = _Py_add_one_to_index_F; } else { - addone = _add_one_to_index_C; + addone = _Py_add_one_to_index_C; } src = buf; /* XXX : This is not going to be the fastest code in the world @@ -611,7 +611,7 @@ elements *= view_src.shape[k]; } while (elements--) { - _add_one_to_index_C(view_src.ndim, indices, view_src.shape); + _Py_add_one_to_index_C(view_src.ndim, indices, view_src.shape); dptr = PyBuffer_GetPointer(&view_dest, indices); sptr = PyBuffer_GetPointer(&view_src, indices); memcpy(dptr, sptr, view_src.itemsize); Modified: python/branches/py3k/Objects/memoryobject.c ============================================================================== --- python/branches/py3k/Objects/memoryobject.c (original) +++ python/branches/py3k/Objects/memoryobject.c Wed Sep 1 14:58:21 2010 @@ -172,9 +172,6 @@ return; } -void _add_one_to_index_F(int nd, Py_ssize_t *index, Py_ssize_t *shape); -void _add_one_to_index_C(int nd, Py_ssize_t *index, Py_ssize_t *shape); - static int _indirect_copy_nd(char *dest, Py_buffer *view, char fort) { @@ -203,10 +200,10 @@ elements *= view->shape[k]; } if (fort == 'F') { - func = _add_one_to_index_F; + func = _Py_add_one_to_index_F; } else { - func = _add_one_to_index_C; + func = _Py_add_one_to_index_C; } while (elements--) { func(view->ndim, indices, view->shape); From python-checkins at python.org Wed Sep 1 15:01:35 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 15:01:35 +0200 (CEST) Subject: [Python-checkins] r84392 - in python/branches/release31-maint: Include/abstract.h Misc/NEWS Objects/abstract.c Objects/memoryobject.c Message-ID: <20100901130135.E7BEFEE98A@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 15:01:35 2010 New Revision: 84392 Log: Merged revisions 84391 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84391 | antoine.pitrou | 2010-09-01 14:58:21 +0200 (mer., 01 sept. 2010) | 5 lines Issue #3101: Helper functions _add_one_to_C() and _add_one_to_F() become _Py_add_one_to_C() and _Py_add_one_to_F(), respectively. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Include/abstract.h python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Objects/abstract.c python/branches/release31-maint/Objects/memoryobject.c Modified: python/branches/release31-maint/Include/abstract.h ============================================================================== --- python/branches/release31-maint/Include/abstract.h (original) +++ python/branches/release31-maint/Include/abstract.h Wed Sep 1 15:01:35 2010 @@ -1233,6 +1233,13 @@ PyAPI_FUNC(int) _PyObject_RealIsSubclass(PyObject *derived, PyObject *cls); +/* For internal use by buffer API functions */ +PyAPI_FUNC(void) _Py_add_one_to_index_F(int nd, Py_ssize_t *index, + const Py_ssize_t *shape); +PyAPI_FUNC(void) _Py_add_one_to_index_C(int nd, Py_ssize_t *index, + const Py_ssize_t *shape); + + #ifdef __cplusplus } #endif Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Wed Sep 1 15:01:35 2010 @@ -514,6 +514,9 @@ Build ----- +- Issue #3101: Helper functions _add_one_to_C() and _add_one_to_F() become + _Py_add_one_to_C() and _Py_add_one_to_F(), respectively. + - Issue #9700: define HAVE_BROKEN_POSIX_SEMAPHORES under AIX 6.x. Patch by S?bastien Sabl?. Modified: python/branches/release31-maint/Objects/abstract.c ============================================================================== --- python/branches/release31-maint/Objects/abstract.c (original) +++ python/branches/release31-maint/Objects/abstract.c Wed Sep 1 15:01:35 2010 @@ -413,7 +413,7 @@ void -_add_one_to_index_F(int nd, Py_ssize_t *index, Py_ssize_t *shape) +_Py_add_one_to_index_F(int nd, Py_ssize_t *index, const Py_ssize_t *shape) { int k; @@ -429,7 +429,7 @@ } void -_add_one_to_index_C(int nd, Py_ssize_t *index, Py_ssize_t *shape) +_Py_add_one_to_index_C(int nd, Py_ssize_t *index, const Py_ssize_t *shape) { int k; @@ -453,7 +453,7 @@ PyBuffer_ToContiguous(void *buf, Py_buffer *view, Py_ssize_t len, char fort) { int k; - void (*addone)(int, Py_ssize_t *, Py_ssize_t *); + void (*addone)(int, Py_ssize_t *, const Py_ssize_t *); Py_ssize_t *indices, elements; char *dest, *ptr; @@ -480,10 +480,10 @@ } if (fort == 'F') { - addone = _add_one_to_index_F; + addone = _Py_add_one_to_index_F; } else { - addone = _add_one_to_index_C; + addone = _Py_add_one_to_index_C; } dest = buf; /* XXX : This is not going to be the fastest code in the world @@ -504,7 +504,7 @@ PyBuffer_FromContiguous(Py_buffer *view, void *buf, Py_ssize_t len, char fort) { int k; - void (*addone)(int, Py_ssize_t *, Py_ssize_t *); + void (*addone)(int, Py_ssize_t *, const Py_ssize_t *); Py_ssize_t *indices, elements; char *src, *ptr; @@ -531,10 +531,10 @@ } if (fort == 'F') { - addone = _add_one_to_index_F; + addone = _Py_add_one_to_index_F; } else { - addone = _add_one_to_index_C; + addone = _Py_add_one_to_index_C; } src = buf; /* XXX : This is not going to be the fastest code in the world @@ -611,7 +611,7 @@ elements *= view_src.shape[k]; } while (elements--) { - _add_one_to_index_C(view_src.ndim, indices, view_src.shape); + _Py_add_one_to_index_C(view_src.ndim, indices, view_src.shape); dptr = PyBuffer_GetPointer(&view_dest, indices); sptr = PyBuffer_GetPointer(&view_src, indices); memcpy(dptr, sptr, view_src.itemsize); Modified: python/branches/release31-maint/Objects/memoryobject.c ============================================================================== --- python/branches/release31-maint/Objects/memoryobject.c (original) +++ python/branches/release31-maint/Objects/memoryobject.c Wed Sep 1 15:01:35 2010 @@ -172,9 +172,6 @@ return; } -void _add_one_to_index_F(int nd, Py_ssize_t *index, Py_ssize_t *shape); -void _add_one_to_index_C(int nd, Py_ssize_t *index, Py_ssize_t *shape); - static int _indirect_copy_nd(char *dest, Py_buffer *view, char fort) { @@ -203,10 +200,10 @@ elements *= view->shape[k]; } if (fort == 'F') { - func = _add_one_to_index_F; + func = _Py_add_one_to_index_F; } else { - func = _add_one_to_index_C; + func = _Py_add_one_to_index_C; } while (elements--) { func(view->ndim, indices, view->shape); From python-checkins at python.org Wed Sep 1 15:02:50 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 15:02:50 +0200 (CEST) Subject: [Python-checkins] r84393 - in python/branches/release27-maint: Include/abstract.h Misc/NEWS Objects/abstract.c Objects/memoryobject.c Message-ID: <20100901130250.CCED2EEAC7@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 15:02:50 2010 New Revision: 84393 Log: Merged revisions 84391 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84391 | antoine.pitrou | 2010-09-01 14:58:21 +0200 (mer., 01 sept. 2010) | 5 lines Issue #3101: Helper functions _add_one_to_C() and _add_one_to_F() become _Py_add_one_to_C() and _Py_add_one_to_F(), respectively. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Include/abstract.h python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Objects/abstract.c python/branches/release27-maint/Objects/memoryobject.c Modified: python/branches/release27-maint/Include/abstract.h ============================================================================== --- python/branches/release27-maint/Include/abstract.h (original) +++ python/branches/release27-maint/Include/abstract.h Wed Sep 1 15:02:50 2010 @@ -1383,6 +1383,13 @@ PyAPI_FUNC(int) _PyObject_RealIsSubclass(PyObject *derived, PyObject *cls); +/* For internal use by buffer API functions */ +PyAPI_FUNC(void) _Py_add_one_to_index_F(int nd, Py_ssize_t *index, + const Py_ssize_t *shape); +PyAPI_FUNC(void) _Py_add_one_to_index_C(int nd, Py_ssize_t *index, + const Py_ssize_t *shape); + + #ifdef __cplusplus } #endif Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Wed Sep 1 15:02:50 2010 @@ -260,6 +260,12 @@ Build ----- +- Issue #3101: Helper functions _add_one_to_C() and _add_one_to_F() become + _Py_add_one_to_C() and _Py_add_one_to_F(), respectively. + +- Issue #9700: define HAVE_BROKEN_POSIX_SEMAPHORES under AIX 6.x. Patch by + S?bastien Sabl?. + - Issue #9280: Make sharedinstall depend on sharedmods. - Issue #9275: The OSX installer once again installs links to binaries in @@ -526,9 +532,6 @@ Build ----- -- Issue #9700: define HAVE_BROKEN_POSIX_SEMAPHORES under AIX 6.x. Patch by - S?bastien Sabl?. - - Display installer warning that Windows 2000 won't be supported in future releases. Modified: python/branches/release27-maint/Objects/abstract.c ============================================================================== --- python/branches/release27-maint/Objects/abstract.c (original) +++ python/branches/release27-maint/Objects/abstract.c Wed Sep 1 15:02:50 2010 @@ -443,7 +443,7 @@ void -_add_one_to_index_F(int nd, Py_ssize_t *index, Py_ssize_t *shape) +_Py_add_one_to_index_F(int nd, Py_ssize_t *index, const Py_ssize_t *shape) { int k; @@ -459,7 +459,7 @@ } void -_add_one_to_index_C(int nd, Py_ssize_t *index, Py_ssize_t *shape) +_Py_add_one_to_index_C(int nd, Py_ssize_t *index, const Py_ssize_t *shape) { int k; @@ -483,7 +483,7 @@ PyBuffer_ToContiguous(void *buf, Py_buffer *view, Py_ssize_t len, char fort) { int k; - void (*addone)(int, Py_ssize_t *, Py_ssize_t *); + void (*addone)(int, Py_ssize_t *, const Py_ssize_t *); Py_ssize_t *indices, elements; char *dest, *ptr; @@ -510,10 +510,10 @@ } if (fort == 'F') { - addone = _add_one_to_index_F; + addone = _Py_add_one_to_index_F; } else { - addone = _add_one_to_index_C; + addone = _Py_add_one_to_index_C; } dest = buf; /* XXX : This is not going to be the fastest code in the world @@ -534,7 +534,7 @@ PyBuffer_FromContiguous(Py_buffer *view, void *buf, Py_ssize_t len, char fort) { int k; - void (*addone)(int, Py_ssize_t *, Py_ssize_t *); + void (*addone)(int, Py_ssize_t *, const Py_ssize_t *); Py_ssize_t *indices, elements; char *src, *ptr; @@ -561,10 +561,10 @@ } if (fort == 'F') { - addone = _add_one_to_index_F; + addone = _Py_add_one_to_index_F; } else { - addone = _add_one_to_index_C; + addone = _Py_add_one_to_index_C; } src = buf; /* XXX : This is not going to be the fastest code in the world @@ -641,7 +641,7 @@ elements *= view_src.shape[k]; } while (elements--) { - _add_one_to_index_C(view_src.ndim, indices, view_src.shape); + _Py_add_one_to_index_C(view_src.ndim, indices, view_src.shape); dptr = PyBuffer_GetPointer(&view_dest, indices); sptr = PyBuffer_GetPointer(&view_src, indices); memcpy(dptr, sptr, view_src.itemsize); Modified: python/branches/release27-maint/Objects/memoryobject.c ============================================================================== --- python/branches/release27-maint/Objects/memoryobject.c (original) +++ python/branches/release27-maint/Objects/memoryobject.c Wed Sep 1 15:02:50 2010 @@ -172,9 +172,6 @@ return; } -void _add_one_to_index_F(int nd, Py_ssize_t *index, Py_ssize_t *shape); -void _add_one_to_index_C(int nd, Py_ssize_t *index, Py_ssize_t *shape); - static int _indirect_copy_nd(char *dest, Py_buffer *view, char fort) { @@ -203,10 +200,10 @@ elements *= view->shape[k]; } if (fort == 'F') { - func = _add_one_to_index_F; + func = _Py_add_one_to_index_F; } else { - func = _add_one_to_index_C; + func = _Py_add_one_to_index_C; } while (elements--) { func(view->ndim, indices, view->shape); From python-checkins at python.org Wed Sep 1 17:10:13 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 17:10:13 +0200 (CEST) Subject: [Python-checkins] r84394 - in python/branches/py3k: Misc/ACKS Misc/NEWS Objects/unicodeobject.c Message-ID: <20100901151013.1DF28EEA80@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 17:10:12 2010 New Revision: 84394 Log: Issue #7415: PyUnicode_FromEncodedObject() now uses the new buffer API properly. Patch by Stefan Behnel. Modified: python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Wed Sep 1 17:10:12 2010 @@ -62,6 +62,7 @@ Robin Becker Neal Becker Bill Bedford +Stefan Behnel Reimer Behrends Ben Bell Thomas Bellman Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 1 17:10:12 2010 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #7415: PyUnicode_FromEncodedObject() now uses the new buffer API + properly. Patch by Stefan Behnel. + - Issue #5553: The Py_LOCAL_INLINE macro now results in inlining on most platforms. Previously, it online inlined when using Microsoft Visual C. Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Wed Sep 1 17:10:12 2010 @@ -1234,8 +1234,7 @@ const char *encoding, const char *errors) { - const char *s = NULL; - Py_ssize_t len; + Py_buffer buffer; PyObject *v; if (obj == NULL) { @@ -1243,44 +1242,44 @@ return NULL; } + /* Decoding bytes objects is the most common case and should be fast */ + if (PyBytes_Check(obj)) { + if (PyBytes_GET_SIZE(obj) == 0) { + Py_INCREF(unicode_empty); + v = (PyObject *) unicode_empty; + } + else { + v = PyUnicode_Decode( + PyBytes_AS_STRING(obj), PyBytes_GET_SIZE(obj), + encoding, errors); + } + return v; + } + if (PyUnicode_Check(obj)) { PyErr_SetString(PyExc_TypeError, "decoding str is not supported"); return NULL; } - /* Coerce object */ - if (PyBytes_Check(obj)) { - s = PyBytes_AS_STRING(obj); - len = PyBytes_GET_SIZE(obj); - } - else if (PyByteArray_Check(obj)) { - s = PyByteArray_AS_STRING(obj); - len = PyByteArray_GET_SIZE(obj); - } - else if (PyObject_AsCharBuffer(obj, &s, &len)) { - /* Overwrite the error message with something more useful in - case of a TypeError. */ - if (PyErr_ExceptionMatches(PyExc_TypeError)) - PyErr_Format(PyExc_TypeError, - "coercing to str: need bytes, bytearray or char buffer, " - "%.80s found", - Py_TYPE(obj)->tp_name); - goto onError; + /* Retrieve a bytes buffer view through the PEP 3118 buffer interface */ + if (PyObject_GetBuffer(obj, &buffer, PyBUF_SIMPLE) < 0) { + PyErr_Format(PyExc_TypeError, + "coercing to str: need bytes, bytearray " + "or buffer-like object, %.80s found", + Py_TYPE(obj)->tp_name); + return NULL; } - /* Convert to Unicode */ - if (len == 0) { + if (buffer.len == 0) { Py_INCREF(unicode_empty); - v = (PyObject *)unicode_empty; + v = (PyObject *) unicode_empty; } else - v = PyUnicode_Decode(s, len, encoding, errors); + v = PyUnicode_Decode((char*) buffer.buf, buffer.len, encoding, errors); + PyBuffer_Release(&buffer); return v; - - onError: - return NULL; } /* Convert encoding to lower case and replace '_' with '-' in order to From python-checkins at python.org Wed Sep 1 17:15:29 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 17:15:29 +0200 (CEST) Subject: [Python-checkins] r84395 - python/branches/release27-maint Message-ID: <20100901151529.1D9F7EEAC9@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 17:15:28 2010 New Revision: 84395 Log: Blocked revisions 84394 via svnmerge ........ r84394 | antoine.pitrou | 2010-09-01 17:10:12 +0200 (mer., 01 sept. 2010) | 4 lines Issue #7415: PyUnicode_FromEncodedObject() now uses the new buffer API properly. Patch by Stefan Behnel. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Wed Sep 1 17:16:42 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 17:16:42 +0200 (CEST) Subject: [Python-checkins] r84396 - in python/branches/release31-maint: Misc/ACKS Misc/NEWS Objects/unicodeobject.c Message-ID: <20100901151642.2C751CA71@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 17:16:41 2010 New Revision: 84396 Log: Merged revisions 84394 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84394 | antoine.pitrou | 2010-09-01 17:10:12 +0200 (mer., 01 sept. 2010) | 4 lines Issue #7415: PyUnicode_FromEncodedObject() now uses the new buffer API properly. Patch by Stefan Behnel. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/ACKS python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Objects/unicodeobject.c Modified: python/branches/release31-maint/Misc/ACKS ============================================================================== --- python/branches/release31-maint/Misc/ACKS (original) +++ python/branches/release31-maint/Misc/ACKS Wed Sep 1 17:16:41 2010 @@ -58,6 +58,7 @@ Robin Becker Neal Becker Bill Bedford +Stefan Behnel Reimer Behrends Ben Bell Thomas Bellman Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Wed Sep 1 17:16:41 2010 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #7415: PyUnicode_FromEncodedObject() now uses the new buffer API + properly. Patch by Stefan Behnel. + - Restore GIL in nis_cat in case of error. - Issue #9712: Fix tokenize on identifiers that start with non-ascii names. Modified: python/branches/release31-maint/Objects/unicodeobject.c ============================================================================== --- python/branches/release31-maint/Objects/unicodeobject.c (original) +++ python/branches/release31-maint/Objects/unicodeobject.c Wed Sep 1 17:16:41 2010 @@ -1162,8 +1162,7 @@ const char *encoding, const char *errors) { - const char *s = NULL; - Py_ssize_t len; + Py_buffer buffer; PyObject *v; if (obj == NULL) { @@ -1171,44 +1170,44 @@ return NULL; } + /* Decoding bytes objects is the most common case and should be fast */ + if (PyBytes_Check(obj)) { + if (PyBytes_GET_SIZE(obj) == 0) { + Py_INCREF(unicode_empty); + v = (PyObject *) unicode_empty; + } + else { + v = PyUnicode_Decode( + PyBytes_AS_STRING(obj), PyBytes_GET_SIZE(obj), + encoding, errors); + } + return v; + } + if (PyUnicode_Check(obj)) { PyErr_SetString(PyExc_TypeError, "decoding str is not supported"); return NULL; } - /* Coerce object */ - if (PyBytes_Check(obj)) { - s = PyBytes_AS_STRING(obj); - len = PyBytes_GET_SIZE(obj); - } - else if (PyByteArray_Check(obj)) { - s = PyByteArray_AS_STRING(obj); - len = PyByteArray_GET_SIZE(obj); - } - else if (PyObject_AsCharBuffer(obj, &s, &len)) { - /* Overwrite the error message with something more useful in - case of a TypeError. */ - if (PyErr_ExceptionMatches(PyExc_TypeError)) - PyErr_Format(PyExc_TypeError, - "coercing to str: need string or buffer, " - "%.80s found", - Py_TYPE(obj)->tp_name); - goto onError; + /* Retrieve a bytes buffer view through the PEP 3118 buffer interface */ + if (PyObject_GetBuffer(obj, &buffer, PyBUF_SIMPLE) < 0) { + PyErr_Format(PyExc_TypeError, + "coercing to str: need bytes, bytearray " + "or buffer-like object, %.80s found", + Py_TYPE(obj)->tp_name); + return NULL; } - /* Convert to Unicode */ - if (len == 0) { + if (buffer.len == 0) { Py_INCREF(unicode_empty); - v = (PyObject *)unicode_empty; + v = (PyObject *) unicode_empty; } else - v = PyUnicode_Decode(s, len, encoding, errors); + v = PyUnicode_Decode((char*) buffer.buf, buffer.len, encoding, errors); + PyBuffer_Release(&buffer); return v; - - onError: - return NULL; } PyObject *PyUnicode_Decode(const char *s, From python-checkins at python.org Wed Sep 1 20:54:56 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 20:54:56 +0200 (CEST) Subject: [Python-checkins] r84397 - in python/branches/py3k: Doc/library/sys.rst Include/unicodeobject.h Lib/site.py Lib/test/test_site.py Misc/NEWS Misc/cheatsheet Objects/unicodeobject.c Python/sysmodule.c Message-ID: <20100901185456.D756DEE9C4@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 20:54:56 2010 New Revision: 84397 Log: Issue #9549: sys.setdefaultencoding() and PyUnicode_SetDefaultEncoding() are now removed, since their effect was inexistent in 3.x (the default encoding is hardcoded to utf-8 and cannot be changed). Modified: python/branches/py3k/Doc/library/sys.rst python/branches/py3k/Include/unicodeobject.h python/branches/py3k/Lib/site.py python/branches/py3k/Lib/test/test_site.py python/branches/py3k/Misc/NEWS python/branches/py3k/Misc/cheatsheet python/branches/py3k/Objects/unicodeobject.c python/branches/py3k/Python/sysmodule.c Modified: python/branches/py3k/Doc/library/sys.rst ============================================================================== --- python/branches/py3k/Doc/library/sys.rst (original) +++ python/branches/py3k/Doc/library/sys.rst Wed Sep 1 20:54:56 2010 @@ -712,18 +712,6 @@ :func:`setswitchinterval` instead. -.. function:: setdefaultencoding(name) - - Set the current default string encoding used by the Unicode implementation. If - *name* does not match any available encoding, :exc:`LookupError` is raised. - This function is only intended to be used by the :mod:`site` module - implementation and, where needed, by :mod:`sitecustomize`. Once used by the - :mod:`site` module, it is removed from the :mod:`sys` module's namespace. - - .. Note that :mod:`site` is not imported if the :option:`-S` option is passed - to the interpreter, in which case this function will remain available. - - .. function:: setdlopenflags(n) Set the flags used by the interpreter for :cfunc:`dlopen` calls, such as when Modified: python/branches/py3k/Include/unicodeobject.h ============================================================================== --- python/branches/py3k/Include/unicodeobject.h (original) +++ python/branches/py3k/Include/unicodeobject.h Wed Sep 1 20:54:56 2010 @@ -212,7 +212,6 @@ # define PyUnicode_Replace PyUnicodeUCS2_Replace # define PyUnicode_Resize PyUnicodeUCS2_Resize # define PyUnicode_RichCompare PyUnicodeUCS2_RichCompare -# define PyUnicode_SetDefaultEncoding PyUnicodeUCS2_SetDefaultEncoding # define PyUnicode_Split PyUnicodeUCS2_Split # define PyUnicode_Splitlines PyUnicodeUCS2_Splitlines # define PyUnicode_Tailmatch PyUnicodeUCS2_Tailmatch @@ -295,7 +294,6 @@ # define PyUnicode_Replace PyUnicodeUCS4_Replace # define PyUnicode_Resize PyUnicodeUCS4_Resize # define PyUnicode_RichCompare PyUnicodeUCS4_RichCompare -# define PyUnicode_SetDefaultEncoding PyUnicodeUCS4_SetDefaultEncoding # define PyUnicode_Split PyUnicodeUCS4_Split # define PyUnicode_Splitlines PyUnicodeUCS4_Splitlines # define PyUnicode_Tailmatch PyUnicodeUCS4_Tailmatch @@ -708,16 +706,6 @@ PyAPI_FUNC(const char*) PyUnicode_GetDefaultEncoding(void); -/* Sets the currently active default encoding. - - Returns 0 on success, -1 in case of an error. - - */ - -PyAPI_FUNC(int) PyUnicode_SetDefaultEncoding( - const char *encoding /* Encoding name in standard form */ - ); - /* --- Generic Codecs ----------------------------------------------------- */ /* Create a Unicode object by decoding the encoded string s of the Modified: python/branches/py3k/Lib/site.py ============================================================================== --- python/branches/py3k/Lib/site.py (original) +++ python/branches/py3k/Lib/site.py Wed Sep 1 20:54:56 2010 @@ -460,25 +460,6 @@ encodings._cache[enc] = encodings._unknown encodings.aliases.aliases[enc] = 'mbcs' -def setencoding(): - """Set the string encoding used by the Unicode implementation. The - default is 'ascii', but if you're willing to experiment, you can - change this.""" - encoding = "ascii" # Default value set by _PyUnicode_Init() - if 0: - # Enable to support locale aware default string encodings. - import locale - loc = locale.getdefaultlocale() - if loc[1]: - encoding = loc[1] - if 0: - # Enable to switch off string to Unicode coercion and implicit - # Unicode to string conversion. - encoding = "undefined" - if encoding != "ascii": - # On Non-Unicode builds this will raise an AttributeError... - sys.setdefaultencoding(encoding) # Needs Python Unicode build ! - def execsitecustomize(): """Run custom site specific code, if available.""" @@ -527,15 +508,9 @@ setcopyright() sethelper() aliasmbcs() - setencoding() execsitecustomize() if ENABLE_USER_SITE: execusercustomize() - # Remove sys.setdefaultencoding() so that users cannot change the - # encoding after initialization. The test for presence is needed when - # this module is run as a script, because this code is executed twice. - if hasattr(sys, "setdefaultencoding"): - del sys.setdefaultencoding main() Modified: python/branches/py3k/Lib/test/test_site.py ============================================================================== --- python/branches/py3k/Lib/test/test_site.py (original) +++ python/branches/py3k/Lib/test/test_site.py Wed Sep 1 20:54:56 2010 @@ -29,10 +29,6 @@ class HelperFunctionsTests(unittest.TestCase): """Tests for helper functions. - - The setting of the encoding (set using sys.setdefaultencoding) used by - the Unicode implementation is not tested. - """ def setUp(self): @@ -333,10 +329,6 @@ else: self.fail("did not alias mbcs") - def test_setdefaultencoding_removed(self): - # Make sure sys.setdefaultencoding is gone - self.assertTrue(not hasattr(sys, "setdefaultencoding")) - def test_sitecustomize_executed(self): # If sitecustomize is available, it should have been imported. if "sitecustomize" not in sys.modules: Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 1 20:54:56 2010 @@ -12,6 +12,10 @@ Core and Builtins ----------------- +- Issue #9549: sys.setdefaultencoding() and PyUnicode_SetDefaultEncoding() + are now removed, since their effect was inexistent in 3.x (the default + encoding is hardcoded to utf-8 and cannot be changed). + - Issue #7415: PyUnicode_FromEncodedObject() now uses the new buffer API properly. Patch by Stefan Behnel. Modified: python/branches/py3k/Misc/cheatsheet ============================================================================== --- python/branches/py3k/Misc/cheatsheet (original) +++ python/branches/py3k/Misc/cheatsheet Wed Sep 1 20:54:56 2010 @@ -1326,8 +1326,6 @@ exc_info() traceback return value to a local variable in a function handling an exception will cause a circular reference. -setdefaultencoding Change default Unicode encoding - defaults to 7-bit ASCII. -(encoding) getrecursionlimit Retrieve maximum recursion depth. () setrecursionlimit Set maximum recursion depth. (Defaults to 1000.) Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Wed Sep 1 20:54:56 2010 @@ -1784,17 +1784,6 @@ return unicode_default_encoding; } -int PyUnicode_SetDefaultEncoding(const char *encoding) -{ - if (strcmp(encoding, unicode_default_encoding) != 0) { - PyErr_Format(PyExc_ValueError, - "Can only set default encoding to %s", - unicode_default_encoding); - return -1; - } - return 0; -} - /* create or adjust a UnicodeDecodeError */ static void make_decode_exception(PyObject **exceptionObject, Modified: python/branches/py3k/Python/sysmodule.c ============================================================================== --- python/branches/py3k/Python/sysmodule.c (original) +++ python/branches/py3k/Python/sysmodule.c Wed Sep 1 20:54:56 2010 @@ -183,24 +183,6 @@ ); static PyObject * -sys_setdefaultencoding(PyObject *self, PyObject *args) -{ - char *encoding; - if (!PyArg_ParseTuple(args, "s:setdefaultencoding", &encoding)) - return NULL; - if (PyUnicode_SetDefaultEncoding(encoding)) - return NULL; - Py_INCREF(Py_None); - return Py_None; -} - -PyDoc_STRVAR(setdefaultencoding_doc, -"setdefaultencoding(encoding)\n\ -\n\ -Set the current default string encoding used by the Unicode implementation." -); - -static PyObject * sys_getfilesystemencoding(PyObject *self) { if (Py_FileSystemDefaultEncoding) @@ -1030,8 +1012,6 @@ #ifdef USE_MALLOPT {"mdebug", sys_mdebug, METH_VARARGS}, #endif - {"setdefaultencoding", sys_setdefaultencoding, METH_VARARGS, - setdefaultencoding_doc}, {"setfilesystemencoding", sys_setfilesystemencoding, METH_VARARGS, setfilesystemencoding_doc}, {"setcheckinterval", sys_setcheckinterval, METH_VARARGS, From python-checkins at python.org Wed Sep 1 20:56:56 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 20:56:56 +0200 (CEST) Subject: [Python-checkins] r84398 - python/branches/release31-maint Message-ID: <20100901185656.4C649EE9C4@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 20:56:56 2010 New Revision: 84398 Log: Blocked revisions 84397 via svnmerge ........ r84397 | antoine.pitrou | 2010-09-01 20:54:56 +0200 (mer., 01 sept. 2010) | 5 lines Issue #9549: sys.setdefaultencoding() and PyUnicode_SetDefaultEncoding() are now removed, since their effect was inexistent in 3.x (the default encoding is hardcoded to utf-8 and cannot be changed). ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Wed Sep 1 20:57:02 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 20:57:02 +0200 (CEST) Subject: [Python-checkins] r84399 - python/branches/release27-maint Message-ID: <20100901185702.898DEEEA3F@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 20:57:02 2010 New Revision: 84399 Log: Blocked revisions 84397 via svnmerge ........ r84397 | antoine.pitrou | 2010-09-01 20:54:56 +0200 (mer., 01 sept. 2010) | 5 lines Issue #9549: sys.setdefaultencoding() and PyUnicode_SetDefaultEncoding() are now removed, since their effect was inexistent in 3.x (the default encoding is hardcoded to utf-8 and cannot be changed). ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Wed Sep 1 21:28:49 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 1 Sep 2010 21:28:49 +0200 (CEST) Subject: [Python-checkins] r84400 - python/branches/py3k/Modules/_ssl.c Message-ID: <20100901192849.3A662EE985@mail.python.org> Author: giampaolo.rodola Date: Wed Sep 1 21:28:49 2010 New Revision: 84400 Log: Issue #9693 - msg 115273: attempt to fix ssl module failures on certain OpenSSL versions by calling ERR_clear_error() before raising IOError Modified: python/branches/py3k/Modules/_ssl.c Modified: python/branches/py3k/Modules/_ssl.c ============================================================================== --- python/branches/py3k/Modules/_ssl.c (original) +++ python/branches/py3k/Modules/_ssl.c Wed Sep 1 21:28:49 2010 @@ -1603,6 +1603,7 @@ PySSL_END_ALLOW_THREADS if (r != 1) { if (errno != 0) { + ERR_clear_error(); PyErr_SetFromErrno(PyExc_IOError); } else { @@ -1619,6 +1620,7 @@ Py_XDECREF(certfile_bytes); if (r != 1) { if (errno != 0) { + ERR_clear_error(); PyErr_SetFromErrno(PyExc_IOError); } else { @@ -1686,6 +1688,7 @@ Py_XDECREF(capath_bytes); if (r != 1) { if (errno != 0) { + ERR_clear_error(); PyErr_SetFromErrno(PyExc_IOError); } else { From python-checkins at python.org Wed Sep 1 21:39:01 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 1 Sep 2010 21:39:01 +0200 (CEST) Subject: [Python-checkins] r84401 - python/branches/py3k/Objects/unicodeobject.c Message-ID: <20100901193901.D14F9EEABA@mail.python.org> Author: victor.stinner Date: Wed Sep 1 21:39:01 2010 New Revision: 84401 Log: Remove unicode_default_encoding constant Inline its value in PyUnicode_GetDefaultEncoding(). The comment is now outdated (we will not change its value anymore). Modified: python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Wed Sep 1 21:39:01 2010 @@ -114,15 +114,6 @@ shared as well. */ static PyUnicodeObject *unicode_latin1[256]; -/* Default encoding to use and assume when NULL is passed as encoding - parameter; it is fixed to "utf-8". Always use the - PyUnicode_GetDefaultEncoding() API to access this global. - - Don't forget to alter Py_FileSystemDefaultEncoding if you change the - hard coded default! -*/ -static const char unicode_default_encoding[] = "utf-8"; - /* Fast detection of the most frequent whitespace characters */ const unsigned char _Py_ascii_whitespace[] = { 0, 0, 0, 0, 0, 0, 0, 0, @@ -1781,7 +1772,7 @@ const char *PyUnicode_GetDefaultEncoding(void) { - return unicode_default_encoding; + return "utf-8"; } /* create or adjust a UnicodeDecodeError */ From python-checkins at python.org Wed Sep 1 21:42:36 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 21:42:36 +0200 (CEST) Subject: [Python-checkins] r84402 - python/branches/release27-maint/Doc/library/bisect.rst Message-ID: <20100901194236.35092EEA80@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 21:42:36 2010 New Revision: 84402 Log: Clean-up bisect docs. Modified: python/branches/release27-maint/Doc/library/bisect.rst Modified: python/branches/release27-maint/Doc/library/bisect.rst ============================================================================== --- python/branches/release27-maint/Doc/library/bisect.rst (original) +++ python/branches/release27-maint/Doc/library/bisect.rst Wed Sep 1 21:42:36 2010 @@ -1,10 +1,10 @@ - :mod:`bisect` --- Array bisection algorithm =========================================== .. module:: bisect :synopsis: Array bisection algorithms for binary searching. .. sectionauthor:: Fred L. Drake, Jr. +.. sectionauthor:: Raymond Hettinger .. example based on the PyModules FAQ entry by Aaron Watters This module provides support for maintaining a list in sorted order without @@ -19,103 +19,111 @@ The following functions are provided: -.. function:: bisect_left(list, item[, lo[, hi]]) - - Locate the proper insertion point for *item* in *list* to maintain sorted order. - The parameters *lo* and *hi* may be used to specify a subset of the list which - should be considered; by default the entire list is used. If *item* is already - present in *list*, the insertion point will be before (to the left of) any - existing entries. The return value is suitable for use as the first parameter - to ``list.insert()``. This assumes that *list* is already sorted. +.. function:: bisect_left(a, x, lo=0, hi=len(a)) + Locate the insertion point for *x* in *a* to maintain sorted order. + The parameters *lo* and *hi* may be used to specify a subset of the list + which should be considered; by default the entire list is used. If *x* is + already present in *a*, the insertion point will be before (to the left of) + any existing entries. The return value is suitable for use as the first + parameter to ``list.insert()`` assuming that *a* is already sorted. -.. function:: bisect_right(list, item[, lo[, hi]]) -.. function:: bisect(list, item[, lo[, hi]]) + The returned insertion point *i* partitions the array *a* into two halves so + that ``all(val < x for val in a[lo:i])`` for the left side and + ``all(val >= x for val in a[i:hi])`` for the right side. - Similar to :func:`bisect_left`, but returns an insertion point which comes after - (to the right of) any existing entries of *item* in *list*. +.. function:: bisect_right(a, x, lo=0, hi=len(a)) + bisect(a, x, lo=0, hi=len(a)) + Similar to :func:`bisect_left`, but returns an insertion point which comes + after (to the right of) any existing entries of *x* in *a*. -.. function:: insort_left(list, item[, lo[, hi]]) + The returned insertion point *i* partitions the array *a* into two halves so + that ``all(val <= x for val in a[lo:i])`` for the left side and + ``all(val > x for val in a[i:hi])`` for the right side. - Insert *item* in *list* in sorted order. This is equivalent to - ``list.insert(bisect.bisect_left(list, item, lo, hi), item)``. This assumes - that *list* is already sorted. +.. function:: insort_left(a, x, lo=0, hi=len(a)) - Also note that while the fast search step is O(log n), the slower insertion - step is O(n), so the overall operation is slow. + Insert *x* in *a* in sorted order. This is equivalent to + ``a.insert(bisect.bisect_left(a, x, lo, hi), x)`` assuming that *a* is + already sorted. Keep in mind that the O(log n) search is dominated by + the slow O(n) insertion step. -.. function:: insort_right(list, item[, lo[, hi]]) +.. function:: insort_right(a, x, lo=0, hi=len(a)) insort(a, x, lo=0, hi=len(a)) - Similar to :func:`insort_left`, but inserting *item* in *list* after any - existing entries of *item*. + Similar to :func:`insort_left`, but inserting *x* in *a* after any existing + entries of *x*. + +.. seealso:: + + `SortedCollection recipe + `_ that uses + bisect to build a full-featured collection class with straight-forward search + methods and support for a key-function. The keys are precomputed to save + unnecessary calls to the key function during searches. - Also note that while the fast search step is O(log n), the slower insertion - step is O(n), so the overall operation is slow. Searching Sorted Lists ---------------------- -The above :func:`bisect` functions are useful for finding insertion points, but -can be tricky or awkward to use for common searching tasks. The following three +The above :func:`bisect` functions are useful for finding insertion points but +can be tricky or awkward to use for common searching tasks. The following five functions show how to transform them into the standard lookups for sorted lists:: - def find(a, key): - '''Find leftmost item exact equal to the key. - Raise ValueError if no such item exists. - - ''' - i = bisect_left(a, key) - if i < len(a) and a[i] == key: + def index(a, x): + 'Locate the leftmost value exactly equal to x' + i = bisect_left(a, x) + if i != len(a) and a[i] == x: + return i + raise ValueError + + def find_lt(a, x): + 'Find rightmost value less than x' + i = bisect_left(a, x) + if i: + return a[i-1] + raise ValueError + + def find_le(a, x): + 'Find rightmost value less than or equal to x' + i = bisect_right(a, x) + if i: + return a[i-1] + raise ValueError + + def find_gt(a, x): + 'Find leftmost value greater than x' + i = bisect_right(a, x) + if i != len(a): return a[i] - raise ValueError('No item found with key equal to: %r' % (key,)) + raise ValueError - def find_le(a, key): - '''Find largest item less-than or equal to key. - Raise ValueError if no such item exists. - If multiple keys are equal, return the leftmost. - - ''' - i = bisect_left(a, key) - if i < len(a) and a[i] == key: + def find_ge(a, x): + 'Find leftmost item greater than or equal to x' + i = bisect_left(a, x) + if i != len(a): return a[i] - if i == 0: - raise ValueError('No item found with key at or below: %r' % (key,)) - return a[i-1] - - def find_ge(a, key): - '''Find smallest item greater-than or equal to key. - Raise ValueError if no such item exists. - If multiple keys are equal, return the leftmost. - - ''' - i = bisect_left(a, key) - if i == len(a): - raise ValueError('No item found with key at or above: %r' % (key,)) - return a[i] + raise ValueError + Other Examples -------------- .. _bisect-example: -The :func:`bisect` function is generally useful for categorizing numeric data. -This example uses :func:`bisect` to look up a letter grade for an exam total -(say) based on a set of ordered numeric breakpoints: 85 and up is an 'A', 75..84 -is a 'B', etc. - - >>> grades = "FEDCBA" - >>> breakpoints = [30, 44, 66, 75, 85] - >>> from bisect import bisect - >>> def grade(total): - ... return grades[bisect(breakpoints, total)] +The :func:`bisect` function can be useful for numeric table lookups. This +example uses :func:`bisect` to look up a letter grade for an exam score (say) +based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is +a 'B', and so on:: + + >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'): + ... i = bisect(breakpoints, score) + ... return grades[i] ... - >>> grade(66) - 'C' - >>> map(grade, [33, 99, 77, 44, 12, 88]) - ['E', 'A', 'B', 'D', 'F', 'A'] + >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]] + ['F', 'A', 'C', 'C', 'B', 'A', 'A'] Unlike the :func:`sorted` function, it does not make sense for the :func:`bisect` functions to have *key* or *reversed* arguments because that would lead to an @@ -137,9 +145,3 @@ >>> data[bisect_left(keys, 8)] ('yellow', 8) -.. seealso:: - - `SortedCollection recipe - `_ that - encapsulates precomputed keys, allowing straight-forward insertion and - searching using a *key* function. From python-checkins at python.org Wed Sep 1 22:29:34 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 22:29:34 +0200 (CEST) Subject: [Python-checkins] r84403 - in python/branches/py3k: Doc/library/array.rst Lib/multiprocessing/managers.py Lib/sre_compile.py Lib/test/test_array.py Lib/test/test_file.py Lib/test/test_io.py Lib/test/test_memoryio.py Lib/test/test_memoryview.py Lib/test/test_struct.py Lib/wave.py Misc/NEWS Modules/arraymodule.c Message-ID: <20100901202934.85370EEAC2@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 22:29:34 2010 New Revision: 84403 Log: Issue #8990: array.fromstring() and array.tostring() get renamed to frombytes() and tobytes(), respectively, to avoid confusion. Furthermore, array.frombytes(), array.extend() as well as the array.array() constructor now accept bytearray objects. Patch by Thomas Jollans. Modified: python/branches/py3k/Doc/library/array.rst python/branches/py3k/Lib/multiprocessing/managers.py python/branches/py3k/Lib/sre_compile.py python/branches/py3k/Lib/test/test_array.py python/branches/py3k/Lib/test/test_file.py python/branches/py3k/Lib/test/test_io.py python/branches/py3k/Lib/test/test_memoryio.py python/branches/py3k/Lib/test/test_memoryview.py python/branches/py3k/Lib/test/test_struct.py python/branches/py3k/Lib/wave.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/arraymodule.c Modified: python/branches/py3k/Doc/library/array.rst ============================================================================== --- python/branches/py3k/Doc/library/array.rst (original) +++ python/branches/py3k/Doc/library/array.rst Wed Sep 1 22:29:34 2010 @@ -60,7 +60,7 @@ appropriate type. If given a list or string, the initializer is passed to the new array's - :meth:`fromlist`, :meth:`fromstring`, or :meth:`fromunicode` method (see below) + :meth:`fromlist`, :meth:`frombytes`, or :meth:`fromunicode` method (see below) to add initial items to the array. Otherwise, the iterable initializer is passed to the :meth:`extend` method. @@ -136,6 +136,15 @@ must be the right type to be appended to the array. +.. method:: array.frombytes(s) + + Appends items from the string, interpreting the string as an array of machine + values (as if it had been read from a file using the :meth:`fromfile` method). + + .. versionadded:: 3.2 + :meth:`fromstring` is renamed to :meth:`frombytes` for clarity. + + .. method:: array.fromfile(f, n) Read *n* items (as machine values) from the file object *f* and append them to @@ -151,17 +160,16 @@ a.append(x)`` except that if there is a type error, the array is unchanged. -.. method:: array.fromstring(s) +.. method:: array.fromstring() - Appends items from the string, interpreting the string as an array of machine - values (as if it had been read from a file using the :meth:`fromfile` method). + Deprecated alias for :meth:`frombytes`. .. method:: array.fromunicode(s) Extends this array with data from the given unicode string. The array must be a type ``'u'`` array; otherwise a :exc:`ValueError` is raised. Use - ``array.fromstring(unicodestring.encode(enc))`` to append Unicode data to an + ``array.frombytes(unicodestring.encode(enc))`` to append Unicode data to an array of some other type. @@ -194,6 +202,16 @@ Reverse the order of the items in the array. +.. method:: array.tobytes() + + Convert the array to an array of machine values and return the bytes + representation (the same sequence of bytes that would be written to a file by + the :meth:`tofile` method.) + + .. versionadded:: 3.2 + :meth:`tostring` is renamed to :meth:`tobytes` for clarity. + + .. method:: array.tofile(f) Write all items (as machine values) to the file object *f*. @@ -206,15 +224,13 @@ .. method:: array.tostring() - Convert the array to an array of machine values and return the string - representation (the same sequence of bytes that would be written to a file by - the :meth:`tofile` method.) + Deprecated alias for :meth:`tobytes`. .. method:: array.tounicode() Convert the array to a unicode string. The array must be a type ``'u'`` array; - otherwise a :exc:`ValueError` is raised. Use ``array.tostring().decode(enc)`` to + otherwise a :exc:`ValueError` is raised. Use ``array.tobytes().decode(enc)`` to obtain a unicode string from an array of some other type. Modified: python/branches/py3k/Lib/multiprocessing/managers.py ============================================================================== --- python/branches/py3k/Lib/multiprocessing/managers.py (original) +++ python/branches/py3k/Lib/multiprocessing/managers.py Wed Sep 1 22:29:34 2010 @@ -32,7 +32,7 @@ # def reduce_array(a): - return array.array, (a.typecode, a.tostring()) + return array.array, (a.typecode, a.tobytes()) ForkingPickler.register(array.array, reduce_array) view_types = [type(getattr({}, name)()) for name in ('items','keys','values')] Modified: python/branches/py3k/Lib/sre_compile.py ============================================================================== --- python/branches/py3k/Lib/sre_compile.py (original) +++ python/branches/py3k/Lib/sre_compile.py Wed Sep 1 22:29:34 2010 @@ -343,7 +343,7 @@ else: code = 'I' # Convert block indices to byte array of 256 bytes - mapping = array.array('b', mapping).tostring() + mapping = array.array('b', mapping).tobytes() # Convert byte array to word array mapping = array.array(code, mapping) assert mapping.itemsize == _sre.CODESIZE Modified: python/branches/py3k/Lib/test/test_array.py ============================================================================== --- python/branches/py3k/Lib/test/test_array.py (original) +++ python/branches/py3k/Lib/test/test_array.py Wed Sep 1 22:29:34 2010 @@ -11,6 +11,7 @@ import io import math import struct +import warnings import array from array import _array_reconstructor as array_reconstructor @@ -367,15 +368,35 @@ self.assertEqual(a, b) def test_tofromstring(self): + nb_warnings = 4 + with warnings.catch_warnings(record=True) as r: + warnings.filterwarnings("always", + message=r"(to|from)string\(\) is deprecated", + category=DeprecationWarning) + a = array.array(self.typecode, 2*self.example) + b = array.array(self.typecode) + self.assertRaises(TypeError, a.tostring, 42) + self.assertRaises(TypeError, b.fromstring) + self.assertRaises(TypeError, b.fromstring, 42) + b.fromstring(a.tostring()) + self.assertEqual(a, b) + if a.itemsize>1: + self.assertRaises(ValueError, b.fromstring, "x") + nb_warnings += 1 + self.assertEqual(len(r), nb_warnings) + + def test_tofrombytes(self): a = array.array(self.typecode, 2*self.example) b = array.array(self.typecode) - self.assertRaises(TypeError, a.tostring, 42) - self.assertRaises(TypeError, b.fromstring) - self.assertRaises(TypeError, b.fromstring, 42) - b.fromstring(a.tostring()) + self.assertRaises(TypeError, a.tobytes, 42) + self.assertRaises(TypeError, b.frombytes) + self.assertRaises(TypeError, b.frombytes, 42) + b.frombytes(a.tobytes()) + c = array.array(self.typecode, bytearray(a.tobytes())) self.assertEqual(a, b) + self.assertEqual(a, c) if a.itemsize>1: - self.assertRaises(ValueError, b.fromstring, "x") + self.assertRaises(ValueError, b.frombytes, b"x") def test_repr(self): a = array.array(self.typecode, 2*self.example) @@ -898,8 +919,8 @@ a = array.array(self.typecode, self.example) m = memoryview(a) expected = m.tobytes() - self.assertEqual(a.tostring(), expected) - self.assertEqual(a.tostring()[0], expected[0]) + self.assertEqual(a.tobytes(), expected) + self.assertEqual(a.tobytes()[0], expected[0]) # Resizing is forbidden when there are buffer exports. # For issue 4509, we also check after each error that # the array was not modified. @@ -913,7 +934,7 @@ self.assertEqual(m.tobytes(), expected) self.assertRaises(BufferError, a.fromlist, a.tolist()) self.assertEqual(m.tobytes(), expected) - self.assertRaises(BufferError, a.fromstring, a.tostring()) + self.assertRaises(BufferError, a.frombytes, a.tobytes()) self.assertEqual(m.tobytes(), expected) if self.typecode == 'u': self.assertRaises(BufferError, a.fromunicode, a.tounicode()) @@ -932,7 +953,7 @@ def test_weakref(self): s = array.array(self.typecode, self.example) p = weakref.proxy(s) - self.assertEqual(p.tostring(), s.tostring()) + self.assertEqual(p.tobytes(), s.tobytes()) s = None self.assertRaises(ReferenceError, len, p) @@ -1110,6 +1131,23 @@ upper = int(pow(2, a.itemsize * 8)) - 1 self.check_overflow(lower, upper) + def test_bytes_extend(self): + s = bytes(self.example) + + a = array.array(self.typecode, self.example) + a.extend(s) + self.assertEqual( + a, + array.array(self.typecode, self.example+self.example) + ) + + a = array.array(self.typecode, self.example) + a.extend(bytearray(reversed(s))) + self.assertEqual( + a, + array.array(self.typecode, self.example+self.example[::-1]) + ) + class ByteTest(SignedNumberTest): typecode = 'b' @@ -1172,7 +1210,7 @@ # On alphas treating the byte swapped bit patters as # floats/doubles results in floating point exceptions # => compare the 8bit string values instead - self.assertNotEqual(a.tostring(), b.tostring()) + self.assertNotEqual(a.tobytes(), b.tobytes()) b.byteswap() self.assertEqual(a, b) Modified: python/branches/py3k/Lib/test/test_file.py ============================================================================== --- python/branches/py3k/Lib/test/test_file.py (original) +++ python/branches/py3k/Lib/test/test_file.py Wed Sep 1 22:29:34 2010 @@ -44,7 +44,7 @@ a = array('b', b'x'*10) self.f = self.open(TESTFN, 'rb') n = self.f.readinto(a) - self.assertEquals(b'12', a.tostring()[:n]) + self.assertEquals(b'12', a.tobytes()[:n]) def testReadinto_text(self): # verify readinto refuses text files @@ -281,7 +281,7 @@ except ValueError: self.fail("readinto() after next() with supposedly empty " "iteration-buffer failed anyway") - line = buf.tostring() + line = buf.tobytes() if line != testline: self.fail("readinto() after next() with empty buffer " "failed. Got %r, expected %r" % (line, testline)) Modified: python/branches/py3k/Lib/test/test_io.py ============================================================================== --- python/branches/py3k/Lib/test/test_io.py (original) +++ python/branches/py3k/Lib/test/test_io.py Wed Sep 1 22:29:34 2010 @@ -480,7 +480,7 @@ def test_array_writes(self): a = array.array('i', range(10)) - n = len(a.tostring()) + n = len(a.tobytes()) with self.open(support.TESTFN, "wb", 0) as f: self.assertEqual(f.write(a), n) with self.open(support.TESTFN, "wb") as f: Modified: python/branches/py3k/Lib/test/test_memoryio.py ============================================================================== --- python/branches/py3k/Lib/test/test_memoryio.py (original) +++ python/branches/py3k/Lib/test/test_memoryio.py Wed Sep 1 22:29:34 2010 @@ -425,7 +425,7 @@ a = array.array('b', b"hello world") memio = self.ioclass(buf) memio.readinto(a) - self.assertEqual(a.tostring(), b"1234567890d") + self.assertEqual(a.tobytes(), b"1234567890d") memio.close() self.assertRaises(ValueError, memio.readinto, b) Modified: python/branches/py3k/Lib/test/test_memoryview.py ============================================================================== --- python/branches/py3k/Lib/test/test_memoryview.py (original) +++ python/branches/py3k/Lib/test/test_memoryview.py Wed Sep 1 22:29:34 2010 @@ -231,7 +231,7 @@ class BaseArrayMemoryTests(AbstractMemoryTests): ro_type = None rw_type = lambda self, b: array.array('i', list(b)) - getitem_type = lambda self, b: array.array('i', list(b)).tostring() + getitem_type = lambda self, b: array.array('i', list(b)).tobytes() itemsize = array.array('i').itemsize format = 'i' Modified: python/branches/py3k/Lib/test/test_struct.py ============================================================================== --- python/branches/py3k/Lib/test/test_struct.py (original) +++ python/branches/py3k/Lib/test/test_struct.py Wed Sep 1 22:29:34 2010 @@ -430,12 +430,12 @@ # Test without offset s.pack_into(writable_buf, 0, test_string) - from_buf = writable_buf.tostring()[:len(test_string)] + from_buf = writable_buf.tobytes()[:len(test_string)] self.assertEqual(from_buf, test_string) # Test with offset. s.pack_into(writable_buf, 10, test_string) - from_buf = writable_buf.tostring()[:len(test_string)+10] + from_buf = writable_buf.tobytes()[:len(test_string)+10] self.assertEqual(from_buf, test_string[:10] + test_string) # Go beyond boundaries. @@ -458,12 +458,12 @@ # Test without offset. pack_into(writable_buf, 0, test_string) - from_buf = writable_buf.tostring()[:len(test_string)] + from_buf = writable_buf.tobytes()[:len(test_string)] self.assertEqual(from_buf, test_string) # Test with offset. pack_into(writable_buf, 10, test_string) - from_buf = writable_buf.tostring()[:len(test_string)+10] + from_buf = writable_buf.tobytes()[:len(test_string)+10] self.assertEqual(from_buf, test_string[:10] + test_string) # Go beyond boundaries. Modified: python/branches/py3k/Lib/wave.py ============================================================================== --- python/branches/py3k/Lib/wave.py (original) +++ python/branches/py3k/Lib/wave.py Wed Sep 1 22:29:34 2010 @@ -248,7 +248,7 @@ chunk = chunk.file chunk.size_read = chunk.size_read + nitems * self._sampwidth data.byteswap() - data = data.tostring() + data = data.tobytes() else: data = self._data_chunk.read(nframes * self._framesize) if self._convert and data: Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 1 22:29:34 2010 @@ -149,6 +149,11 @@ Library ------- +- Issue #8990: array.fromstring() and array.tostring() get renamed to + frombytes() and tobytes(), respectively, to avoid confusion. Furthermore, + array.frombytes(), array.extend() as well as the array.array() + constructor now accept bytearray objects. Patch by Thomas Jollans. + - Issue #808164: Fixed socket.close to avoid references to globals, to avoid issues when socket.close is called from a __del__ method. Modified: python/branches/py3k/Modules/arraymodule.c ============================================================================== --- python/branches/py3k/Modules/arraymodule.c (original) +++ python/branches/py3k/Modules/arraymodule.c Wed Sep 1 22:29:34 2010 @@ -1175,7 +1175,7 @@ /* Forward */ -static PyObject *array_fromstring(arrayobject *self, PyObject *args); +static PyObject *array_frombytes(arrayobject *self, PyObject *args); static PyObject * array_fromfile(arrayobject *self, PyObject *args) @@ -1212,7 +1212,7 @@ if (args == NULL) return NULL; - res = array_fromstring(self, args); + res = array_frombytes(self, args); Py_DECREF(args); if (res == NULL) return NULL; @@ -1331,45 +1331,84 @@ \n\ Convert array to an ordinary list with the same items."); - static PyObject * -array_fromstring(arrayobject *self, PyObject *args) +frombytes(arrayobject *self, Py_buffer *buffer) { - char *str; - Py_ssize_t n; int itemsize = self->ob_descr->itemsize; - if (!PyArg_ParseTuple(args, "s#:fromstring", &str, &n)) + Py_ssize_t n; + if (buffer->itemsize != 1) { + PyBuffer_Release(buffer); + PyErr_SetString(PyExc_TypeError, "string/buffer of bytes required."); return NULL; + } + n = buffer->len; if (n % itemsize != 0) { + PyBuffer_Release(buffer); PyErr_SetString(PyExc_ValueError, "string length not a multiple of item size"); return NULL; } n = n / itemsize; if (n > 0) { - Py_ssize_t old_size = Py_SIZE(self); + Py_ssize_t old_size = Py_SIZE(self); if ((n > PY_SSIZE_T_MAX - old_size) || ((old_size + n) > PY_SSIZE_T_MAX / itemsize)) { + PyBuffer_Release(buffer); return PyErr_NoMemory(); } - if (array_resize(self, old_size + n) == -1) + if (array_resize(self, old_size + n) == -1) { + PyBuffer_Release(buffer); return NULL; + } memcpy(self->ob_item + old_size * itemsize, - str, n * itemsize); + buffer->buf, n * itemsize); } + PyBuffer_Release(buffer); Py_INCREF(Py_None); return Py_None; } +static PyObject * +array_fromstring(arrayobject *self, PyObject *args) +{ + Py_buffer buffer; + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "fromstring() is deprecated. Use frombytes() instead.", 2) != 0) + return NULL; + if (!PyArg_ParseTuple(args, "s*:fromstring", &buffer)) + return NULL; + else + return frombytes(self, &buffer); +} + PyDoc_STRVAR(fromstring_doc, "fromstring(string)\n\ \n\ Appends items from the string, interpreting it as an array of machine\n\ +values, as if it had been read from a file using the fromfile() method).\n\ +\n\ +This method is deprecated. Use frombytes instead."); + + +static PyObject * +array_frombytes(arrayobject *self, PyObject *args) +{ + Py_buffer buffer; + if (!PyArg_ParseTuple(args, "y*:frombytes", &buffer)) + return NULL; + else + return frombytes(self, &buffer); +} + +PyDoc_STRVAR(frombytes_doc, +"frombytes(bytestring)\n\ +\n\ +Appends items from the string, interpreting it as an array of machine\n\ values, as if it had been read from a file using the fromfile() method)."); static PyObject * -array_tostring(arrayobject *self, PyObject *unused) +array_tobytes(arrayobject *self, PyObject *unused) { if (Py_SIZE(self) <= PY_SSIZE_T_MAX / self->ob_descr->itemsize) { return PyBytes_FromStringAndSize(self->ob_item, @@ -1379,13 +1418,30 @@ } } -PyDoc_STRVAR(tostring_doc, -"tostring() -> string\n\ +PyDoc_STRVAR(tobytes_doc, +"tobytes() -> bytes\n\ \n\ -Convert the array to an array of machine values and return the string\n\ +Convert the array to an array of machine values and return the bytes\n\ representation."); +static PyObject * +array_tostring(arrayobject *self, PyObject *unused) +{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "tostring() is deprecated. Use tobytes() instead.", 2) != 0) + return NULL; + return array_tobytes(self, unused); +} + +PyDoc_STRVAR(tostring_doc, +"tostring() -> bytes\n\ +\n\ +Convert the array to an array of machine values and return the bytes\n\ +representation.\n\ +\n\ +This method is deprecated. Use tobytes instead."); + static PyObject * array_fromunicode(arrayobject *self, PyObject *args) @@ -1420,7 +1476,7 @@ \n\ Extends this array with data from the unicode string ustr.\n\ The array must be a unicode type array; otherwise a ValueError\n\ -is raised. Use array.fromstring(ustr.decode(...)) to\n\ +is raised. Use array.frombytes(ustr.decode(...)) to\n\ append Unicode data to an array of some other type."); @@ -1927,7 +1983,7 @@ return result; } - array_str = array_tostring(array, NULL); + array_str = array_tobytes(array, NULL); if (array_str == NULL) { Py_DECREF(dict); return NULL; @@ -1983,6 +2039,8 @@ fromlist_doc}, {"fromstring", (PyCFunction)array_fromstring, METH_VARARGS, fromstring_doc}, + {"frombytes", (PyCFunction)array_frombytes, METH_VARARGS, + frombytes_doc}, {"fromunicode", (PyCFunction)array_fromunicode, METH_VARARGS, fromunicode_doc}, {"index", (PyCFunction)array_index, METH_O, @@ -2005,6 +2063,8 @@ tolist_doc}, {"tostring", (PyCFunction)array_tostring, METH_NOARGS, tostring_doc}, + {"tobytes", (PyCFunction)array_tobytes, METH_NOARGS, + tobytes_doc}, {"tounicode", (PyCFunction)array_tounicode, METH_NOARGS, tounicode_doc}, {NULL, NULL} /* sentinel */ @@ -2386,7 +2446,7 @@ Py_DECREF(a); return NULL; } - v = array_fromstring((arrayobject *)a, + v = array_frombytes((arrayobject *)a, t_initial); Py_DECREF(t_initial); if (v == NULL) { From python-checkins at python.org Wed Sep 1 22:30:18 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 22:30:18 +0200 (CEST) Subject: [Python-checkins] r84404 - python/branches/release31-maint Message-ID: <20100901203018.98E57EEB21@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 22:30:18 2010 New Revision: 84404 Log: Blocked revisions 84403 via svnmerge ........ r84403 | antoine.pitrou | 2010-09-01 22:29:34 +0200 (mer., 01 sept. 2010) | 6 lines Issue #8990: array.fromstring() and array.tostring() get renamed to frombytes() and tobytes(), respectively, to avoid confusion. Furthermore, array.frombytes(), array.extend() as well as the array.array() constructor now accept bytearray objects. Patch by Thomas Jollans. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Wed Sep 1 22:31:59 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 22:31:59 +0200 (CEST) Subject: [Python-checkins] r84405 - python/branches/py3k/Doc/tools/sphinxext/indexcontent.html Message-ID: <20100901203159.EA01DEEB28@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 22:31:59 2010 New Revision: 84405 Log: 'Using Python' now called 'Python Setup' Modified: python/branches/py3k/Doc/tools/sphinxext/indexcontent.html Modified: python/branches/py3k/Doc/tools/sphinxext/indexcontent.html ============================================================================== --- python/branches/py3k/Doc/tools/sphinxext/indexcontent.html (original) +++ python/branches/py3k/Doc/tools/sphinxext/indexcontent.html Wed Sep 1 22:31:59 2010 @@ -7,14 +7,14 @@ or all "What's new" documents since 2.0

- + From python-checkins at python.org Wed Sep 1 22:55:42 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 22:55:42 +0200 (CEST) Subject: [Python-checkins] r84406 - python/branches/py3k/Modules/_ssl.c Message-ID: <20100901205542.1F389EEB39@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 22:55:41 2010 New Revision: 84406 Log: Try to fix some buildbot failures on test_ssl Modified: python/branches/py3k/Modules/_ssl.c Modified: python/branches/py3k/Modules/_ssl.c ============================================================================== --- python/branches/py3k/Modules/_ssl.c (original) +++ python/branches/py3k/Modules/_ssl.c Wed Sep 1 22:55:41 2010 @@ -1581,6 +1581,7 @@ int r; errno = 0; + ERR_clear_error(); if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:load_cert_chain", kwlist, &certfile, &keyfile)) From python-checkins at python.org Wed Sep 1 23:02:50 2010 From: python-checkins at python.org (georg.brandl) Date: Wed, 1 Sep 2010 23:02:50 +0200 (CEST) Subject: [Python-checkins] r84407 - python/branches/py3k/Doc/tools/sphinxext/indexcontent.html Message-ID: <20100901210250.6F32DEEB06@mail.python.org> Author: georg.brandl Date: Wed Sep 1 23:02:50 2010 New Revision: 84407 Log: #9677: fix link. Modified: python/branches/py3k/Doc/tools/sphinxext/indexcontent.html Modified: python/branches/py3k/Doc/tools/sphinxext/indexcontent.html ============================================================================== --- python/branches/py3k/Doc/tools/sphinxext/indexcontent.html (original) +++ python/branches/py3k/Doc/tools/sphinxext/indexcontent.html Wed Sep 1 23:02:50 2010 @@ -34,7 +34,7 @@

Indices and tables:

- + @@ -145,11 +145,11 @@ - + From python-checkins at python.org Sat Sep 25 15:30:03 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 25 Sep 2010 15:30:03 +0200 (CEST) Subject: [Python-checkins] r85008 - python/branches/py3k/Doc/library/itertools.rst Message-ID: <20100925133003.3F27DD2B8@mail.python.org> Author: georg.brandl Date: Sat Sep 25 15:30:03 2010 New Revision: 85008 Log: #9944: fix typo. Modified: python/branches/py3k/Doc/library/itertools.rst Modified: python/branches/py3k/Doc/library/itertools.rst ============================================================================== --- python/branches/py3k/Doc/library/itertools.rst (original) +++ python/branches/py3k/Doc/library/itertools.rst Sat Sep 25 15:30:03 2010 @@ -592,7 +592,7 @@ # feed the entire iterator into a zero-length deque collections.deque(iterator, maxlen=0) else: - # advance to the emtpy slice starting at position n + # advance to the empty slice starting at position n next(islice(iterator, n, n), None) def nth(iterable, n, default=None): From python-checkins at python.org Sat Sep 25 15:31:21 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 25 Sep 2010 15:31:21 +0200 (CEST) Subject: [Python-checkins] r85009 - python/branches/release27-maint/Doc/library/compiler.rst Message-ID: <20100925133121.B5DA6D2B8@mail.python.org> Author: georg.brandl Date: Sat Sep 25 15:31:21 2010 New Revision: 85009 Log: Fix typo. Modified: python/branches/release27-maint/Doc/library/compiler.rst Modified: python/branches/release27-maint/Doc/library/compiler.rst ============================================================================== --- python/branches/release27-maint/Doc/library/compiler.rst (original) +++ python/branches/release27-maint/Doc/library/compiler.rst Sat Sep 25 15:31:21 2010 @@ -18,7 +18,7 @@ The :mod:`compiler` package is a Python source to bytecode translator written in Python. It uses the built-in parser and standard :mod:`parser` module to -generated a concrete syntax tree. This tree is used to generate an abstract +generate a concrete syntax tree. This tree is used to generate an abstract syntax tree (AST) and then Python bytecode. The full functionality of the package duplicates the built-in compiler provided From python-checkins at python.org Sat Sep 25 15:46:23 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 25 Sep 2010 15:46:23 +0200 (CEST) Subject: [Python-checkins] r85010 - python/branches/release27-maint/Doc/reference/lexical_analysis.rst Message-ID: <20100925134623.AAFE5C37F@mail.python.org> Author: georg.brandl Date: Sat Sep 25 15:46:23 2010 New Revision: 85010 Log: #1865: document syntax for bytes literals. Modified: python/branches/release27-maint/Doc/reference/lexical_analysis.rst Modified: python/branches/release27-maint/Doc/reference/lexical_analysis.rst ============================================================================== --- python/branches/release27-maint/Doc/reference/lexical_analysis.rst (original) +++ python/branches/release27-maint/Doc/reference/lexical_analysis.rst Sat Sep 25 15:46:23 2010 @@ -426,6 +426,7 @@ .. productionlist:: stringliteral: [`stringprefix`](`shortstring` | `longstring`) stringprefix: "r" | "u" | "ur" | "R" | "U" | "UR" | "Ur" | "uR" + : | "b" | "B" | "br" | "Br" | "bR" | "BR" shortstring: "'" `shortstringitem`* "'" | '"' `shortstringitem`* '"' longstring: "'''" `longstringitem`* "'''" : | '"""' `longstringitem`* '"""' @@ -458,8 +459,10 @@ ``'u'`` or ``'U'`` makes the string a Unicode string. Unicode strings use the Unicode character set as defined by the Unicode Consortium and ISO 10646. Some additional escape sequences, described below, are available in Unicode strings. -The two prefix characters may be combined; in this case, ``'u'`` must appear -before ``'r'``. +A prefix of ``'b'`` or ``'B'`` is ignored in Python 2; it indicates that the +literal should become a bytes literal in Python 3 (e.g. when code is +automatically converted with 2to3). A ``'u'`` or ``'b'`` prefix may be followed +by an ``'r'`` prefix. In triple-quoted strings, unescaped newlines and quotes are allowed (and are retained), except that three unescaped quotes in a row terminate the string. (A From python-checkins at python.org Sat Sep 25 18:56:42 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 25 Sep 2010 18:56:42 +0200 (CEST) Subject: [Python-checkins] r85011 - python/branches/py3k/Doc/library/stdtypes.rst Message-ID: <20100925165642.41AAEEE98E@mail.python.org> Author: georg.brandl Date: Sat Sep 25 18:56:36 2010 New Revision: 85011 Log: #9562: slight clarification. Modified: python/branches/py3k/Doc/library/stdtypes.rst Modified: python/branches/py3k/Doc/library/stdtypes.rst ============================================================================== --- python/branches/py3k/Doc/library/stdtypes.rst (original) +++ python/branches/py3k/Doc/library/stdtypes.rst Sat Sep 25 18:56:36 2010 @@ -2157,7 +2157,7 @@ existing keys. Return ``None``. :meth:`update` accepts either another dictionary object or an iterable of - key/value pairs (as a tuple or other iterable of length two). If keyword + key/value pairs (as tuples or other iterables of length two). If keyword arguments are specified, the dictionary is then updated with those key/value pairs: ``d.update(red=1, blue=2)``. From python-checkins at python.org Sat Sep 25 19:42:36 2010 From: python-checkins at python.org (vinay.sajip) Date: Sat, 25 Sep 2010 19:42:36 +0200 (CEST) Subject: [Python-checkins] r85012 - in python/branches: py3k/Lib/logging/__init__.py py3k/Misc/NEWS release27-maint/Lib/logging/__init__.py release27-maint/Misc/NEWS Message-ID: <20100925174236.7AE49F4F2@mail.python.org> Author: vinay.sajip Date: Sat Sep 25 19:42:36 2010 New Revision: 85012 Log: Issue #9945: logging: Fixed locking bugs in addHandler/removeHandler. Modified: python/branches/py3k/Lib/logging/__init__.py python/branches/py3k/Misc/NEWS python/branches/release27-maint/Lib/logging/__init__.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/py3k/Lib/logging/__init__.py ============================================================================== --- python/branches/py3k/Lib/logging/__init__.py (original) +++ python/branches/py3k/Lib/logging/__init__.py Sat Sep 25 19:42:36 2010 @@ -1226,19 +1226,23 @@ """ Add the specified handler to this logger. """ - if not (hdlr in self.handlers): - self.handlers.append(hdlr) + _acquireLock() + try: + if not (hdlr in self.handlers): + self.handlers.append(hdlr) + finally: + _releaseLock() def removeHandler(self, hdlr): """ Remove the specified handler from this logger. """ - if hdlr in self.handlers: - hdlr.acquire() - try: + _acquireLock() + try: + if hdlr in self.handlers: self.handlers.remove(hdlr) - finally: - hdlr.release() + finally: + _releaseLock() def hasHandlers(self): """ Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 25 19:42:36 2010 @@ -68,6 +68,8 @@ Library ------- +- Issue #9945: logging: Fixed locking bugs in addHandler/removeHandler. + - Issue #9936: Fixed executable lines' search in the trace module. - Issue #9790: Rework imports necessary for samefile and sameopenfile Modified: python/branches/release27-maint/Lib/logging/__init__.py ============================================================================== --- python/branches/release27-maint/Lib/logging/__init__.py (original) +++ python/branches/release27-maint/Lib/logging/__init__.py Sat Sep 25 19:42:36 2010 @@ -1263,20 +1263,23 @@ """ Add the specified handler to this logger. """ - if not (hdlr in self.handlers): - self.handlers.append(hdlr) + _acquireLock() + try: + if not (hdlr in self.handlers): + self.handlers.append(hdlr) + finally: + _releaseLock() def removeHandler(self, hdlr): """ Remove the specified handler from this logger. """ - if hdlr in self.handlers: - #hdlr.close() - hdlr.acquire() - try: + _acquireLock() + try: + if hdlr in self.handlers: self.handlers.remove(hdlr) - finally: - hdlr.release() + finally: + _releaseLock() def callHandlers(self, record): """ Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sat Sep 25 19:42:36 2010 @@ -43,6 +43,8 @@ Library ------- +- Issue #9945: logging: Fixed locking bugs in addHandler/removeHandler. + - Issue #9936: Fixed executable lines' search in the trace module. - Issue #9928: Properly initialize the types exported by the bz2 module. From python-checkins at python.org Sat Sep 25 19:48:26 2010 From: python-checkins at python.org (vinay.sajip) Date: Sat, 25 Sep 2010 19:48:26 +0200 (CEST) Subject: [Python-checkins] r85013 - in python/branches: py3k/Lib/logging/config.py py3k/Misc/NEWS release27-maint/Lib/logging/config.py release27-maint/Misc/NEWS Message-ID: <20100925174826.223D5EE984@mail.python.org> Author: vinay.sajip Date: Sat Sep 25 19:48:25 2010 New Revision: 85013 Log: Issue #9947: logging: Fixed locking bug in stopListening. Modified: python/branches/py3k/Lib/logging/config.py python/branches/py3k/Misc/NEWS python/branches/release27-maint/Lib/logging/config.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/py3k/Lib/logging/config.py ============================================================================== --- python/branches/py3k/Lib/logging/config.py (original) +++ python/branches/py3k/Lib/logging/config.py Sat Sep 25 19:48:25 2010 @@ -917,8 +917,10 @@ Stop the listening server which was created with a call to listen(). """ global _listener - if _listener: - logging._acquireLock() - _listener.abort = 1 - _listener = None + logging._acquireLock() + try: + if _listener: + _listener.abort = 1 + _listener = None + finally: logging._releaseLock() Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 25 19:48:25 2010 @@ -68,6 +68,8 @@ Library ------- +- Issue #9947: logging: Fixed locking bug in stopListening. + - Issue #9945: logging: Fixed locking bugs in addHandler/removeHandler. - Issue #9936: Fixed executable lines' search in the trace module. Modified: python/branches/release27-maint/Lib/logging/config.py ============================================================================== --- python/branches/release27-maint/Lib/logging/config.py (original) +++ python/branches/release27-maint/Lib/logging/config.py Sat Sep 25 19:48:25 2010 @@ -895,8 +895,10 @@ Stop the listening server which was created with a call to listen(). """ global _listener - if _listener: - logging._acquireLock() - _listener.abort = 1 - _listener = None + logging._acquireLock() + try: + if _listener: + _listener.abort = 1 + _listener = None + finally: logging._releaseLock() Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sat Sep 25 19:48:25 2010 @@ -43,6 +43,8 @@ Library ------- +- Issue #9947: logging: Fixed locking bug in stopListening. + - Issue #9945: logging: Fixed locking bugs in addHandler/removeHandler. - Issue #9936: Fixed executable lines' search in the trace module. From python-checkins at python.org Sat Sep 25 21:44:56 2010 From: python-checkins at python.org (phillip.eby) Date: Sat, 25 Sep 2010 21:44:56 +0200 (CEST) Subject: [Python-checkins] r85014 - peps/trunk/pep-0333.txt Message-ID: <20100925194456.05ABAEE995@mail.python.org> Author: phillip.eby Date: Sat Sep 25 21:44:55 2010 New Revision: 85014 Log: WSGI is now Python 3-friendly. This does not cover the other planned addenda/errata, and it may need more work even on these bits, but it is now begun. (Many thanks to Graham and Ian.) Modified: peps/trunk/pep-0333.txt Modified: peps/trunk/pep-0333.txt ============================================================================== --- peps/trunk/pep-0333.txt (original) +++ peps/trunk/pep-0333.txt Sat Sep 25 21:44:55 2010 @@ -142,6 +142,51 @@ introspected upon. +A Note On String Types +---------------------- + +In general, HTTP deals with bytes, which means that this specification +is mostly about handling bytes. + +However, the content of those bytes often has some kind of textual +interpretation, and in Python, strings are the most convenient way +to handle text. + +But in many Python versions and implementations, strings are Unicode, +rather than bytes. This requires a careful balance between a usable +API and correct translations between bytes and text in the context of +HTTP... especially to support porting code between Python +implementations with different ``str`` types. + +WSGI therefore defines two kinds of "string": + +* "Native" strings (which are always implemented using the type + named ``str``) that are used for request/response headers and + metadata + +* "Bytestrings" (which are implemented using the ``bytes`` type + in Python 3, and ``str`` elsewhere), that are used for the bodies + of requests and responses (e.g. POST/PUT input data and HTML page + outputs). + +Do not be confused however: even if Python's ``str`` type is actually +Unicode "under the hood", the *content* of native strings must +still be translatable to bytes via the Latin-1 encoding! (See +the section on `Unicode Issues`_ later in this document for more +details.) + +In short: where you see the word "string" in this document, it refers +to a "native" string, i.e., an object of type ``str``, whether it is +internally implemented as bytes or unicode. Where you see references +to "bytestring", this should be read as "an object of type ``bytes`` +under Python 3, or type ``str`` under Python 2". + +And so, even though HTTP is in some sense "really just bytes", there +are many API conveniences to be had by using whatever Python's +default ``str`` type is. + + + The Application/Framework Side ------------------------------ @@ -164,13 +209,15 @@ Here are two example application objects; one is a function, and the other is a class:: + # this would need to be a byte string in Python 3: + HELLO_WORLD = "Hello world!\n" + def simple_app(environ, start_response): """Simplest possible application object""" status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) - return ['Hello world!\n'] - + return [HELLO_WORLD] class AppClass: """Produce the same output, but using a class @@ -195,7 +242,7 @@ status = '200 OK' response_headers = [('Content-type', 'text/plain')] self.start(status, response_headers) - yield "Hello world!\n" + yield HELLO_WORLD The Server/Gateway Side @@ -243,7 +290,7 @@ sys.stdout.write('%s: %s\r\n' % header) sys.stdout.write('\r\n') - sys.stdout.write(data) + sys.stdout.write(data) # TODO: this needs to be binary on Py3 sys.stdout.flush() def start_response(status, response_headers, exc_info=None): @@ -326,7 +373,7 @@ """Transform iterated output to piglatin, if it's okay to do so Note that the "okayness" can change until the application yields - its first non-empty string, so 'transform_ok' has to be a mutable + its first non-empty bytestring, so 'transform_ok' has to be a mutable truth value. """ @@ -341,7 +388,7 @@ def next(self): if self.transform_ok: - return piglatin(self._next()) + return piglatin(self._next()) # call must be byte-safe on Py3 else: return self._next() @@ -376,7 +423,7 @@ if transform_ok: def write_latin(data): - write(piglatin(data)) + write(piglatin(data)) # call must be byte-safe on Py3 return write_latin else: return write @@ -426,7 +473,7 @@ attempting to display an error message to the browser. The ``start_response`` callable must return a ``write(body_data)`` -callable that takes one positional parameter: a string to be written +callable that takes one positional parameter: a bytestring to be written as part of the HTTP response body. (Note: the ``write()`` callable is provided only to support certain existing frameworks' imperative output APIs; it should not be used by new applications or frameworks if it @@ -434,24 +481,24 @@ details.) When called by the server, the application object must return an -iterable yielding zero or more strings. This can be accomplished in a -variety of ways, such as by returning a list of strings, or by the -application being a generator function that yields strings, or +iterable yielding zero or more bytestrings. This can be accomplished in a +variety of ways, such as by returning a list of bytestrings, or by the +application being a generator function that yields bytestrings, or by the application being a class whose instances are iterable. Regardless of how it is accomplished, the application object must -always return an iterable yielding zero or more strings. +always return an iterable yielding zero or more bytestrings. -The server or gateway must transmit the yielded strings to the client -in an unbuffered fashion, completing the transmission of each string +The server or gateway must transmit the yielded bytestrings to the client +in an unbuffered fashion, completing the transmission of each bytestring before requesting another one. (In other words, applications **should** perform their own buffering. See the `Buffering and Streaming`_ section below for more on how application output must be handled.) -The server or gateway should treat the yielded strings as binary byte +The server or gateway should treat the yielded bytestrings as binary byte sequences: in particular, it should ensure that line endings are not altered. The application is responsible for ensuring that the -string(s) to be written are in a format suitable for the client. (The +bytestring(s) to be written are in a format suitable for the client. (The server or gateway **may** apply HTTP transfer encodings, or perform other transformations for the purpose of implementing HTTP features such as byte-range transmission. See `Other HTTP Features`_, below, @@ -472,7 +519,7 @@ generator support, and other common iterables with ``close()`` methods. (Note: the application **must** invoke the ``start_response()`` -callable before the iterable yields its first body string, so that the +callable before the iterable yields its first body bytestring, so that the server can send the headers before any body content. However, this invocation **may** be performed by the iterable's first iteration, so servers **must not** assume that ``start_response()`` has been called @@ -565,7 +612,7 @@ Note: missing variables (such as ``REMOTE_USER`` when no authentication has occurred) should be left out of the ``environ`` -dictionary. Also note that CGI-defined variables must be strings, +dictionary. Also note that CGI-defined variables must be native strings, if they are present at all. It is a violation of this specification for a CGI variable's value to be of any type other than ``str``. @@ -585,9 +632,9 @@ ``"http"`` or ``"https"``, as appropriate. ``wsgi.input`` An input stream (file-like object) from which - the HTTP request body can be read. (The server - or gateway may perform reads on-demand as - requested by the application, or it may pre- + the HTTP request body bytes can be read. (The + server or gateway may perform reads on-demand + as requested by the application, or it may pre- read the client's request body and buffer it in-memory or on disk, or use any other technique for providing such an input stream, @@ -602,6 +649,12 @@ ending, and assume that it will be converted to the correct line ending by the server/gateway. + (On platforms where the ``str`` type is unicode, + the error stream **should** accept and log + arbitary unicode without raising an error; it + is allowed, however, to substitute characters + that cannot be rendered in the stream's encoding.) + For many servers, ``wsgi.errors`` will be the server's main error log. Alternatively, this may be ``sys.stderr``, or a log file of some @@ -745,7 +798,7 @@ The ``start_response`` callable **must not** actually transmit the response headers. Instead, it must store them for the server or gateway to transmit **only** after the first iteration of the -application return value that yields a non-empty string, or upon +application return value that yields a non-empty bytestring, or upon the application's first invocation of the ``write()`` callable. In other words, response headers must not be sent until there is actual body data available, or until the application's returned iterable is @@ -820,12 +873,12 @@ avoid the need to close the client connection. If the application does *not* call the ``write()`` callable, and returns an iterable whose ``len()`` is 1, then the server can automatically determine -``Content-Length`` by taking the length of the first string yielded +``Content-Length`` by taking the length of the first bytestring yielded by the iterable. And, if the server and client both support HTTP/1.1 "chunked encoding" [3]_, then the server **may** use chunked encoding to send -a chunk for each ``write()`` call or string yielded by the iterable, +a chunk for each ``write()`` call or bytestring yielded by the iterable, thus generating a ``Content-Length`` header for each chunk. This allows the server to keep the client connection alive, if it wishes to do so. Note that the server **must** comply fully with RFC 2616 @@ -850,7 +903,7 @@ The corresponding approach in WSGI is for the application to simply return a single-element iterable (such as a list) containing the -response body as a single string. This is the recommended approach +response body as a single bytestring. This is the recommended approach for the vast majority of application functions, that render HTML pages whose text easily fits in memory. @@ -899,12 +952,12 @@ middleware components **must not** block iteration waiting for multiple values from an application iterable. If the middleware needs to accumulate more data from the application before it can -produce any output, it **must** yield an empty string. +produce any output, it **must** yield an empty bytestring. To put this requirement another way, a middleware component **must yield at least one value** each time its underlying application yields a value. If the middleware cannot yield any other value, -it must yield an empty string. +it must yield an empty bytestring. This requirement ensures that asynchronous applications and servers can conspire to reduce the number of threads that are required @@ -946,22 +999,22 @@ potentially providing better throughput for the server as a whole. The ``write()`` callable is returned by the ``start_response()`` -callable, and it accepts a single parameter: a string to be +callable, and it accepts a single parameter: a bytestring to be written as part of the HTTP response body, that is treated exactly as though it had been yielded by the output iterable. In other words, before ``write()`` returns, it must guarantee that the -passed-in string was either completely sent to the client, or +passed-in bytestring was either completely sent to the client, or that it is buffered for transmission while the application proceeds onward. An application **must** return an iterable object, even if it uses ``write()`` to produce all or part of its response body. The returned iterable **may** be empty (i.e. yield no non-empty -strings), but if it *does* yield non-empty strings, that output +bytestrings), but if it *does* yield non-empty bytestrings, that output must be treated normally by the server or gateway (i.e., it must be sent or queued immediately). Applications **must not** invoke ``write()`` from within their return iterable, and therefore any -strings yielded by the iterable are transmitted after all strings +bytestrings yielded by the iterable are transmitted after all bytestrings passed to ``write()`` have been sent to the client. @@ -970,9 +1023,9 @@ HTTP does not directly support Unicode, and neither does this interface. All encoding/decoding must be handled by the application; -all strings passed to or from the server must be standard Python byte -strings, not Unicode objects. The result of using a Unicode object -where a string object is required, is undefined. +all strings passed to or from the server must be of type ``str`` or +``bytes``, never ``unicode``. The result of using a ``unicode`` +object where a string object is required, is undefined. Note also that strings passed to ``start_response()`` as a status or as response headers **must** follow RFC 2616 with respect to encoding. @@ -980,7 +1033,7 @@ MIME encoding. On Python platforms where the ``str`` or ``StringType`` type is in -fact Unicode-based (e.g. Jython, IronPython, Python 3000, etc.), all +fact Unicode-based (e.g. Jython, IronPython, Python 3, etc.), all "strings" referred to in this specification must contain only code points representable in ISO-8859-1 encoding (``\u0000`` through ``\u00FF``, inclusive). It is a fatal error for an application to @@ -988,12 +1041,18 @@ Similarly, servers and gateways **must not** supply strings to an application containing any other Unicode characters. -Again, all strings referred to in this specification **must** be -of type ``str`` or ``StringType``, and **must not** be of type -``unicode`` or ``UnicodeType``. And, even if a given platform allows -for more than 8 bits per character in ``str``/``StringType`` objects, -only the lower 8 bits may be used, for any value referred to in -this specification as a "string". +Again, all objects referred to in this specification as "strings" +**must** be of type ``str`` or ``StringType``, and **must not** be +of type ``unicode`` or ``UnicodeType``. And, even if a given platform +allows for more than 8 bits per character in ``str``/``StringType`` +objects, only the lower 8 bits may be used, for any value referred +to in this specification as a "string". + +For values referred to in this specification as "bytestrings" +(i.e., values read from ``wsgi.input``, passed to ``write()`` +or yielded by the application), the value **must** be of type +``bytes`` under Python 3, and ``str`` in earlier versions of +Python. Error Handling @@ -1448,7 +1507,7 @@ ``environ`` dictionary. This is the recommended approach for offering any such value-added services. -2. Why can you call ``write()`` *and* yield strings/return an +2. Why can you call ``write()`` *and* yield bytestrings/return an iterable? Shouldn't we pick just one way? If we supported only the iteration approach, then current From python-checkins at python.org Sun Sep 26 00:12:00 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 26 Sep 2010 00:12:00 +0200 (CEST) Subject: [Python-checkins] r85015 - python/branches/py3k/Doc/library/os.rst Message-ID: <20100925221200.7992DEE9A9@mail.python.org> Author: antoine.pitrou Date: Sun Sep 26 00:12:00 2010 New Revision: 85015 Log: Fix typo. Modified: python/branches/py3k/Doc/library/os.rst Modified: python/branches/py3k/Doc/library/os.rst ============================================================================== --- python/branches/py3k/Doc/library/os.rst (original) +++ python/branches/py3k/Doc/library/os.rst Sun Sep 26 00:12:00 2010 @@ -162,7 +162,7 @@ error handler if the filesystem encoding is ``'mbcs'`` (which is the default encoding). - :func:`fsdencode` is the reverse function. + :func:`fsdecode` is the reverse function. .. versionadded:: 3.2 From solipsis at pitrou.net Sun Sep 26 04:46:21 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 26 Sep 2010 04:46:21 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r85015): sum=0 Message-ID: py3k results for svn r85015 (hg cset e98f797b5ccd) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/refloguCLkOI', '-x'] From python-checkins at python.org Sun Sep 26 12:37:12 2010 From: python-checkins at python.org (mark.dickinson) Date: Sun, 26 Sep 2010 12:37:12 +0200 (CEST) Subject: [Python-checkins] r85016 - in python/branches/release27-maint: Lib/test/test_class.py Lib/test/test_long.py Misc/NEWS Objects/abstract.c Message-ID: <20100926103712.84839EEA21@mail.python.org> Author: mark.dickinson Date: Sun Sep 26 12:37:12 2010 New Revision: 85016 Log: Issue #9869: Make long() and PyNumber_Long return something of type long for a class whose __long__ method returns a plain int. This fixes an interpreter crash (due to long_subtype_new assuming PyNumber_Long returns a long) when initializing an instance of a long subclass from an object whose __long__ method returns a plain int. Modified: python/branches/release27-maint/Lib/test/test_class.py python/branches/release27-maint/Lib/test/test_long.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Objects/abstract.c Modified: python/branches/release27-maint/Lib/test/test_class.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_class.py (original) +++ python/branches/release27-maint/Lib/test/test_class.py Sun Sep 26 12:37:12 2010 @@ -513,7 +513,7 @@ callLst[:] = [] as_long = long(mixIntAndLong) - self.assertEquals(type(as_long), int) + self.assertEquals(type(as_long), long) self.assertEquals(as_long, 64) self.assertCallStack([('__long__', (mixIntAndLong,))]) Modified: python/branches/release27-maint/Lib/test/test_long.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_long.py (original) +++ python/branches/release27-maint/Lib/test/test_long.py Sun Sep 26 12:37:12 2010 @@ -601,6 +601,22 @@ slicemin, slicemax = X()[-2L**100:2L**100] self.assertEqual(X()[slicemin:slicemax], (slicemin, slicemax)) + def test_issue9869(self): + # Issue 9869: Interpreter crash when initializing an instance + # of a long subclass from an object whose __long__ method returns + # a plain int. + class BadLong(object): + def __long__(self): + return 1000000 + + class MyLong(long): + pass + + x = MyLong(BadLong()) + self.assertIsInstance(x, long) + self.assertEqual(x, 1000000) + + # ----------------------------------- tests of auto int->long conversion def test_auto_overflow(self): Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Sep 26 12:37:12 2010 @@ -12,6 +12,11 @@ Core and Builtins ----------------- +- Issue #9869: Make long() and PyNumber_Long return something of type + long for a class whose __long__ method returns a plain int. This + fixes an interpreter crash when initializing an instance of a long + subclass from an object whose __long__ method returns a plain int. + - Issue #9797: pystate.c wrongly assumed that zero couldn't be a valid thread-local storage key. Modified: python/branches/release27-maint/Objects/abstract.c ============================================================================== --- python/branches/release27-maint/Objects/abstract.c (original) +++ python/branches/release27-maint/Objects/abstract.c Sun Sep 26 12:37:12 2010 @@ -1713,7 +1713,14 @@ if (m && m->nb_long) { /* This should include subclasses of long */ /* Classic classes always take this branch. */ PyObject *res = m->nb_long(o); - if (res && (!PyInt_Check(res) && !PyLong_Check(res))) { + if (res == NULL) + return NULL; + if (PyInt_Check(res)) { + long value = PyInt_AS_LONG(res); + Py_DECREF(res); + return PyLong_FromLong(value); + } + else if (!PyLong_Check(res)) { PyErr_Format(PyExc_TypeError, "__long__ returned non-long (type %.200s)", res->ob_type->tp_name); From python-checkins at python.org Sun Sep 26 13:04:10 2010 From: python-checkins at python.org (vinay.sajip) Date: Sun, 26 Sep 2010 13:04:10 +0200 (CEST) Subject: [Python-checkins] r85017 - in python/branches: py3k/Lib/logging/__init__.py release27-maint/Lib/logging/__init__.py Message-ID: <20100926110410.B1592EE995@mail.python.org> Author: vinay.sajip Date: Sun Sep 26 13:04:10 2010 New Revision: 85017 Log: logging: NullHandler optimisation. Modified: python/branches/py3k/Lib/logging/__init__.py python/branches/release27-maint/Lib/logging/__init__.py Modified: python/branches/py3k/Lib/logging/__init__.py ============================================================================== --- python/branches/py3k/Lib/logging/__init__.py (original) +++ python/branches/py3k/Lib/logging/__init__.py Sun Sep 26 13:04:10 2010 @@ -1652,9 +1652,15 @@ a NullHandler and add it to the top-level logger of the library module or package. """ + def handle(self, record): + pass + def emit(self, record): pass + def createLock(self): + self.lock = None + # Warnings integration _warnings_showwarning = None Modified: python/branches/release27-maint/Lib/logging/__init__.py ============================================================================== --- python/branches/release27-maint/Lib/logging/__init__.py (original) +++ python/branches/release27-maint/Lib/logging/__init__.py Sun Sep 26 13:04:10 2010 @@ -1656,9 +1656,15 @@ a NullHandler and add it to the top-level logger of the library module or package. """ + def handle(self, record): + pass + def emit(self, record): pass + def createLock(self): + self.lock = None + # Warnings integration _warnings_showwarning = None From python-checkins at python.org Sun Sep 26 18:36:34 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 26 Sep 2010 18:36:34 +0200 (CEST) Subject: [Python-checkins] r85018 - python/branches/py3k/Lib/tkinter/test/test_ttk/test_widgets.py Message-ID: <20100926163634.0B914EE9E1@mail.python.org> Author: antoine.pitrou Date: Sun Sep 26 18:36:33 2010 New Revision: 85018 Log: Issue #8445: try to fix some buildbot failures on test_ttk_guionly. Patch by Guilherme. Modified: python/branches/py3k/Lib/tkinter/test/test_ttk/test_widgets.py Modified: python/branches/py3k/Lib/tkinter/test/test_ttk/test_widgets.py ============================================================================== --- python/branches/py3k/Lib/tkinter/test/test_ttk/test_widgets.py (original) +++ python/branches/py3k/Lib/tkinter/test/test_ttk/test_widgets.py Sun Sep 26 18:36:33 2010 @@ -13,7 +13,7 @@ def setUp(self): support.root_deiconify() - self.widget = ttk.Button() + self.widget = ttk.Button(width=0, text="Text") self.widget.pack() self.widget.wait_visibility() @@ -24,7 +24,10 @@ def test_identify(self): self.widget.update_idletasks() - self.assertEqual(self.widget.identify(5, 5), "label") + self.assertEqual(self.widget.identify( + int(self.widget.winfo_width() / 2), + int(self.widget.winfo_height() / 2) + ), "label") self.assertEqual(self.widget.identify(-1, -1), "") self.assertRaises(tkinter.TclError, self.widget.identify, None, 5) @@ -530,7 +533,7 @@ def setUp(self): support.root_deiconify() - self.nb = ttk.Notebook() + self.nb = ttk.Notebook(padding=0) self.child1 = ttk.Label() self.child2 = ttk.Label() self.nb.add(self.child1, text='a') @@ -717,6 +720,7 @@ self.nb.tab(self.child1, text='a', underline=0) self.nb.enable_traversal() self.nb.focus_force() + support.simulate_mouse_click(self.nb, 5, 5) self.nb.event_generate('') self.assertEqual(self.nb.select(), str(self.child1)) @@ -725,7 +729,7 @@ def setUp(self): support.root_deiconify() - self.tv = ttk.Treeview() + self.tv = ttk.Treeview(padding=0) def tearDown(self): self.tv.destroy() From python-checkins at python.org Mon Sep 27 01:36:24 2010 From: python-checkins at python.org (phillip.eby) Date: Mon, 27 Sep 2010 01:36:24 +0200 (CEST) Subject: [Python-checkins] r85019 - peps/trunk/pep-3333.txt Message-ID: <20100926233624.97BE9EEAF9@mail.python.org> Author: phillip.eby Date: Mon Sep 27 01:36:24 2010 New Revision: 85019 Log: Clone and update PEP 3333 Added: peps/trunk/pep-3333.txt - copied, changed from r85014, /peps/trunk/pep-0333.txt Copied: peps/trunk/pep-3333.txt (from r85014, /peps/trunk/pep-0333.txt) ============================================================================== --- /peps/trunk/pep-0333.txt (original) +++ peps/trunk/pep-3333.txt Mon Sep 27 01:36:24 2010 @@ -1,14 +1,36 @@ -PEP: 333 -Title: Python Web Server Gateway Interface v1.0 +PEP: 3333 +Title: Python Web Server Gateway Interface v1.0.1 Version: $Revision$ Last-Modified: $Date$ -Author: Phillip J. Eby +Author: P.J. Eby Discussions-To: Python Web-SIG Status: Draft Type: Informational Content-Type: text/x-rst -Created: 07-Dec-2003 -Post-History: 07-Dec-2003, 08-Aug-2004, 20-Aug-2004, 27-Aug-2004 +Created: 26-Sep-2010 +Post-History: 26-Sep-2010 + + +Preface for Readers of PEP 333 +============================== + +This is an updated version of PEP 333, modified slightly to improve +usability under Python 3, and to incorporate several long-standing +de-facto amendments to the WSGI protocol. + +While for procedural reasons [6]_, this must be a distinct PEP, no +changes were made that invalidate previously-compliant servers or +applications under Python 2.x. If your 2.x application or server +is compliant to PEP 333, it is also compliant with PEP 3333. + +Under Python 3, however, your app or server must also follow the +rules outlined in the sections below titled, `A Note On String +Types`_, and `Unicode Issues`_. + +For detailed, line-by-line diffs between this document and PEP 333, +you may view its `SVN revision history`_, from revision 85014 forward. + +.. _SVN revision history: http://svn.python.org/view/peps/trunk/pep-3333.txt?view=log Abstract @@ -1697,6 +1719,8 @@ .. [5] mod_ssl Reference, "Environment Variables" (http://www.modssl.org/docs/2.8/ssl_reference.html#ToC25) +.. [6] Procedural issues regarding modifications to PEP 333 + (http://mail.python.org/pipermail/python-dev/2010-September/104114.html) Copyright ========= From python-checkins at python.org Mon Sep 27 01:37:17 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Mon, 27 Sep 2010 01:37:17 +0200 (CEST) Subject: [Python-checkins] r85020 - in python/branches/release27-maint: Lib/test/test_trace.py Message-ID: <20100926233717.DA147EEAEE@mail.python.org> Author: alexander.belopolsky Date: Mon Sep 27 01:37:17 2010 New Revision: 85020 Log: Merged revisions 85000 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85000 | alexander.belopolsky | 2010-09-24 18:04:22 -0400 (Fri, 24 Sep 2010) | 1 line This should fix buildbot failure introduced by r84994 ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_trace.py Modified: python/branches/release27-maint/Lib/test/test_trace.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_trace.py (original) +++ python/branches/release27-maint/Lib/test/test_trace.py Mon Sep 27 01:37:17 2010 @@ -327,6 +327,8 @@ for line in stdout: lines, cov, module = line.split()[:3] coverage[module] = (int(lines), int(cov[:-1])) + # XXX This is needed to run regrtest.py as a script + modname = trace.fullmodname(sys.modules[modname].__file__) self.assertIn(modname, coverage) self.assertEqual(coverage[modname], (5, 100)) From python-checkins at python.org Mon Sep 27 01:44:39 2010 From: python-checkins at python.org (phillip.eby) Date: Mon, 27 Sep 2010 01:44:39 +0200 (CEST) Subject: [Python-checkins] r85021 - peps/trunk/pep-0333.txt Message-ID: <20100926234439.B0F75EEABE@mail.python.org> Author: phillip.eby Date: Mon Sep 27 01:44:39 2010 New Revision: 85021 Log: Revert Python 3 changes Modified: peps/trunk/pep-0333.txt Modified: peps/trunk/pep-0333.txt ============================================================================== --- peps/trunk/pep-0333.txt (original) +++ peps/trunk/pep-0333.txt Mon Sep 27 01:44:39 2010 @@ -142,51 +142,6 @@ introspected upon. -A Note On String Types ----------------------- - -In general, HTTP deals with bytes, which means that this specification -is mostly about handling bytes. - -However, the content of those bytes often has some kind of textual -interpretation, and in Python, strings are the most convenient way -to handle text. - -But in many Python versions and implementations, strings are Unicode, -rather than bytes. This requires a careful balance between a usable -API and correct translations between bytes and text in the context of -HTTP... especially to support porting code between Python -implementations with different ``str`` types. - -WSGI therefore defines two kinds of "string": - -* "Native" strings (which are always implemented using the type - named ``str``) that are used for request/response headers and - metadata - -* "Bytestrings" (which are implemented using the ``bytes`` type - in Python 3, and ``str`` elsewhere), that are used for the bodies - of requests and responses (e.g. POST/PUT input data and HTML page - outputs). - -Do not be confused however: even if Python's ``str`` type is actually -Unicode "under the hood", the *content* of native strings must -still be translatable to bytes via the Latin-1 encoding! (See -the section on `Unicode Issues`_ later in this document for more -details.) - -In short: where you see the word "string" in this document, it refers -to a "native" string, i.e., an object of type ``str``, whether it is -internally implemented as bytes or unicode. Where you see references -to "bytestring", this should be read as "an object of type ``bytes`` -under Python 3, or type ``str`` under Python 2". - -And so, even though HTTP is in some sense "really just bytes", there -are many API conveniences to be had by using whatever Python's -default ``str`` type is. - - - The Application/Framework Side ------------------------------ @@ -209,15 +164,13 @@ Here are two example application objects; one is a function, and the other is a class:: - # this would need to be a byte string in Python 3: - HELLO_WORLD = "Hello world!\n" - def simple_app(environ, start_response): """Simplest possible application object""" status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) - return [HELLO_WORLD] + return ['Hello world!\n'] + class AppClass: """Produce the same output, but using a class @@ -242,7 +195,7 @@ status = '200 OK' response_headers = [('Content-type', 'text/plain')] self.start(status, response_headers) - yield HELLO_WORLD + yield "Hello world!\n" The Server/Gateway Side @@ -290,7 +243,7 @@ sys.stdout.write('%s: %s\r\n' % header) sys.stdout.write('\r\n') - sys.stdout.write(data) # TODO: this needs to be binary on Py3 + sys.stdout.write(data) sys.stdout.flush() def start_response(status, response_headers, exc_info=None): @@ -373,7 +326,7 @@ """Transform iterated output to piglatin, if it's okay to do so Note that the "okayness" can change until the application yields - its first non-empty bytestring, so 'transform_ok' has to be a mutable + its first non-empty string, so 'transform_ok' has to be a mutable truth value. """ @@ -388,7 +341,7 @@ def next(self): if self.transform_ok: - return piglatin(self._next()) # call must be byte-safe on Py3 + return piglatin(self._next()) else: return self._next() @@ -423,7 +376,7 @@ if transform_ok: def write_latin(data): - write(piglatin(data)) # call must be byte-safe on Py3 + write(piglatin(data)) return write_latin else: return write @@ -473,7 +426,7 @@ attempting to display an error message to the browser. The ``start_response`` callable must return a ``write(body_data)`` -callable that takes one positional parameter: a bytestring to be written +callable that takes one positional parameter: a string to be written as part of the HTTP response body. (Note: the ``write()`` callable is provided only to support certain existing frameworks' imperative output APIs; it should not be used by new applications or frameworks if it @@ -481,24 +434,24 @@ details.) When called by the server, the application object must return an -iterable yielding zero or more bytestrings. This can be accomplished in a -variety of ways, such as by returning a list of bytestrings, or by the -application being a generator function that yields bytestrings, or +iterable yielding zero or more strings. This can be accomplished in a +variety of ways, such as by returning a list of strings, or by the +application being a generator function that yields strings, or by the application being a class whose instances are iterable. Regardless of how it is accomplished, the application object must -always return an iterable yielding zero or more bytestrings. +always return an iterable yielding zero or more strings. -The server or gateway must transmit the yielded bytestrings to the client -in an unbuffered fashion, completing the transmission of each bytestring +The server or gateway must transmit the yielded strings to the client +in an unbuffered fashion, completing the transmission of each string before requesting another one. (In other words, applications **should** perform their own buffering. See the `Buffering and Streaming`_ section below for more on how application output must be handled.) -The server or gateway should treat the yielded bytestrings as binary byte +The server or gateway should treat the yielded strings as binary byte sequences: in particular, it should ensure that line endings are not altered. The application is responsible for ensuring that the -bytestring(s) to be written are in a format suitable for the client. (The +string(s) to be written are in a format suitable for the client. (The server or gateway **may** apply HTTP transfer encodings, or perform other transformations for the purpose of implementing HTTP features such as byte-range transmission. See `Other HTTP Features`_, below, @@ -519,7 +472,7 @@ generator support, and other common iterables with ``close()`` methods. (Note: the application **must** invoke the ``start_response()`` -callable before the iterable yields its first body bytestring, so that the +callable before the iterable yields its first body string, so that the server can send the headers before any body content. However, this invocation **may** be performed by the iterable's first iteration, so servers **must not** assume that ``start_response()`` has been called @@ -612,7 +565,7 @@ Note: missing variables (such as ``REMOTE_USER`` when no authentication has occurred) should be left out of the ``environ`` -dictionary. Also note that CGI-defined variables must be native strings, +dictionary. Also note that CGI-defined variables must be strings, if they are present at all. It is a violation of this specification for a CGI variable's value to be of any type other than ``str``. @@ -632,9 +585,9 @@ ``"http"`` or ``"https"``, as appropriate. ``wsgi.input`` An input stream (file-like object) from which - the HTTP request body bytes can be read. (The - server or gateway may perform reads on-demand - as requested by the application, or it may pre- + the HTTP request body can be read. (The server + or gateway may perform reads on-demand as + requested by the application, or it may pre- read the client's request body and buffer it in-memory or on disk, or use any other technique for providing such an input stream, @@ -649,12 +602,6 @@ ending, and assume that it will be converted to the correct line ending by the server/gateway. - (On platforms where the ``str`` type is unicode, - the error stream **should** accept and log - arbitary unicode without raising an error; it - is allowed, however, to substitute characters - that cannot be rendered in the stream's encoding.) - For many servers, ``wsgi.errors`` will be the server's main error log. Alternatively, this may be ``sys.stderr``, or a log file of some @@ -798,7 +745,7 @@ The ``start_response`` callable **must not** actually transmit the response headers. Instead, it must store them for the server or gateway to transmit **only** after the first iteration of the -application return value that yields a non-empty bytestring, or upon +application return value that yields a non-empty string, or upon the application's first invocation of the ``write()`` callable. In other words, response headers must not be sent until there is actual body data available, or until the application's returned iterable is @@ -873,12 +820,12 @@ avoid the need to close the client connection. If the application does *not* call the ``write()`` callable, and returns an iterable whose ``len()`` is 1, then the server can automatically determine -``Content-Length`` by taking the length of the first bytestring yielded +``Content-Length`` by taking the length of the first string yielded by the iterable. And, if the server and client both support HTTP/1.1 "chunked encoding" [3]_, then the server **may** use chunked encoding to send -a chunk for each ``write()`` call or bytestring yielded by the iterable, +a chunk for each ``write()`` call or string yielded by the iterable, thus generating a ``Content-Length`` header for each chunk. This allows the server to keep the client connection alive, if it wishes to do so. Note that the server **must** comply fully with RFC 2616 @@ -903,7 +850,7 @@ The corresponding approach in WSGI is for the application to simply return a single-element iterable (such as a list) containing the -response body as a single bytestring. This is the recommended approach +response body as a single string. This is the recommended approach for the vast majority of application functions, that render HTML pages whose text easily fits in memory. @@ -952,12 +899,12 @@ middleware components **must not** block iteration waiting for multiple values from an application iterable. If the middleware needs to accumulate more data from the application before it can -produce any output, it **must** yield an empty bytestring. +produce any output, it **must** yield an empty string. To put this requirement another way, a middleware component **must yield at least one value** each time its underlying application yields a value. If the middleware cannot yield any other value, -it must yield an empty bytestring. +it must yield an empty string. This requirement ensures that asynchronous applications and servers can conspire to reduce the number of threads that are required @@ -999,22 +946,22 @@ potentially providing better throughput for the server as a whole. The ``write()`` callable is returned by the ``start_response()`` -callable, and it accepts a single parameter: a bytestring to be +callable, and it accepts a single parameter: a string to be written as part of the HTTP response body, that is treated exactly as though it had been yielded by the output iterable. In other words, before ``write()`` returns, it must guarantee that the -passed-in bytestring was either completely sent to the client, or +passed-in string was either completely sent to the client, or that it is buffered for transmission while the application proceeds onward. An application **must** return an iterable object, even if it uses ``write()`` to produce all or part of its response body. The returned iterable **may** be empty (i.e. yield no non-empty -bytestrings), but if it *does* yield non-empty bytestrings, that output +strings), but if it *does* yield non-empty strings, that output must be treated normally by the server or gateway (i.e., it must be sent or queued immediately). Applications **must not** invoke ``write()`` from within their return iterable, and therefore any -bytestrings yielded by the iterable are transmitted after all bytestrings +strings yielded by the iterable are transmitted after all strings passed to ``write()`` have been sent to the client. @@ -1023,9 +970,9 @@ HTTP does not directly support Unicode, and neither does this interface. All encoding/decoding must be handled by the application; -all strings passed to or from the server must be of type ``str`` or -``bytes``, never ``unicode``. The result of using a ``unicode`` -object where a string object is required, is undefined. +all strings passed to or from the server must be standard Python byte +strings, not Unicode objects. The result of using a Unicode object +where a string object is required, is undefined. Note also that strings passed to ``start_response()`` as a status or as response headers **must** follow RFC 2616 with respect to encoding. @@ -1033,7 +980,7 @@ MIME encoding. On Python platforms where the ``str`` or ``StringType`` type is in -fact Unicode-based (e.g. Jython, IronPython, Python 3, etc.), all +fact Unicode-based (e.g. Jython, IronPython, Python 3000, etc.), all "strings" referred to in this specification must contain only code points representable in ISO-8859-1 encoding (``\u0000`` through ``\u00FF``, inclusive). It is a fatal error for an application to @@ -1041,18 +988,12 @@ Similarly, servers and gateways **must not** supply strings to an application containing any other Unicode characters. -Again, all objects referred to in this specification as "strings" -**must** be of type ``str`` or ``StringType``, and **must not** be -of type ``unicode`` or ``UnicodeType``. And, even if a given platform -allows for more than 8 bits per character in ``str``/``StringType`` -objects, only the lower 8 bits may be used, for any value referred -to in this specification as a "string". - -For values referred to in this specification as "bytestrings" -(i.e., values read from ``wsgi.input``, passed to ``write()`` -or yielded by the application), the value **must** be of type -``bytes`` under Python 3, and ``str`` in earlier versions of -Python. +Again, all strings referred to in this specification **must** be +of type ``str`` or ``StringType``, and **must not** be of type +``unicode`` or ``UnicodeType``. And, even if a given platform allows +for more than 8 bits per character in ``str``/``StringType`` objects, +only the lower 8 bits may be used, for any value referred to in +this specification as a "string". Error Handling @@ -1507,7 +1448,7 @@ ``environ`` dictionary. This is the recommended approach for offering any such value-added services. -2. Why can you call ``write()`` *and* yield bytestrings/return an +2. Why can you call ``write()`` *and* yield strings/return an iterable? Shouldn't we pick just one way? If we supported only the iteration approach, then current From python-checkins at python.org Mon Sep 27 01:49:21 2010 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 27 Sep 2010 01:49:21 +0200 (CEST) Subject: [Python-checkins] r85022 - python/branches/release27-maint/Doc/library/stdtypes.rst Message-ID: <20100926234921.15AE5EEB10@mail.python.org> Author: benjamin.peterson Date: Mon Sep 27 01:49:20 2010 New Revision: 85022 Log: versionadded #9956 Modified: python/branches/release27-maint/Doc/library/stdtypes.rst Modified: python/branches/release27-maint/Doc/library/stdtypes.rst ============================================================================== --- python/branches/release27-maint/Doc/library/stdtypes.rst (original) +++ python/branches/release27-maint/Doc/library/stdtypes.rst Mon Sep 27 01:49:20 2010 @@ -2546,6 +2546,8 @@ memoryview type =============== +.. versionadded:: 2.7 + :class:`memoryview` objects allow Python code to access the internal data of an object that supports the buffer protocol without copying. Memory is generally interpreted as simple bytes. From python-checkins at python.org Mon Sep 27 01:52:17 2010 From: python-checkins at python.org (phillip.eby) Date: Mon, 27 Sep 2010 01:52:17 +0200 (CEST) Subject: [Python-checkins] r85023 - peps/trunk/pep-0333.txt Message-ID: <20100926235217.83C45EEAEE@mail.python.org> Author: phillip.eby Date: Mon Sep 27 01:52:17 2010 New Revision: 85023 Log: PEP 333 is dead; long live PEP 3333. Or not. ;-) Modified: peps/trunk/pep-0333.txt Modified: peps/trunk/pep-0333.txt ============================================================================== --- peps/trunk/pep-0333.txt (original) +++ peps/trunk/pep-0333.txt Mon Sep 27 01:52:17 2010 @@ -4,13 +4,22 @@ Last-Modified: $Date$ Author: Phillip J. Eby Discussions-To: Python Web-SIG -Status: Draft +Status: Final +Replaced-By: 3333 Type: Informational Content-Type: text/x-rst Created: 07-Dec-2003 Post-History: 07-Dec-2003, 08-Aug-2004, 20-Aug-2004, 27-Aug-2004 +Preface +======= + +Note: For an updated version of this spec that supports Python 3.x and +includes community errata, addenda, and clarifications, please +see PEP 3333 instead. + + Abstract ======== From python-checkins at python.org Mon Sep 27 01:56:00 2010 From: python-checkins at python.org (phillip.eby) Date: Mon, 27 Sep 2010 01:56:00 +0200 (CEST) Subject: [Python-checkins] r85024 - peps/trunk/pep-3333.txt Message-ID: <20100926235600.02B75EEB10@mail.python.org> Author: phillip.eby Date: Mon Sep 27 01:55:59 2010 New Revision: 85024 Log: Credit Graham's work, add Replaces: header. Modified: peps/trunk/pep-3333.txt Modified: peps/trunk/pep-3333.txt ============================================================================== --- peps/trunk/pep-3333.txt (original) +++ peps/trunk/pep-3333.txt Mon Sep 27 01:55:59 2010 @@ -6,6 +6,7 @@ Discussions-To: Python Web-SIG Status: Draft Type: Informational +Replaces: 333 Content-Type: text/x-rst Created: 26-Sep-2010 Post-History: 26-Sep-2010 @@ -1700,6 +1701,13 @@ HTTP RFC compliance, especially with regard to HTTP/1.1 features that I didn't even know existed until he pointed them out. +* Graham Dumpleton, who worked tirelessly in the face of my laziness + and stupidity to get some sort of Python 3 version out, who proposed + the "native strings" vs. "byte strings" concept, and thoughtfully + wrestled through a great many HTTP, ``wsgi.input``, and other + amendments. Most, if not all, of the credit for this new PEP + belongs to him. + References ========== From python-checkins at python.org Mon Sep 27 03:26:04 2010 From: python-checkins at python.org (senthil.kumaran) Date: Mon, 27 Sep 2010 03:26:04 +0200 (CEST) Subject: [Python-checkins] r85025 - in python/branches/py3k/Lib: test/test_urllib2net.py urllib/request.py Message-ID: <20100927012604.0355BEEAFE@mail.python.org> Author: senthil.kumaran Date: Mon Sep 27 03:26:03 2010 New Revision: 85025 Log: Fix Issue1595365 - Adding the req.headers after the un-redirect headers have been added. This helps in accidental overwritting of User-Agent header to default value. To preserve the old behavior, only headers not in unredirected headers will be updated. Modified: python/branches/py3k/Lib/test/test_urllib2net.py python/branches/py3k/Lib/urllib/request.py Modified: python/branches/py3k/Lib/test/test_urllib2net.py ============================================================================== --- python/branches/py3k/Lib/test/test_urllib2net.py (original) +++ python/branches/py3k/Lib/test/test_urllib2net.py Mon Sep 27 03:26:03 2010 @@ -156,6 +156,18 @@ self.assertEqual(res.geturl(), "http://docs.python.org/glossary.html") + def test_custom_headers(self): + url = "http://www.example.com" + opener = urllib.request.build_opener() + request = urllib.request.Request(url) + self.assertFalse(request.header_items()) + opener.open(request) + self.assertTrue(request.header_items()) + self.assertTrue(request.has_header('User-agent')) + request.add_header('User-Agent','Test-Agent') + opener.open(request) + self.assertEqual(request.get_header('User-agent'),'Test-Agent') + def _test_urls(self, urls, handlers, retry=True): import time import logging Modified: python/branches/py3k/Lib/urllib/request.py ============================================================================== --- python/branches/py3k/Lib/urllib/request.py (original) +++ python/branches/py3k/Lib/urllib/request.py Mon Sep 27 03:26:03 2010 @@ -1063,8 +1063,10 @@ raise URLError('no host given') h = http_class(host, timeout=req.timeout) # will parse host:port - headers = dict(req.headers) - headers.update(req.unredirected_hdrs) + + headers = dict(req.unredirected_hdrs) + headers.update(dict((k, v) for k, v in req.headers.items() + if k not in headers)) # TODO(jhylton): Should this be redesigned to handle # persistent connections? From python-checkins at python.org Mon Sep 27 03:28:10 2010 From: python-checkins at python.org (senthil.kumaran) Date: Mon, 27 Sep 2010 03:28:10 +0200 (CEST) Subject: [Python-checkins] r85026 - in python/branches/release31-maint: Lib/test/test_urllib2net.py Lib/urllib/request.py Message-ID: <20100927012810.52BFAEEB1A@mail.python.org> Author: senthil.kumaran Date: Mon Sep 27 03:28:10 2010 New Revision: 85026 Log: Merged revisions 85025 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85025 | senthil.kumaran | 2010-09-27 06:56:03 +0530 (Mon, 27 Sep 2010) | 6 lines Fix Issue1595365 - Adding the req.headers after the un-redirect headers have been added. This helps in accidental overwritting of User-Agent header to default value. To preserve the old behavior, only headers not in unredirected headers will be updated. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_urllib2net.py python/branches/release31-maint/Lib/urllib/request.py Modified: python/branches/release31-maint/Lib/test/test_urllib2net.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_urllib2net.py (original) +++ python/branches/release31-maint/Lib/test/test_urllib2net.py Mon Sep 27 03:28:10 2010 @@ -159,6 +159,18 @@ self.assertEqual(res.geturl(), "http://docs.python.org/glossary.html") + def test_custom_headers(self): + url = "http://www.example.com" + opener = urllib.request.build_opener() + request = urllib.request.Request(url) + self.assertFalse(request.header_items()) + opener.open(request) + self.assertTrue(request.header_items()) + self.assertTrue(request.has_header('User-agent')) + request.add_header('User-Agent','Test-Agent') + opener.open(request) + self.assertEqual(request.get_header('User-agent'),'Test-Agent') + def _test_urls(self, urls, handlers, retry=True): import socket import time Modified: python/branches/release31-maint/Lib/urllib/request.py ============================================================================== --- python/branches/release31-maint/Lib/urllib/request.py (original) +++ python/branches/release31-maint/Lib/urllib/request.py Mon Sep 27 03:28:10 2010 @@ -1063,8 +1063,10 @@ raise URLError('no host given') h = http_class(host, timeout=req.timeout) # will parse host:port - headers = dict(req.headers) - headers.update(req.unredirected_hdrs) + + headers = dict(req.unredirected_hdrs) + headers.update(dict((k, v) for k, v in req.headers.items() + if k not in headers)) # TODO(jhylton): Should this be redesigned to handle # persistent connections? From python-checkins at python.org Mon Sep 27 03:40:59 2010 From: python-checkins at python.org (senthil.kumaran) Date: Mon, 27 Sep 2010 03:40:59 +0200 (CEST) Subject: [Python-checkins] r85027 - in python/branches/release27-maint: Lib/test/test_urllib2net.py Lib/urllib2.py Message-ID: <20100927014059.619B4EEABC@mail.python.org> Author: senthil.kumaran Date: Mon Sep 27 03:40:59 2010 New Revision: 85027 Log: Merged revisions 85025 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85025 | senthil.kumaran | 2010-09-27 06:56:03 +0530 (Mon, 27 Sep 2010) | 6 lines Fix Issue1595365 - Adding the req.headers after the un-redirect headers have been added. This helps in accidental overwritting of User-Agent header to default value. To preserve the old behavior, only headers not in unredirected headers will be updated. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_urllib2net.py python/branches/release27-maint/Lib/urllib2.py Modified: python/branches/release27-maint/Lib/test/test_urllib2net.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_urllib2net.py (original) +++ python/branches/release27-maint/Lib/test/test_urllib2net.py Mon Sep 27 03:40:59 2010 @@ -172,6 +172,18 @@ finally: res.close() + def test_custom_headers(self): + url = "http://www.example.com" + opener = urllib2.build_opener() + request = urllib2.Request(url) + self.assertFalse(request.header_items()) + opener.open(request) + self.assertTrue(request.header_items()) + self.assertTrue(request.has_header('User-agent')) + request.add_header('User-Agent','Test-Agent') + opener.open(request) + self.assertEqual(request.get_header('User-agent'),'Test-Agent') + def _test_urls(self, urls, handlers, retry=True): import time import logging Modified: python/branches/release27-maint/Lib/urllib2.py ============================================================================== --- python/branches/release27-maint/Lib/urllib2.py (original) +++ python/branches/release27-maint/Lib/urllib2.py Mon Sep 27 03:40:59 2010 @@ -1127,8 +1127,10 @@ h = http_class(host, timeout=req.timeout) # will parse host:port h.set_debuglevel(self._debuglevel) - headers = dict(req.headers) - headers.update(req.unredirected_hdrs) + headers = dict(req.unredirected_hdrs) + headers.update(dict((k, v) for k, v in req.headers.items() + if k not in headers)) + # We want to make an HTTP/1.1 request, but the addinfourl # class isn't prepared to deal with a persistent connection. # It will try to read all remaining data from the socket, From solipsis at pitrou.net Mon Sep 27 04:46:24 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 27 Sep 2010 04:46:24 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r85025): sum=0 Message-ID: py3k results for svn r85025 (hg cset a7a6197b450d) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogLO96L4', '-x'] From python-checkins at python.org Mon Sep 27 07:32:54 2010 From: python-checkins at python.org (kristjan.jonsson) Date: Mon, 27 Sep 2010 07:32:54 +0200 (CEST) Subject: [Python-checkins] r85028 - in python/branches/py3k: Doc/c-api/init.rst Include/pythonrun.h Modules/getpath.c PC/getpathp.c Message-ID: <20100927053254.DAD4AEE9DC@mail.python.org> Author: kristjan.jonsson Date: Mon Sep 27 07:32:54 2010 New Revision: 85028 Log: issue 9910 Add a Py_SetPath api to override magic path computations when starting up python. Modified: python/branches/py3k/Doc/c-api/init.rst python/branches/py3k/Include/pythonrun.h python/branches/py3k/Modules/getpath.c python/branches/py3k/PC/getpathp.c Modified: python/branches/py3k/Doc/c-api/init.rst ============================================================================== --- python/branches/py3k/Doc/c-api/init.rst (original) +++ python/branches/py3k/Doc/c-api/init.rst Mon Sep 27 07:32:54 2010 @@ -27,8 +27,9 @@ Initialize the Python interpreter. In an application embedding Python, this should be called before using any other Python/C API functions; with the - exception of :cfunc:`Py_SetProgramName`, :cfunc:`PyEval_InitThreads`, - :cfunc:`PyEval_ReleaseLock`, and :cfunc:`PyEval_AcquireLock`. This initializes + exception of :cfunc:`Py_SetProgramName`, :cfunc:`Py_SetPath`, + :cfunc:`PyEval_InitThreads`, :cfunc:`PyEval_ReleaseLock`, and + :cfunc:`PyEval_AcquireLock`. This initializes the table of loaded modules (``sys.modules``), and creates the fundamental modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`. It also initializes the module search path (``sys.path``). It does not set ``sys.argv``; use @@ -256,6 +257,7 @@ .. index:: triple: module; search; path single: path (in module sys) + single: Py_SetPath() Return the default module search path; this is computed from the program name (set by :cfunc:`Py_SetProgramName` above) and some environment variables. @@ -270,6 +272,25 @@ .. XXX should give the exact rules +.. cfunction:: void Py_SetPath(const wchar_t *) + + .. index:: + triple: module; search; path + single: path (in module sys) + single: Py_GetPath() + + Set the default module search path. If this function is called before + :cfunc: `Py_Initialize` then :cfunc: Py_GetPath won't attempt to compute + a default serarch path but uses the provided one in stead. This is useful + if Python is being embedded by an application that has full knowledge + of the location of all modules. The path components should be separated + by semicolons. + + This also causes `sys.executable` to be set only to the raw program name + (see :cfunc:`Py_SetProgramName`) and `for sys.prefix` and + `sys.exec_prefix` to be empty. It is up to the caller to modify these if + required after calling :cfunc: `Py_Initialize`. + .. cfunction:: const char* Py_GetVersion() Return the version of this Python interpreter. This is a string that looks Modified: python/branches/py3k/Include/pythonrun.h ============================================================================== --- python/branches/py3k/Include/pythonrun.h (original) +++ python/branches/py3k/Include/pythonrun.h Mon Sep 27 07:32:54 2010 @@ -113,6 +113,7 @@ PyAPI_FUNC(wchar_t *) Py_GetPrefix(void); PyAPI_FUNC(wchar_t *) Py_GetExecPrefix(void); PyAPI_FUNC(wchar_t *) Py_GetPath(void); +PyAPI_FUNC(void) Py_SetPath(const wchar_t *); /* In their own files */ PyAPI_FUNC(const char *) Py_GetVersion(void); Modified: python/branches/py3k/Modules/getpath.c ============================================================================== --- python/branches/py3k/Modules/getpath.c (original) +++ python/branches/py3k/Modules/getpath.c Mon Sep 27 07:32:54 2010 @@ -90,6 +90,9 @@ * known use of sys.prefix and sys.exec_prefix is for the ILU installation * process to find the installed Python tree. * + * An embedding application can use Py_SetPath() to override all of + * these authomatic path computations. + * * NOTE: Windows MSVC builds use PC/getpathp.c instead! */ @@ -771,6 +774,23 @@ /* External interface */ +void +Py_SetPath(const wchar_t *path) +{ + if (module_search_path != NULL) { + free(module_search_path); + module_search_path = NULL; + } + if (path != NULL) { + extern wchar_t *Py_GetProgramName(void); + wchar_t *prog = Py_GetProgramName(); + wcsncpy(progpath, prog, MAXPATHLEN); + exec_prefix[0] = prefix[0] = L'\0'; + module_search_path = malloc((wcslen(path) + 1) * sizeof(wchar_t)); + if (module_search_path != NULL) + wcscpy(module_search_path, path); + } +} wchar_t * Py_GetPath(void) Modified: python/branches/py3k/PC/getpathp.c ============================================================================== --- python/branches/py3k/PC/getpathp.c (original) +++ python/branches/py3k/PC/getpathp.c Mon Sep 27 07:32:54 2010 @@ -51,6 +51,9 @@ exe, some very strange installation setup) you get a path with some default, but relative, paths. + * An embedding application can use Py_SetPath() to override all of + these authomatic path computations. + ---------------------------------------------------------------- */ @@ -79,6 +82,9 @@ * The approach is an adaptation for Windows of the strategy used in * ../Modules/getpath.c; it uses the Windows Registry as one of its * information sources. + * + * Py_SetPath() can be used to override this mechanism. Call Py_SetPath + * with a semicolon separated path prior to calling Py_Initialize. */ #ifndef LANDMARK @@ -654,6 +660,24 @@ /* External interface */ +void +Py_SetPath(const wchar_t *path) +{ + if (module_search_path != NULL) { + free(module_search_path); + module_search_path = NULL; + } + if (path != NULL) { + extern wchar_t *Py_GetProgramName(void); + wchar_t *prog = Py_GetProgramName(); + wcsncpy(progpath, prog, MAXPATHLEN); + prefix[0] = L'\0'; + module_search_path = malloc((wcslen(path) + 1) * sizeof(wchar_t)); + if (module_search_path != NULL) + wcscpy(module_search_path, path); + } +} + wchar_t * Py_GetPath(void) { From python-checkins at python.org Mon Sep 27 15:53:47 2010 From: python-checkins at python.org (vinay.sajip) Date: Mon, 27 Sep 2010 15:53:47 +0200 (CEST) Subject: [Python-checkins] r85029 - in python/branches: py3k/Doc/library/logging.rst release27-maint/Doc/library/logging.rst Message-ID: <20100927135347.BB8C4EE986@mail.python.org> Author: vinay.sajip Date: Mon Sep 27 15:53:47 2010 New Revision: 85029 Log: logging: Updated library configuration documentation. Modified: python/branches/py3k/Doc/library/logging.rst python/branches/release27-maint/Doc/library/logging.rst Modified: python/branches/py3k/Doc/library/logging.rst ============================================================================== --- python/branches/py3k/Doc/library/logging.rst (original) +++ python/branches/py3k/Doc/library/logging.rst Mon Sep 27 15:53:47 2010 @@ -526,10 +526,18 @@ libraries, then the logger name specified can be "orgname.foo" rather than just "foo". +**PLEASE NOTE:** It is strongly advised that you *do not add any handlers other +than* :class:`NullHandler` *to your library's loggers*. This is because the +configuration of handlers is the prerogative of the application developer who +uses your library. The application developer knows their target audience and +what handlers are most appropriate for their application: if you add handlers +"under the hood", you might well interfere with their ability to carry out +unit tests and deliver logs which suit their requirements. + .. versionadded:: 3.1 - The :class:`NullHandler` class was not present in previous versions, but is - now included, so that it need not be defined in library code. +The :class:`NullHandler` class was not present in previous versions, but is +now included, so that it need not be defined in library code. @@ -2141,6 +2149,16 @@ This method does nothing. + .. method:: handle(record) + + This method does nothing. + + .. method:: createLock() + + This method returns `None` for the lock, since there is no + underlying I/O to which access needs to be serialized. + + See :ref:`library-config` for more information on how to use :class:`NullHandler`. Modified: python/branches/release27-maint/Doc/library/logging.rst ============================================================================== --- python/branches/release27-maint/Doc/library/logging.rst (original) +++ python/branches/release27-maint/Doc/library/logging.rst Mon Sep 27 15:53:47 2010 @@ -530,6 +530,14 @@ libraries, then the logger name specified can be "orgname.foo" rather than just "foo". +**PLEASE NOTE:** It is strongly advised that you *do not add any handlers other +than* :class:`NullHandler` *to your library's loggers*. This is because the +configuration of handlers is the prerogative of the application developer who +uses your library. The application developer knows their target audience and +what handlers are most appropriate for their application: if you add handlers +"under the hood", you might well interfere with their ability to carry out +unit tests and deliver logs which suit their requirements. + .. versionadded:: 2.7 The :class:`NullHandler` class was not present in previous versions, but is now @@ -1957,6 +1965,16 @@ This method does nothing. + .. method:: handle(record) + + This method does nothing. + + .. method:: createLock() + + This method returns `None` for the lock, since there is no + underlying I/O to which access needs to be serialized. + + See :ref:`library-config` for more information on how to use :class:`NullHandler`. From python-checkins at python.org Mon Sep 27 16:43:09 2010 From: python-checkins at python.org (phillip.eby) Date: Mon, 27 Sep 2010 16:43:09 +0200 (CEST) Subject: [Python-checkins] r85030 - in peps/trunk: pep-0333.txt pep-3333.txt Message-ID: <20100927144309.7FF26EE989@mail.python.org> Author: phillip.eby Date: Mon Sep 27 16:43:09 2010 New Revision: 85030 Log: Fix PEP headers and markup problems Modified: peps/trunk/pep-0333.txt peps/trunk/pep-3333.txt Modified: peps/trunk/pep-0333.txt ============================================================================== --- peps/trunk/pep-0333.txt (original) +++ peps/trunk/pep-0333.txt Mon Sep 27 16:43:09 2010 @@ -5,11 +5,11 @@ Author: Phillip J. Eby Discussions-To: Python Web-SIG Status: Final -Replaced-By: 3333 Type: Informational Content-Type: text/x-rst Created: 07-Dec-2003 Post-History: 07-Dec-2003, 08-Aug-2004, 20-Aug-2004, 27-Aug-2004 +Replaced-By: 3333 Preface Modified: peps/trunk/pep-3333.txt ============================================================================== --- peps/trunk/pep-3333.txt (original) +++ peps/trunk/pep-3333.txt Mon Sep 27 16:43:09 2010 @@ -6,14 +6,14 @@ Discussions-To: Python Web-SIG Status: Draft Type: Informational -Replaces: 333 Content-Type: text/x-rst Created: 26-Sep-2010 Post-History: 26-Sep-2010 +Replaces: 333 -Preface for Readers of PEP 333 -============================== +Preface for Readers of PEP \333 +=============================== This is an updated version of PEP 333, modified slightly to improve usability under Python 3, and to incorporate several long-standing @@ -22,16 +22,14 @@ While for procedural reasons [6]_, this must be a distinct PEP, no changes were made that invalidate previously-compliant servers or applications under Python 2.x. If your 2.x application or server -is compliant to PEP 333, it is also compliant with PEP 3333. +is compliant to PEP \333, it is also compliant with this PEP. Under Python 3, however, your app or server must also follow the rules outlined in the sections below titled, `A Note On String Types`_, and `Unicode Issues`_. -For detailed, line-by-line diffs between this document and PEP 333, -you may view its `SVN revision history`_, from revision 85014 forward. - -.. _SVN revision history: http://svn.python.org/view/peps/trunk/pep-3333.txt?view=log +For detailed, line-by-line diffs between this document and PEP \333, +you may view its SVN revision history [7]_, from revision 85014 forward. Abstract @@ -1727,9 +1725,12 @@ .. [5] mod_ssl Reference, "Environment Variables" (http://www.modssl.org/docs/2.8/ssl_reference.html#ToC25) -.. [6] Procedural issues regarding modifications to PEP 333 +.. [6] Procedural issues regarding modifications to PEP \333 (http://mail.python.org/pipermail/python-dev/2010-September/104114.html) +.. [7] SVN revision history for PEP \3333 + (http://svn.python.org/view/peps/trunk/pep-3333.txt?view=log) + Copyright ========= From python-checkins at python.org Mon Sep 27 17:49:20 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Mon, 27 Sep 2010 17:49:20 +0200 (CEST) Subject: [Python-checkins] r85031 - python/branches/py3k/Lib/trace.py Message-ID: <20100927154920.99C9DEEA4F@mail.python.org> Author: alexander.belopolsky Date: Mon Sep 27 17:49:20 2010 New Revision: 85031 Log: Issue 9941: Minor code cleanup before implementing the context manager feature: - Eliminated code repetition between run and runctx; - Removed redundant calls to dict.key; - Removed unused "blabbed" attribute; - Simplified the loop in write_results_file(). Modified: python/branches/py3k/Lib/trace.py Modified: python/branches/py3k/Lib/trace.py ============================================================================== --- python/branches/py3k/Lib/trace.py (original) +++ python/branches/py3k/Lib/trace.py Mon Sep 27 17:49:20 2010 @@ -243,13 +243,13 @@ other_calledfuncs = other.calledfuncs other_callers = other.callers - for key in other_counts.keys(): + for key in other_counts: counts[key] = counts.get(key, 0) + other_counts[key] - for key in other_calledfuncs.keys(): + for key in other_calledfuncs: calledfuncs[key] = 1 - for key in other_callers.keys(): + for key in other_callers: callers[key] = 1 def write_results(self, show_missing=True, summary=False, coverdir=None): @@ -259,7 +259,7 @@ if self.calledfuncs: print() print("functions called:") - calls = self.calledfuncs.keys() + calls = self.calledfuncs for filename, modulename, funcname in sorted(calls): print(("filename: %s, modulename: %s, funcname: %s" % (filename, modulename, funcname))) @@ -269,7 +269,7 @@ print("calling relationships:") lastfile = lastcfile = "" for ((pfile, pmod, pfunc), (cfile, cmod, cfunc)) \ - in sorted(self.callers.keys()): + in sorted(self.callers): if pfile != lastfile: print() print("***", pfile, "***") @@ -283,7 +283,7 @@ # turn the counts data ("(filename, lineno) = count") into something # accessible on a per-file basis per_file = {} - for filename, lineno in self.counts.keys(): + for filename, lineno in self.counts: lines_hit = per_file[filename] = per_file.get(filename, {}) lines_hit[lineno] = self.counts[(filename, lineno)] @@ -324,7 +324,7 @@ if summary and sums: print("lines cov% module (path)") - for m in sorted(sums.keys()): + for m in sorted(sums): n_lines, percent, modulename, filename = sums[m] print("%5d %3d%% %s (%s)" % sums[m]) @@ -348,8 +348,7 @@ n_lines = 0 n_hits = 0 - for i, line in enumerate(lines): - lineno = i + 1 + for lineno, line in enumerate(lines, 1): # do the blank/comment match to try to mark more lines # (help the reader find stuff that hasn't been covered) if lineno in lines_hit: @@ -362,12 +361,12 @@ # lines preceded by no marks weren't hit # Highlight them if so indicated, unless the line contains # #pragma: NO COVER - if lineno in lnotab and not PRAGMA_NOCOVER in lines[i]: + if lineno in lnotab and not PRAGMA_NOCOVER in line: outfile.write(">>>>>> ") n_lines += 1 else: outfile.write(" ") - outfile.write(lines[i].expandtabs(8)) + outfile.write(line.expandtabs(8)) outfile.close() return n_hits, n_lines @@ -456,7 +455,6 @@ self.outfile = outfile self.ignore = Ignore(ignoremods, ignoredirs) self.counts = {} # keys are (filename, linenumber) - self.blabbed = {} # for debugging self.pathtobasename = {} # for memoizing os.path.basename self.donothing = 0 self.trace = trace @@ -486,15 +484,7 @@ def run(self, cmd): import __main__ dict = __main__.__dict__ - if not self.donothing: - threading.settrace(self.globaltrace) - sys.settrace(self.globaltrace) - try: - exec(cmd, dict, dict) - finally: - if not self.donothing: - sys.settrace(None) - threading.settrace(None) + self.runctx(cmd, dict, dict) def runctx(self, cmd, globals=None, locals=None): if globals is None: globals = {} From python-checkins at python.org Mon Sep 27 19:52:25 2010 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 27 Sep 2010 19:52:25 +0200 (CEST) Subject: [Python-checkins] r85032 - in python/branches/py3k: Lib/test/test_socket.py Misc/NEWS Modules/socketmodule.c Message-ID: <20100927175225.57AC1FAD5@mail.python.org> Author: antoine.pitrou Date: Mon Sep 27 19:52:25 2010 New Revision: 85032 Log: Issue #9950: Fix socket.sendall() crash or misbehaviour when a signal is received. Now sendall() properly calls signal handlers if necessary, and retries sending if these returned successfully, including on sockets with a timeout. Modified: python/branches/py3k/Lib/test/test_socket.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/socketmodule.c Modified: python/branches/py3k/Lib/test/test_socket.py ============================================================================== --- python/branches/py3k/Lib/test/test_socket.py (original) +++ python/branches/py3k/Lib/test/test_socket.py Mon Sep 27 19:52:25 2010 @@ -16,6 +16,7 @@ import contextlib from weakref import proxy import signal +import math def try_address(host, port=0, family=socket.AF_INET): """Try to bind a socket on the given host:port and return True @@ -655,6 +656,42 @@ # have a reverse entry yet # socket.gethostbyaddr('?????????.python.org') + def check_sendall_interrupted(self, with_timeout): + # socketpair() is not stricly required, but it makes things easier. + if not hasattr(signal, 'alarm') or not hasattr(socket, 'socketpair'): + self.skipTest("signal.alarm and socket.socketpair required for this test") + # Our signal handlers clobber the C errno by calling a math function + # with an invalid domain value. + def ok_handler(*args): + self.assertRaises(ValueError, math.acosh, 0) + def raising_handler(*args): + self.assertRaises(ValueError, math.acosh, 0) + 1 // 0 + c, s = socket.socketpair() + old_alarm = signal.signal(signal.SIGALRM, raising_handler) + try: + if with_timeout: + # Just above the one second minimum for signal.alarm + c.settimeout(1.5) + with self.assertRaises(ZeroDivisionError): + signal.alarm(1) + c.sendall(b"x" * (1024**2)) + if with_timeout: + signal.signal(signal.SIGALRM, ok_handler) + signal.alarm(1) + self.assertRaises(socket.timeout, c.sendall, b"x" * (1024**2)) + finally: + signal.signal(signal.SIGALRM, old_alarm) + c.close() + s.close() + + def test_sendall_interrupted(self): + self.check_sendall_interrupted(False) + + def test_sendall_interrupted_with_timeout(self): + self.check_sendall_interrupted(True) + + @unittest.skipUnless(thread, 'Threading required for this test.') class BasicTCPTest(SocketConnectedTest): Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 27 19:52:25 2010 @@ -68,6 +68,11 @@ Library ------- +- Issue #9950: Fix socket.sendall() crash or misbehaviour when a signal is + received. Now sendall() properly calls signal handlers if necessary, + and retries sending if these returned successfully, including on sockets + with a timeout. + - Issue #9947: logging: Fixed locking bug in stopListening. - Issue #9945: logging: Fixed locking bugs in addHandler/removeHandler. Modified: python/branches/py3k/Modules/socketmodule.c ============================================================================== --- python/branches/py3k/Modules/socketmodule.c (original) +++ python/branches/py3k/Modules/socketmodule.c Mon Sep 27 19:52:25 2010 @@ -2568,7 +2568,7 @@ { char *buf; Py_ssize_t len, n = -1; - int flags = 0, timeout; + int flags = 0, timeout, saved_errno; Py_buffer pbuf; if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags)) @@ -2581,42 +2581,44 @@ return select_error(); } - Py_BEGIN_ALLOW_THREADS do { + Py_BEGIN_ALLOW_THREADS timeout = internal_select(s, 1); n = -1; - if (timeout) - break; + if (!timeout) { #ifdef __VMS - n = sendsegmented(s->sock_fd, buf, len, flags); + n = sendsegmented(s->sock_fd, buf, len, flags); #else - n = send(s->sock_fd, buf, len, flags); + n = send(s->sock_fd, buf, len, flags); #endif + } + Py_END_ALLOW_THREADS + if (timeout == 1) { + PyBuffer_Release(&pbuf); + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + /* PyErr_CheckSignals() might change errno */ + saved_errno = errno; + /* We must run our signal handlers before looping again. + send() can return a successful partial write when it is + interrupted, so we can't restrict ourselves to EINTR. */ + if (PyErr_CheckSignals()) { + PyBuffer_Release(&pbuf); + return NULL; + } if (n < 0) { -#ifdef EINTR - /* We must handle EINTR here as there is no way for - * the caller to know how much was sent otherwise. */ - if (errno == EINTR) { - /* Run signal handlers. If an exception was - * raised, abort and leave this socket in - * an unknown state. */ - if (PyErr_CheckSignals()) - return NULL; + /* If interrupted, try again */ + if (saved_errno == EINTR) continue; - } -#endif - break; + else + break; } buf += n; len -= n; } while (len > 0); - Py_END_ALLOW_THREADS PyBuffer_Release(&pbuf); - if (timeout == 1) { - PyErr_SetString(socket_timeout, "timed out"); - return NULL; - } if (n < 0) return s->errorhandler(); From python-checkins at python.org Mon Sep 27 19:56:36 2010 From: python-checkins at python.org (brian.curtin) Date: Mon, 27 Sep 2010 19:56:36 +0200 (CEST) Subject: [Python-checkins] r85033 - in python/branches/py3k: Doc/library/winreg.rst Lib/test/test_winreg.py Misc/NEWS PC/winreg.c Message-ID: <20100927175636.F1F14EE9DA@mail.python.org> Author: brian.curtin Date: Mon Sep 27 19:56:36 2010 New Revision: 85033 Log: Implement #8521. Added named argument handling to winreg's CreateKeyEx, DeleteKeyEx, and OpenKeyEx. Note that CKE and DKE are new functions for 3.2 so I didn't give them a versionchanged because of the existing versionadded. OpenKeyEx already existed so it gets a versionchanged tag. Modified: python/branches/py3k/Doc/library/winreg.rst python/branches/py3k/Lib/test/test_winreg.py python/branches/py3k/Misc/NEWS python/branches/py3k/PC/winreg.c Modified: python/branches/py3k/Doc/library/winreg.rst ============================================================================== --- python/branches/py3k/Doc/library/winreg.rst (original) +++ python/branches/py3k/Doc/library/winreg.rst Mon Sep 27 19:56:36 2010 @@ -60,7 +60,7 @@ :exc:`WindowsError` exception is raised. -.. function:: CreateKeyEx(key, sub_key[, res[, sam]]) +.. function:: CreateKeyEx(key, sub_key, reserved=0, access=KEY_ALL_ACCESS) Creates or opens the specified key, returning a :ref:`handle object `. @@ -103,7 +103,7 @@ If the method fails, a :exc:`WindowsError` exception is raised. -.. function:: DeleteKeyEx(key, sub_key[, sam[, res]]) +.. function:: DeleteKeyEx(key, sub_key, access=KEY_ALL_ACCESS, reserved=0) Deletes the specified key. @@ -243,7 +243,7 @@ specified in *file_name* is relative to the remote computer. -.. function:: OpenKey(key, sub_key[, res[, sam]]) +.. function:: OpenKey(key, sub_key, reserved=0, access=KEY_ALL_ACCESS) Opens the specified key, returning a :ref:`handle object `. @@ -262,6 +262,8 @@ If the function fails, :exc:`WindowsError` is raised. + .. versionchanged:: 3.2 Allow the use of named arguments. + .. function:: OpenKeyEx() Modified: python/branches/py3k/Lib/test/test_winreg.py ============================================================================== --- python/branches/py3k/Lib/test/test_winreg.py (original) +++ python/branches/py3k/Lib/test/test_winreg.py Mon Sep 27 19:56:36 2010 @@ -185,6 +185,16 @@ self._read_test_data(root_key, subkeystr) self._delete_test_data(root_key, subkeystr) + def _test_named_args(self, key, sub_key): + with CreateKeyEx(key=key, sub_key=sub_key, reserved=0, + access=KEY_ALL_ACCESS) as ckey: + self.assertTrue(ckey.handle != 0) + + with OpenKeyEx(key=key, sub_key=sub_key, reserved=0, + access=KEY_ALL_ACCESS) as okey: + self.assertTrue(okey.handle != 0) + + class LocalWinregTests(BaseWinregTests): def test_registry_works(self): @@ -203,6 +213,12 @@ self._delete_test_data(HKEY_CURRENT_USER) + def test_named_arguments(self): + self._test_named_args(HKEY_CURRENT_USER, test_key_name) + # Use the regular DeleteKey to clean up + # DeleteKeyEx takes named args and is tested separately + DeleteKey(HKEY_CURRENT_USER, test_key_name) + def test_connect_registry_to_local_machine_works(self): # perform minimal ConnectRegistry test which just invokes it h = ConnectRegistry(None, HKEY_LOCAL_MACHINE) @@ -314,6 +330,12 @@ @unittest.skipUnless(WIN64_MACHINE, "x64 specific registry tests") class Win64WinregTests(BaseWinregTests): + def test_named_arguments(self): + self._test_named_args(HKEY_CURRENT_USER, test_key_name) + # Clean up and also exercise the named arguments + DeleteKeyEx(key=HKEY_CURRENT_USER, sub_key=test_key_name, + access=KEY_ALL_ACCESS, reserved=0) + def test_reflection_functions(self): # Test that we can call the query, enable, and disable functions # on a key which isn't on the reflection list with no consequences. Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 27 19:56:36 2010 @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #8521: Allow CreateKeyEx, OpenKeyEx, and DeleteKeyEx functions + of winreg to use named arguments. + - Issue #9930: Remove bogus subtype check that was causing (e.g.) float.__rdiv__(2.0, 3) to return NotImplemented instead of the expected 1.5. Modified: python/branches/py3k/PC/winreg.c ============================================================================== --- python/branches/py3k/PC/winreg.c (original) +++ python/branches/py3k/PC/winreg.c Mon Sep 27 19:56:36 2010 @@ -989,23 +989,26 @@ } static PyObject * -PyCreateKeyEx(PyObject *self, PyObject *args) +PyCreateKeyEx(PyObject *self, PyObject *args, PyObject *kwargs) { HKEY hKey; - PyObject *obKey; - wchar_t *subKey; + PyObject *key; + wchar_t *sub_key; HKEY retKey; - int res = 0; - REGSAM sam = KEY_WRITE; + int reserved = 0; + REGSAM access = KEY_WRITE; long rc; - if (!PyArg_ParseTuple(args, "OZ|ii:CreateKeyEx", &obKey, &subKey, - &res, &sam)) + + char *kwlist[] = {"key", "sub_key", "reserved", "access", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OZ|ii:CreateKeyEx", kwlist, + &key, &sub_key, &reserved, &access)) return NULL; - if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + if (!PyHKEY_AsHKEY(key, &hKey, FALSE)) return NULL; - rc = RegCreateKeyExW(hKey, subKey, res, NULL, (DWORD)NULL, - sam, NULL, &retKey, NULL); + rc = RegCreateKeyExW(hKey, sub_key, reserved, NULL, (DWORD)NULL, + access, NULL, &retKey, NULL); if (rc != ERROR_SUCCESS) return PyErr_SetFromWindowsErrWithFunction(rc, "CreateKeyEx"); return PyHKEY_FromHKEY(retKey); @@ -1030,22 +1033,23 @@ } static PyObject * -PyDeleteKeyEx(PyObject *self, PyObject *args) +PyDeleteKeyEx(PyObject *self, PyObject *args, PyObject *kwargs) { HKEY hKey; - PyObject *obKey; + PyObject *key; HMODULE hMod; typedef LONG (WINAPI *RDKEFunc)(HKEY, const wchar_t*, REGSAM, int); RDKEFunc pfn = NULL; - wchar_t *subKey; + wchar_t *sub_key; long rc; - int res = 0; - REGSAM sam = KEY_WOW64_64KEY; + int reserved = 0; + REGSAM access = KEY_WOW64_64KEY; - if (!PyArg_ParseTuple(args, "Ou|ii:DeleteKeyEx", - &obKey, &subKey, &sam, &res)) + char *kwlist[] = {"key", "sub_key", "access", "reserved", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Ou|ii:DeleteKeyEx", kwlist, + &key, &sub_key, &access, &reserved)) return NULL; - if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + if (!PyHKEY_AsHKEY(key, &hKey, FALSE)) return NULL; /* Only available on 64bit platforms, so we must load it @@ -1060,7 +1064,7 @@ return NULL; } Py_BEGIN_ALLOW_THREADS - rc = (*pfn)(hKey, subKey, sam, res); + rc = (*pfn)(hKey, sub_key, access, reserved); Py_END_ALLOW_THREADS if (rc != ERROR_SUCCESS) @@ -1282,24 +1286,26 @@ } static PyObject * -PyOpenKey(PyObject *self, PyObject *args) +PyOpenKey(PyObject *self, PyObject *args, PyObject *kwargs) { HKEY hKey; - PyObject *obKey; - - wchar_t *subKey; - int res = 0; + PyObject *key; + wchar_t *sub_key; + int reserved = 0; HKEY retKey; long rc; - REGSAM sam = KEY_READ; - if (!PyArg_ParseTuple(args, "OZ|ii:OpenKey", &obKey, &subKey, - &res, &sam)) + REGSAM access = KEY_READ; + + char *kwlist[] = {"key", "sub_key", "reserved", "access", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OZ|ii:OpenKey", kwlist, + &key, &sub_key, &reserved, &access)) return NULL; - if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + if (!PyHKEY_AsHKEY(key, &hKey, FALSE)) return NULL; Py_BEGIN_ALLOW_THREADS - rc = RegOpenKeyExW(hKey, subKey, res, sam, &retKey); + rc = RegOpenKeyExW(hKey, sub_key, reserved, access, &retKey); Py_END_ALLOW_THREADS if (rc != ERROR_SUCCESS) return PyErr_SetFromWindowsErrWithFunction(rc, "RegOpenKeyEx"); @@ -1667,9 +1673,11 @@ {"CloseKey", PyCloseKey, METH_VARARGS, CloseKey_doc}, {"ConnectRegistry", PyConnectRegistry, METH_VARARGS, ConnectRegistry_doc}, {"CreateKey", PyCreateKey, METH_VARARGS, CreateKey_doc}, - {"CreateKeyEx", PyCreateKeyEx, METH_VARARGS, CreateKeyEx_doc}, + {"CreateKeyEx", (PyCFunction)PyCreateKeyEx, + METH_VARARGS | METH_KEYWORDS, CreateKeyEx_doc}, {"DeleteKey", PyDeleteKey, METH_VARARGS, DeleteKey_doc}, - {"DeleteKeyEx", PyDeleteKeyEx, METH_VARARGS, DeleteKeyEx_doc}, + {"DeleteKeyEx", (PyCFunction)PyDeleteKeyEx, + METH_VARARGS | METH_KEYWORDS, DeleteKeyEx_doc}, {"DeleteValue", PyDeleteValue, METH_VARARGS, DeleteValue_doc}, {"DisableReflectionKey", PyDisableReflectionKey, METH_VARARGS, DisableReflectionKey_doc}, {"EnableReflectionKey", PyEnableReflectionKey, METH_VARARGS, EnableReflectionKey_doc}, @@ -1679,8 +1687,10 @@ ExpandEnvironmentStrings_doc }, {"FlushKey", PyFlushKey, METH_VARARGS, FlushKey_doc}, {"LoadKey", PyLoadKey, METH_VARARGS, LoadKey_doc}, - {"OpenKey", PyOpenKey, METH_VARARGS, OpenKey_doc}, - {"OpenKeyEx", PyOpenKey, METH_VARARGS, OpenKeyEx_doc}, + {"OpenKey", (PyCFunction)PyOpenKey, METH_VARARGS | METH_KEYWORDS, + OpenKey_doc}, + {"OpenKeyEx", (PyCFunction)PyOpenKey, METH_VARARGS | METH_KEYWORDS, + OpenKeyEx_doc}, {"QueryValue", PyQueryValue, METH_VARARGS, QueryValue_doc}, {"QueryValueEx", PyQueryValueEx, METH_VARARGS, QueryValueEx_doc}, {"QueryInfoKey", PyQueryInfoKey, METH_VARARGS, QueryInfoKey_doc}, From python-checkins at python.org Mon Sep 27 20:14:43 2010 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 27 Sep 2010 20:14:43 +0200 (CEST) Subject: [Python-checkins] r85034 - in python/branches/release31-maint: Lib/test/test_socket.py Misc/NEWS Modules/socketmodule.c Message-ID: <20100927181443.4EC6CEEA58@mail.python.org> Author: antoine.pitrou Date: Mon Sep 27 20:14:43 2010 New Revision: 85034 Log: Merged revisions 85032 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85032 | antoine.pitrou | 2010-09-27 19:52:25 +0200 (lun., 27 sept. 2010) | 6 lines Issue #9950: Fix socket.sendall() crash or misbehaviour when a signal is received. Now sendall() properly calls signal handlers if necessary, and retries sending if these returned successfully, including on sockets with a timeout. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_socket.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/socketmodule.c Modified: python/branches/release31-maint/Lib/test/test_socket.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_socket.py (original) +++ python/branches/release31-maint/Lib/test/test_socket.py Mon Sep 27 20:14:43 2010 @@ -17,6 +17,7 @@ import contextlib from weakref import proxy import signal +import math def try_address(host, port=0, family=socket.AF_INET): """Try to bind a socket on the given host:port and return True @@ -607,6 +608,42 @@ socket.AI_PASSIVE) + def check_sendall_interrupted(self, with_timeout): + # socketpair() is not stricly required, but it makes things easier. + if not hasattr(signal, 'alarm') or not hasattr(socket, 'socketpair'): + self.skipTest("signal.alarm and socket.socketpair required for this test") + # Our signal handlers clobber the C errno by calling a math function + # with an invalid domain value. + def ok_handler(*args): + self.assertRaises(ValueError, math.acosh, 0) + def raising_handler(*args): + self.assertRaises(ValueError, math.acosh, 0) + 1 // 0 + c, s = socket.socketpair() + old_alarm = signal.signal(signal.SIGALRM, raising_handler) + try: + if with_timeout: + # Just above the one second minimum for signal.alarm + c.settimeout(1.5) + with self.assertRaises(ZeroDivisionError): + signal.alarm(1) + c.sendall(b"x" * (1024**2)) + if with_timeout: + signal.signal(signal.SIGALRM, ok_handler) + signal.alarm(1) + self.assertRaises(socket.timeout, c.sendall, b"x" * (1024**2)) + finally: + signal.signal(signal.SIGALRM, old_alarm) + c.close() + s.close() + + def test_sendall_interrupted(self): + self.check_sendall_interrupted(False) + + def test_sendall_interrupted_with_timeout(self): + self.check_sendall_interrupted(True) + + class BasicTCPTest(SocketConnectedTest): def __init__(self, methodName='runTest'): Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Mon Sep 27 20:14:43 2010 @@ -121,6 +121,11 @@ Library ------- +- Issue #9950: Fix socket.sendall() crash or misbehaviour when a signal is + received. Now sendall() properly calls signal handlers if necessary, + and retries sending if these returned successfully, including on sockets + with a timeout. + - Issue #9936: Fixed executable lines' search in the trace module. - Issue #9928: Properly initialize the types exported by the bz2 module. Modified: python/branches/release31-maint/Modules/socketmodule.c ============================================================================== --- python/branches/release31-maint/Modules/socketmodule.c (original) +++ python/branches/release31-maint/Modules/socketmodule.c Mon Sep 27 20:14:43 2010 @@ -2553,7 +2553,7 @@ { char *buf; Py_ssize_t len, n = -1; - int flags = 0, timeout; + int flags = 0, timeout, saved_errno; Py_buffer pbuf; if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags)) @@ -2566,29 +2566,44 @@ return select_error(); } - Py_BEGIN_ALLOW_THREADS do { + Py_BEGIN_ALLOW_THREADS timeout = internal_select(s, 1); n = -1; - if (timeout) - break; + if (!timeout) { #ifdef __VMS - n = sendsegmented(s->sock_fd, buf, len, flags); + n = sendsegmented(s->sock_fd, buf, len, flags); #else - n = send(s->sock_fd, buf, len, flags); + n = send(s->sock_fd, buf, len, flags); #endif - if (n < 0) - break; + } + Py_END_ALLOW_THREADS + if (timeout == 1) { + PyBuffer_Release(&pbuf); + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + /* PyErr_CheckSignals() might change errno */ + saved_errno = errno; + /* We must run our signal handlers before looping again. + send() can return a successful partial write when it is + interrupted, so we can't restrict ourselves to EINTR. */ + if (PyErr_CheckSignals()) { + PyBuffer_Release(&pbuf); + return NULL; + } + if (n < 0) { + /* If interrupted, try again */ + if (saved_errno == EINTR) + continue; + else + break; + } buf += n; len -= n; } while (len > 0); - Py_END_ALLOW_THREADS PyBuffer_Release(&pbuf); - if (timeout == 1) { - PyErr_SetString(socket_timeout, "timed out"); - return NULL; - } if (n < 0) return s->errorhandler(); From python-checkins at python.org Mon Sep 27 20:16:46 2010 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 27 Sep 2010 20:16:46 +0200 (CEST) Subject: [Python-checkins] r85035 - in python/branches/release27-maint: Lib/test/test_socket.py Misc/NEWS Modules/socketmodule.c Message-ID: <20100927181646.45383EEA62@mail.python.org> Author: antoine.pitrou Date: Mon Sep 27 20:16:46 2010 New Revision: 85035 Log: Merged revisions 85032 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85032 | antoine.pitrou | 2010-09-27 19:52:25 +0200 (lun., 27 sept. 2010) | 6 lines Issue #9950: Fix socket.sendall() crash or misbehaviour when a signal is received. Now sendall() properly calls signal handlers if necessary, and retries sending if these returned successfully, including on sockets with a timeout. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_socket.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/socketmodule.c Modified: python/branches/release27-maint/Lib/test/test_socket.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_socket.py (original) +++ python/branches/release27-maint/Lib/test/test_socket.py Mon Sep 27 20:16:46 2010 @@ -15,6 +15,7 @@ import contextlib from weakref import proxy import signal +import math def try_address(host, port=0, family=socket.AF_INET): """Try to bind a socket on the given host:port and return True @@ -611,6 +612,42 @@ socket.AI_PASSIVE) + def check_sendall_interrupted(self, with_timeout): + # socketpair() is not stricly required, but it makes things easier. + if not hasattr(signal, 'alarm') or not hasattr(socket, 'socketpair'): + self.skipTest("signal.alarm and socket.socketpair required for this test") + # Our signal handlers clobber the C errno by calling a math function + # with an invalid domain value. + def ok_handler(*args): + self.assertRaises(ValueError, math.acosh, 0) + def raising_handler(*args): + self.assertRaises(ValueError, math.acosh, 0) + 1 // 0 + c, s = socket.socketpair() + old_alarm = signal.signal(signal.SIGALRM, raising_handler) + try: + if with_timeout: + # Just above the one second minimum for signal.alarm + c.settimeout(1.5) + with self.assertRaises(ZeroDivisionError): + signal.alarm(1) + c.sendall(b"x" * (1024**2)) + if with_timeout: + signal.signal(signal.SIGALRM, ok_handler) + signal.alarm(1) + self.assertRaises(socket.timeout, c.sendall, b"x" * (1024**2)) + finally: + signal.signal(signal.SIGALRM, old_alarm) + c.close() + s.close() + + def test_sendall_interrupted(self): + self.check_sendall_interrupted(False) + + def test_sendall_interrupted_with_timeout(self): + self.check_sendall_interrupted(True) + + @unittest.skipUnless(thread, 'Threading required for this test.') class BasicTCPTest(SocketConnectedTest): Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Mon Sep 27 20:16:46 2010 @@ -48,6 +48,11 @@ Library ------- +- Issue #9950: Fix socket.sendall() crash or misbehaviour when a signal is + received. Now sendall() properly calls signal handlers if necessary, + and retries sending if these returned successfully, including on sockets + with a timeout. + - Issue #9947: logging: Fixed locking bug in stopListening. - Issue #9945: logging: Fixed locking bugs in addHandler/removeHandler. Modified: python/branches/release27-maint/Modules/socketmodule.c ============================================================================== --- python/branches/release27-maint/Modules/socketmodule.c (original) +++ python/branches/release27-maint/Modules/socketmodule.c Mon Sep 27 20:16:46 2010 @@ -2729,7 +2729,7 @@ sock_sendall(PySocketSockObject *s, PyObject *args) { char *buf; - int len, n = -1, flags = 0, timeout; + int len, n = -1, flags = 0, timeout, saved_errno; Py_buffer pbuf; if (!PyArg_ParseTuple(args, "s*|i:sendall", &pbuf, &flags)) @@ -2742,42 +2742,44 @@ return select_error(); } - Py_BEGIN_ALLOW_THREADS do { + Py_BEGIN_ALLOW_THREADS timeout = internal_select(s, 1); n = -1; - if (timeout) - break; + if (!timeout) { #ifdef __VMS - n = sendsegmented(s->sock_fd, buf, len, flags); + n = sendsegmented(s->sock_fd, buf, len, flags); #else - n = send(s->sock_fd, buf, len, flags); + n = send(s->sock_fd, buf, len, flags); #endif + } + Py_END_ALLOW_THREADS + if (timeout == 1) { + PyBuffer_Release(&pbuf); + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + /* PyErr_CheckSignals() might change errno */ + saved_errno = errno; + /* We must run our signal handlers before looping again. + send() can return a successful partial write when it is + interrupted, so we can't restrict ourselves to EINTR. */ + if (PyErr_CheckSignals()) { + PyBuffer_Release(&pbuf); + return NULL; + } if (n < 0) { -#ifdef EINTR - /* We must handle EINTR here as there is no way for - * the caller to know how much was sent otherwise. */ - if (errno == EINTR) { - /* Run signal handlers. If an exception was - * raised, abort and leave this socket in - * an unknown state. */ - if (PyErr_CheckSignals()) - return NULL; + /* If interrupted, try again */ + if (saved_errno == EINTR) continue; - } -#endif - break; + else + break; } buf += n; len -= n; } while (len > 0); - Py_END_ALLOW_THREADS PyBuffer_Release(&pbuf); - if (timeout == 1) { - PyErr_SetString(socket_timeout, "timed out"); - return NULL; - } if (n < 0) return s->errorhandler(); From python-checkins at python.org Mon Sep 27 21:16:13 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 27 Sep 2010 21:16:13 +0200 (CEST) Subject: [Python-checkins] r85036 - python/branches/release27-maint Message-ID: <20100927191613.7AE48EEA88@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 27 21:16:07 2010 New Revision: 85036 Log: Blocked revisions 84762,84876,84897,84902,84949 via svnmerge ........ r84762 | hirokazu.yamamoto | 2010-09-13 14:59:38 +0900 | 1 line Updated PC/VS8.0 with PCBuild/vs9to8.py. ........ r84876 | hirokazu.yamamoto | 2010-09-18 13:42:41 +0900 | 1 line In Python3000, Tkinter was renamed to tkinter. And print is now function. ........ r84897 | hirokazu.yamamoto | 2010-09-19 17:31:01 +0900 | 1 line Set svn:ignore to folders. ........ r84902 | hirokazu.yamamoto | 2010-09-19 18:24:20 +0900 | 1 line Issue #9552: Avoid unnecessary rebuild of OpenSSL. (Windows) ........ r84949 | hirokazu.yamamoto | 2010-09-22 01:05:47 +0900 | 1 line Revert r84902 before committing better solution. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Mon Sep 27 21:21:57 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 27 Sep 2010 21:21:57 +0200 (CEST) Subject: [Python-checkins] r85037 - python/branches/release31-maint Message-ID: <20100927192157.6D097EEA79@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 27 21:21:57 2010 New Revision: 85037 Log: Blocked revisions 84762,84897,84902,84949 via svnmerge ........ r84762 | hirokazu.yamamoto | 2010-09-13 14:59:38 +0900 | 1 line Updated PC/VS8.0 with PCBuild/vs9to8.py. ........ r84897 | hirokazu.yamamoto | 2010-09-19 17:31:01 +0900 | 1 line Set svn:ignore to folders. ........ r84902 | hirokazu.yamamoto | 2010-09-19 18:24:20 +0900 | 1 line Issue #9552: Avoid unnecessary rebuild of OpenSSL. (Windows) ........ r84949 | hirokazu.yamamoto | 2010-09-22 01:05:47 +0900 | 1 line Revert r84902 before committing better solution. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Mon Sep 27 21:25:55 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 27 Sep 2010 21:25:55 +0200 (CEST) Subject: [Python-checkins] r85038 - python/branches/py3k-ttk-debug-on-xp5 Message-ID: <20100927192555.A9E56EE998@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 27 21:25:55 2010 New Revision: 85038 Log: Temporary branch to investigate test_ttk_guionly hang on XP-5 buildbot. Added: python/branches/py3k-ttk-debug-on-xp5/ - copied from r85037, /python/branches/py3k/ From python-checkins at python.org Mon Sep 27 21:36:11 2010 From: python-checkins at python.org (brett.cannon) Date: Mon, 27 Sep 2010 21:36:11 +0200 (CEST) Subject: [Python-checkins] r85039 - peps/trunk/pep0/constants.py Message-ID: <20100927193611.C2DD5EEA1A@mail.python.org> Author: brett.cannon Date: Mon Sep 27 21:36:11 2010 New Revision: 85039 Log: Add in a dummy Version value for PEP 0 to make pep0 code happy. Modified: peps/trunk/pep0/constants.py Modified: peps/trunk/pep0/constants.py ============================================================================== --- peps/trunk/pep0/constants.py (original) +++ peps/trunk/pep0/constants.py Mon Sep 27 21:36:11 2010 @@ -6,6 +6,7 @@ header = u"""PEP: 0 Title: Index of Python Enhancement Proposals (PEPs) Last-Modified: %s +Version: N/A Author: David Goodger , Barry Warsaw Status: Active From python-checkins at python.org Mon Sep 27 22:47:22 2010 From: python-checkins at python.org (nick.coghlan) Date: Mon, 27 Sep 2010 22:47:22 +0200 (CEST) Subject: [Python-checkins] r85040 - in peps/trunk: pep-0333.txt pep-3333.txt Message-ID: <20100927204722.8DA5BEEA4B@mail.python.org> Author: nick.coghlan Date: Mon Sep 27 22:47:22 2010 New Revision: 85040 Log: Attempt to trigger rebuild of recently modified PEPs after failure of PEP 0 creation Modified: peps/trunk/pep-0333.txt peps/trunk/pep-3333.txt Modified: peps/trunk/pep-0333.txt ============================================================================== --- peps/trunk/pep-0333.txt (original) +++ peps/trunk/pep-0333.txt Mon Sep 27 22:47:22 2010 @@ -8,7 +8,7 @@ Type: Informational Content-Type: text/x-rst Created: 07-Dec-2003 -Post-History: 07-Dec-2003, 08-Aug-2004, 20-Aug-2004, 27-Aug-2004 +Post-History: 07-Dec-2003, 08-Aug-2004, 20-Aug-2004, 27-Aug-2004, 27-Sep-2010 Replaced-By: 3333 Modified: peps/trunk/pep-3333.txt ============================================================================== --- peps/trunk/pep-3333.txt (original) +++ peps/trunk/pep-3333.txt Mon Sep 27 22:47:22 2010 @@ -8,7 +8,7 @@ Type: Informational Content-Type: text/x-rst Created: 26-Sep-2010 -Post-History: 26-Sep-2010 +Post-History: 27-Sep-2010 Replaces: 333 From python-checkins at python.org Mon Sep 27 22:49:32 2010 From: python-checkins at python.org (martin.v.loewis) Date: Mon, 27 Sep 2010 22:49:32 +0200 (CEST) Subject: [Python-checkins] r85041 - peps/trunk/genpepindex.py Message-ID: <20100927204932.48635EE9F2@mail.python.org> Author: martin.v.loewis Date: Mon Sep 27 22:49:32 2010 New Revision: 85041 Log: Don't read pep-0000 when generating it. Modified: peps/trunk/genpepindex.py Modified: peps/trunk/genpepindex.py ============================================================================== --- peps/trunk/genpepindex.py (original) +++ peps/trunk/genpepindex.py Mon Sep 27 22:49:32 2010 @@ -35,6 +35,8 @@ peps = [] if os.path.isdir(path): for file_path in os.listdir(path): + if file_path == 'pep-0000.txt': + continue abs_file_path = os.path.join(path, file_path) if not os.path.isfile(abs_file_path): continue From python-checkins at python.org Mon Sep 27 22:54:00 2010 From: python-checkins at python.org (brett.cannon) Date: Mon, 27 Sep 2010 22:54:00 +0200 (CEST) Subject: [Python-checkins] r85042 - peps/trunk/pep0/constants.py Message-ID: <20100927205400.A0FCEEE9DD@mail.python.org> Author: brett.cannon Date: Mon Sep 27 22:54:00 2010 New Revision: 85042 Log: Put Version before Last-Modified in PEP 0. Modified: peps/trunk/pep0/constants.py Modified: peps/trunk/pep0/constants.py ============================================================================== --- peps/trunk/pep0/constants.py (original) +++ peps/trunk/pep0/constants.py Mon Sep 27 22:54:00 2010 @@ -5,8 +5,8 @@ header = u"""PEP: 0 Title: Index of Python Enhancement Proposals (PEPs) -Last-Modified: %s Version: N/A +Last-Modified: %s Author: David Goodger , Barry Warsaw Status: Active From python-checkins at python.org Mon Sep 27 23:08:38 2010 From: python-checkins at python.org (brett.cannon) Date: Mon, 27 Sep 2010 23:08:38 +0200 (CEST) Subject: [Python-checkins] r85043 - in python/branches/py3k: Misc/NEWS Python/bltinmodule.c Message-ID: <20100927210838.C47C0EE986@mail.python.org> Author: brett.cannon Date: Mon Sep 27 23:08:38 2010 New Revision: 85043 Log: Since __import__ is not designed for general use, have its docstring point people towards importlib.import_module(). Closes issue #7397. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Python/bltinmodule.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 27 23:08:38 2010 @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #7397: Mention that importlib.import_module() is probably what someone + really wants to be using in __import__'s docstring. + - Issue #8521: Allow CreateKeyEx, OpenKeyEx, and DeleteKeyEx functions of winreg to use named arguments. Modified: python/branches/py3k/Python/bltinmodule.c ============================================================================== --- python/branches/py3k/Python/bltinmodule.c (original) +++ python/branches/py3k/Python/bltinmodule.c Mon Sep 27 23:08:38 2010 @@ -173,8 +173,12 @@ PyDoc_STRVAR(import_doc, "__import__(name, globals={}, locals={}, fromlist=[], level=-1) -> module\n\ \n\ -Import a module. The globals are only used to determine the context;\n\ -they are not modified. The locals are currently unused. The fromlist\n\ +Import a module. Because this function is meant for use by the Python\n\ +interpreter and not for general use it is better to use\n\ +importlib.import_module() to programmatically import a module.\n\ +\n\ +The globals argument is only used to determine the context;\n\ +they are not modified. The locals argument is unused. The fromlist\n\ should be a list of names to emulate ``from name import ...'', or an\n\ empty list to emulate ``import name''.\n\ When importing a module from a package, note that __import__('A.B', ...)\n\ From python-checkins at python.org Mon Sep 27 23:38:56 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 27 Sep 2010 23:38:56 +0200 (CEST) Subject: [Python-checkins] r85044 - python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/test.bat Message-ID: <20100927213856.3A936EEA1A@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 27 23:38:56 2010 New Revision: 85044 Log: Run test_ttk_guionly in verbose mode. Modified: python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/test.bat Modified: python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/test.bat ============================================================================== --- python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/test.bat (original) +++ python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/test.bat Mon Sep 27 23:38:56 2010 @@ -1,4 +1,5 @@ @rem Used by the buildbot "test" step. cd PCbuild +python_d -m test.test_ttk_guionly call rt.bat -d -q -uall -rw -n From python-checkins at python.org Mon Sep 27 23:46:14 2010 From: python-checkins at python.org (vinay.sajip) Date: Mon, 27 Sep 2010 23:46:14 +0200 (CEST) Subject: [Python-checkins] r85045 - python/branches/release31-maint/Lib/logging/__init__.py Message-ID: <20100927214614.A6E96EEA1A@mail.python.org> Author: vinay.sajip Date: Mon Sep 27 23:46:14 2010 New Revision: 85045 Log: Issue #9945: logging: backported locking fixes from py3k. Modified: python/branches/release31-maint/Lib/logging/__init__.py Modified: python/branches/release31-maint/Lib/logging/__init__.py ============================================================================== --- python/branches/release31-maint/Lib/logging/__init__.py (original) +++ python/branches/release31-maint/Lib/logging/__init__.py Mon Sep 27 23:46:14 2010 @@ -1180,20 +1180,23 @@ """ Add the specified handler to this logger. """ - if not (hdlr in self.handlers): - self.handlers.append(hdlr) + _acquireLock() + try: + if not (hdlr in self.handlers): + self.handlers.append(hdlr) + finally: + _releaseLock() def removeHandler(self, hdlr): """ Remove the specified handler from this logger. """ - if hdlr in self.handlers: - #hdlr.close() - hdlr.acquire() - try: + _acquireLock() + try: + if hdlr in self.handlers: self.handlers.remove(hdlr) - finally: - hdlr.release() + finally: + _releaseLock() def callHandlers(self, record): """ @@ -1389,26 +1392,28 @@ using sys.stdout or sys.stderr), whereas FileHandler closes its stream when the handler is closed. """ - if len(root.handlers) == 0: - filename = kwargs.get("filename") - if filename: - mode = kwargs.get("filemode", 'a') - hdlr = FileHandler(filename, mode) - else: - stream = kwargs.get("stream") - hdlr = StreamHandler(stream) - fs = kwargs.get("format", BASIC_FORMAT) - dfs = kwargs.get("datefmt", None) - fmt = Formatter(fs, dfs) - hdlr.setFormatter(fmt) - root.addHandler(hdlr) - level = kwargs.get("level") - if level is not None: - if str(level) == level: # If a string was passed, do more checks - if level not in _levelNames: - raise ValueError("Unknown level: %r" % level) - level = _levelNames[level] - root.setLevel(level) + # Add thread safety in case someone mistakenly calls + # basicConfig() from multiple threads + _acquireLock() + try: + if len(root.handlers) == 0: + filename = kwargs.get("filename") + if filename: + mode = kwargs.get("filemode", 'a') + hdlr = FileHandler(filename, mode) + else: + stream = kwargs.get("stream") + hdlr = StreamHandler(stream) + fs = kwargs.get("format", BASIC_FORMAT) + dfs = kwargs.get("datefmt", None) + fmt = Formatter(fs, dfs) + hdlr.setFormatter(fmt) + root.addHandler(hdlr) + level = kwargs.get("level") + if level is not None: + root.setLevel(level) + finally: + _releaseLock() #--------------------------------------------------------------------------- # Utility functions at module level. @@ -1426,15 +1431,6 @@ else: return root -#def getRootLogger(): -# """ -# Return the root logger. -# -# Note that getLogger('') now does the same thing, so this function is -# deprecated and may disappear in the future. -# """ -# return root - def critical(msg, *args, **kwargs): """ Log a message with severity 'CRITICAL' on the root logger. @@ -1543,9 +1539,15 @@ a NullHandler and add it to the top-level logger of the library module or package. """ + def handle(self, record): + pass + def emit(self, record): pass + def createLock(self): + self.lock = None + # Warnings integration _warnings_showwarning = None From python-checkins at python.org Mon Sep 27 23:51:36 2010 From: python-checkins at python.org (vinay.sajip) Date: Mon, 27 Sep 2010 23:51:36 +0200 (CEST) Subject: [Python-checkins] r85046 - python/branches/release31-maint/Lib/logging/config.py Message-ID: <20100927215136.A50A2EE98C@mail.python.org> Author: vinay.sajip Date: Mon Sep 27 23:51:36 2010 New Revision: 85046 Log: Issue #9947: logging: backported locking fix from py3k. Modified: python/branches/release31-maint/Lib/logging/config.py Modified: python/branches/release31-maint/Lib/logging/config.py ============================================================================== --- python/branches/release31-maint/Lib/logging/config.py (original) +++ python/branches/release31-maint/Lib/logging/config.py Mon Sep 27 23:51:36 2010 @@ -19,7 +19,7 @@ is based on PEP 282 and comments thereto in comp.lang.python, and influenced by Apache's log4j system. -Copyright (C) 2001-2008 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2010 Vinay Sajip. All Rights Reserved. To use, simply 'import logging' and log away! """ @@ -370,8 +370,10 @@ Stop the listening server which was created with a call to listen(). """ global _listener - if _listener: - logging._acquireLock() - _listener.abort = 1 - _listener = None + logging._acquireLock() + try: + if _listener: + _listener.abort = 1 + _listener = None + finally: logging._releaseLock() From solipsis at pitrou.net Tue Sep 28 04:48:27 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 28 Sep 2010 04:48:27 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r85043): sum=5 Message-ID: py3k results for svn r85043 (hg cset 9dcc582d6cff) -------------------------------------------------- test_robotparser leaked [5, 0, 0] references, sum=5 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogPUO7gB', '-x'] From python-checkins at python.org Tue Sep 28 09:03:40 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 09:03:40 +0200 (CEST) Subject: [Python-checkins] r85047 - python/branches/py3k/PC/VS8.0/bz2.vcproj Message-ID: <20100928070340.D18CCEE9A6@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 09:03:40 2010 New Revision: 85047 Log: Updated VS8.0 bz2.vcproj with PCBuild/vs9to8.py. Modified: python/branches/py3k/PC/VS8.0/bz2.vcproj Modified: python/branches/py3k/PC/VS8.0/bz2.vcproj ============================================================================== --- python/branches/py3k/PC/VS8.0/bz2.vcproj (original) +++ python/branches/py3k/PC/VS8.0/bz2.vcproj Tue Sep 28 09:03:40 2010 @@ -43,6 +43,7 @@ + + + + + + + + + + + + + + + + + + + + + + From python-checkins at python.org Tue Sep 28 09:22:27 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 28 Sep 2010 09:22:27 +0200 (CEST) Subject: [Python-checkins] r85048 - in python/branches/py3k: Misc/NEWS Modules/mathmodule.c Message-ID: <20100928072227.89E72EE9A6@mail.python.org> Author: mark.dickinson Date: Tue Sep 28 09:22:27 2010 New Revision: 85048 Log: Issue #9599: Tweak loghelper algorithm to return slightly improved results for powers of 2. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/mathmodule.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 28 09:22:27 2010 @@ -2201,6 +2201,9 @@ Extension Modules ----------------- +- Issue #9959: Tweak formula used for computing math.log of an integer, + making it marginally more accurate for exact powers of 2. + - Issue #9422: Fix memory leak when re-initializing a struct.Struct object. - Issue #7900: The getgroups(2) system call on MacOSX behaves rather oddly Modified: python/branches/py3k/Modules/mathmodule.c ============================================================================== --- python/branches/py3k/Modules/mathmodule.c (original) +++ python/branches/py3k/Modules/mathmodule.c Tue Sep 28 09:22:27 2010 @@ -1572,12 +1572,14 @@ "math domain error"); return NULL; } - /* Special case for log(1), to make sure we get an - exact result there. */ - if (e == 1 && x == 0.5) - return PyFloat_FromDouble(0.0); - /* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */ - x = func(x) + func(2.0) * e; + /* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. + + It's slightly better to compute the log as log(2 * x) + log(2) * (e + - 1): then when 'arg' is a power of 2, 2**k say, this gives us 0.0 + + log(2) * k instead of log(0.5) + log(2)*(k+1), and so marginally + increases the chances of log(arg, 2) returning the correct result. + */ + x = func(2.0 * x) + func(2.0) * (e - 1); return PyFloat_FromDouble(x); } From python-checkins at python.org Tue Sep 28 09:32:05 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 09:32:05 +0200 (CEST) Subject: [Python-checkins] r85049 - python/branches/release27-maint-ttk-debug-on-xp5 Message-ID: <20100928073205.BDC21EE9BD@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 09:32:05 2010 New Revision: 85049 Log: Temporary branch to investigate test_ttk_guionly hang on XP-5 buildbot. Added: python/branches/release27-maint-ttk-debug-on-xp5/ - copied from r85048, /python/branches/release27-maint/ From python-checkins at python.org Tue Sep 28 09:35:56 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 09:35:56 +0200 (CEST) Subject: [Python-checkins] r85050 - python/branches/release27-maint/Tools/buildbot/test.bat Message-ID: <20100928073556.CE1A0FC92@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 09:35:56 2010 New Revision: 85050 Log: Run test_ttk_guionly in verbose mode. Modified: python/branches/release27-maint/Tools/buildbot/test.bat Modified: python/branches/release27-maint/Tools/buildbot/test.bat ============================================================================== --- python/branches/release27-maint/Tools/buildbot/test.bat (original) +++ python/branches/release27-maint/Tools/buildbot/test.bat Tue Sep 28 09:35:56 2010 @@ -1,3 +1,4 @@ @rem Used by the buildbot "test" step. cd PCbuild -call rt.bat -d -q -uall -rw +python_d -m test.test_ttk_guionly +call rt.bat -d -q -uall -rw -x test.test_ttk_guionly From python-checkins at python.org Tue Sep 28 10:19:18 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 10:19:18 +0200 (CEST) Subject: [Python-checkins] r85051 - python/branches/release27-maint/Tools/buildbot/test.bat Message-ID: <20100928081918.54032EE986@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 10:19:18 2010 New Revision: 85051 Log: Sorry, I committed into wrong branch. So reverted it. Modified: python/branches/release27-maint/Tools/buildbot/test.bat Modified: python/branches/release27-maint/Tools/buildbot/test.bat ============================================================================== --- python/branches/release27-maint/Tools/buildbot/test.bat (original) +++ python/branches/release27-maint/Tools/buildbot/test.bat Tue Sep 28 10:19:18 2010 @@ -1,4 +1,3 @@ @rem Used by the buildbot "test" step. cd PCbuild -python_d -m test.test_ttk_guionly -call rt.bat -d -q -uall -rw -x test.test_ttk_guionly +call rt.bat -d -q -uall -rw From python-checkins at python.org Tue Sep 28 10:43:56 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 10:43:56 +0200 (CEST) Subject: [Python-checkins] r85052 - python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/test.bat Message-ID: <20100928084356.522ABEEA78@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 10:43:56 2010 New Revision: 85052 Log: Run test_ttk_guionly in verbose mode. Modified: python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/test.bat Modified: python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/test.bat ============================================================================== --- python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/test.bat (original) +++ python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/test.bat Tue Sep 28 10:43:56 2010 @@ -1,3 +1,4 @@ @rem Used by the buildbot "test" step. cd PCbuild -call rt.bat -d -q -uall -rw +call rt.bat -d -q -uall -rw -v test_ttk_guionly +call rt.bat -d -q -uall -rw -x test_ttk_guionly From python-checkins at python.org Tue Sep 28 10:47:57 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 10:47:57 +0200 (CEST) Subject: [Python-checkins] r85053 - python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/test.bat Message-ID: <20100928084757.28B45EEA5D@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 10:47:57 2010 New Revision: 85053 Log: Fix to find tcl/tk dll. Modified: python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/test.bat Modified: python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/test.bat ============================================================================== --- python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/test.bat (original) +++ python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/test.bat Tue Sep 28 10:47:57 2010 @@ -1,5 +1,5 @@ @rem Used by the buildbot "test" step. cd PCbuild -python_d -m test.test_ttk_guionly -call rt.bat -d -q -uall -rw -n +call rt.bat -d -q -uall -rw -n -v test_ttk_guionly +call rt.bat -d -q -uall -rw -n -x test_ttk_guionly From python-checkins at python.org Tue Sep 28 11:11:21 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 11:11:21 +0200 (CEST) Subject: [Python-checkins] r85054 - python/branches/py3k/Tools/buildbot/test.bat Message-ID: <20100928091121.EDC15FA6A@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 11:11:21 2010 New Revision: 85054 Log: Run test_ttk_guionly in verbose mode. (This commit is temporary) Modified: python/branches/py3k/Tools/buildbot/test.bat Modified: python/branches/py3k/Tools/buildbot/test.bat ============================================================================== --- python/branches/py3k/Tools/buildbot/test.bat (original) +++ python/branches/py3k/Tools/buildbot/test.bat Tue Sep 28 11:11:21 2010 @@ -1,4 +1,5 @@ @rem Used by the buildbot "test" step. cd PCbuild -call rt.bat -d -q -uall -rw -n +call rt.bat -d -q -uall -rw -n -v test_ttk_guionly +call rt.bat -d -q -uall -rw -n -x test_ttk_guionly From python-checkins at python.org Tue Sep 28 11:46:48 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 11:46:48 +0200 (CEST) Subject: [Python-checkins] r85055 - python/branches/py3k/Tools/buildbot/test.bat Message-ID: <20100928094648.662DAF962@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 11:46:48 2010 New Revision: 85055 Log: Changed order to see other test results. (Also temporary commit) Modified: python/branches/py3k/Tools/buildbot/test.bat Modified: python/branches/py3k/Tools/buildbot/test.bat ============================================================================== --- python/branches/py3k/Tools/buildbot/test.bat (original) +++ python/branches/py3k/Tools/buildbot/test.bat Tue Sep 28 11:46:48 2010 @@ -1,5 +1,5 @@ @rem Used by the buildbot "test" step. cd PCbuild -call rt.bat -d -q -uall -rw -n -v test_ttk_guionly call rt.bat -d -q -uall -rw -n -x test_ttk_guionly +call rt.bat -d -q -uall -rw -n -v test_ttk_guionly From python-checkins at python.org Tue Sep 28 13:53:21 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 13:53:21 +0200 (CEST) Subject: [Python-checkins] r85056 - python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/clean.bat Message-ID: <20100928115321.51255EEA66@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 13:53:21 2010 New Revision: 85056 Log: Try to fix "another process is using files" error in *clean* faze. Modified: python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/clean.bat Modified: python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/clean.bat ============================================================================== --- python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/clean.bat (original) +++ python/branches/py3k-ttk-debug-on-xp5/Tools/buildbot/clean.bat Tue Sep 28 13:53:21 2010 @@ -3,5 +3,7 @@ @echo Deleting .pyc/.pyo files ... del /s Lib\*.pyc Lib\*.pyo cd PCbuild +vcbuild /useenv kill_python.vcproj "Release|Win32" && kill_python.exe +vcbuild /useenv kill_python.vcproj "Debug|Win32" && kill_python_d.exe vcbuild /clean pcbuild.sln "Release|Win32" vcbuild /clean pcbuild.sln "Debug|Win32" From python-checkins at python.org Tue Sep 28 14:22:07 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 14:22:07 +0200 (CEST) Subject: [Python-checkins] r85057 - in python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot: build.bat clean.bat Message-ID: <20100928122207.D8723F844@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 14:22:07 2010 New Revision: 85057 Log: Try to fix "another process is using files" error in *clean* faze. Modified: python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/build.bat python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/clean.bat Modified: python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/build.bat ============================================================================== --- python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/build.bat (original) +++ python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/build.bat Tue Sep 28 14:22:07 2010 @@ -2,6 +2,5 @@ cmd /c Tools\buildbot\external.bat call "%VS90COMNTOOLS%vsvars32.bat" cmd /c Tools\buildbot\clean.bat -vcbuild /useenv PCbuild\kill_python.vcproj "Debug|Win32" && PCbuild\kill_python_d.exe vcbuild /useenv PCbuild\pcbuild.sln "Debug|Win32" Modified: python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/clean.bat ============================================================================== --- python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/clean.bat (original) +++ python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/clean.bat Tue Sep 28 14:22:07 2010 @@ -1,5 +1,6 @@ @rem Used by the buildbot "clean" step. call "%VS90COMNTOOLS%vsvars32.bat" +vcbuild /useenv PCbuild\kill_python.vcproj "Debug|Win32" && PCbuild\kill_python_d.exe @echo Deleting .pyc/.pyo files ... del /s Lib\*.pyc Lib\*.pyo cd PCbuild From python-checkins at python.org Tue Sep 28 15:10:15 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 15:10:15 +0200 (CEST) Subject: [Python-checkins] r85058 - in python/branches/release27-maint-ttk-debug-on-xp5: Lib/lib-tk/test/test_ttk/test_extensions.py Tools/buildbot/test.bat Message-ID: <20100928131015.06231EE9C3@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 15:10:14 2010 New Revision: 85058 Log: Try to determine where script hangs. Modified: python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/test.bat Modified: python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py ============================================================================== --- python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py (original) +++ python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Tue Sep 28 15:10:14 2010 @@ -2,6 +2,9 @@ import unittest import Tkinter import ttk +import threading +import thread +import time from test.test_support import requires, run_unittest import support @@ -17,7 +20,7 @@ support.root_withdraw() - def test_widget_destroy(self): + def _test_widget_destroy(self): # automatically created variable x = ttk.LabeledScale() var = x._variable._name @@ -48,7 +51,7 @@ self.assertFalse(sys.last_type == Tkinter.TclError) - def test_initialization(self): + def _test_initialization(self): # master passing x = ttk.LabeledScale() self.assertEqual(x.master, Tkinter._default_root) @@ -104,6 +107,13 @@ def test_horizontal_range(self): + def func(): + time.sleep(60 * 5) # XXX: adjust here + thread.interrupt_main() + + t = threading.Thread(target=func) + t.start() + lscale = ttk.LabeledScale(from_=0, to=10) lscale.pack() lscale.wait_visibility() @@ -132,8 +142,9 @@ lscale.destroy() + t.join() - def test_variable_change(self): + def _test_variable_change(self): x = ttk.LabeledScale() x.pack() x.wait_visibility() @@ -160,7 +171,7 @@ x.destroy() - def test_resize(self): + def _test_resize(self): x = ttk.LabeledScale() x.pack(expand=True, fill='both') x.wait_visibility() @@ -268,7 +279,8 @@ optmenu.destroy() -tests_gui = (LabeledScaleTest, OptionMenuTest) +#tests_gui = (LabeledScaleTest, OptionMenuTest) +tests_gui = (LabeledScaleTest,) if __name__ == "__main__": run_unittest(*tests_gui) Modified: python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/test.bat ============================================================================== --- python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/test.bat (original) +++ python/branches/release27-maint-ttk-debug-on-xp5/Tools/buildbot/test.bat Tue Sep 28 15:10:14 2010 @@ -1,4 +1,4 @@ @rem Used by the buildbot "test" step. cd PCbuild -call rt.bat -d -q -uall -rw -v test_ttk_guionly +call rt.bat -d -uall -v test_ttk_guionly call rt.bat -d -q -uall -rw -x test_ttk_guionly From python-checkins at python.org Tue Sep 28 15:57:58 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 28 Sep 2010 15:57:58 +0200 (CEST) Subject: [Python-checkins] r85059 - in python/branches/py3k: Mac/BuildScript/scripts/postflight.patch-profile Misc/NEWS Message-ID: <20100928135758.C8485FA7C@mail.python.org> Author: ronald.oussoren Date: Tue Sep 28 15:57:58 2010 New Revision: 85059 Log: Add support for the ZSH shell to the "Update Shell Profile" script on MacOSX. Patch by Sylvain Mora, issue #9701. Modified: python/branches/py3k/Mac/BuildScript/scripts/postflight.patch-profile python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Mac/BuildScript/scripts/postflight.patch-profile ============================================================================== --- python/branches/py3k/Mac/BuildScript/scripts/postflight.patch-profile (original) +++ python/branches/py3k/Mac/BuildScript/scripts/postflight.patch-profile Tue Sep 28 15:57:58 2010 @@ -20,7 +20,7 @@ # Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH. BSH="`basename "${theShell}"`" case "${BSH}" in -bash|ksh|sh|*csh) +bash|ksh|sh|*csh|zsh) if [ `id -ur` = 0 ]; then P=`su - ${USER} -c 'echo A-X-4-X@@$PATH@@X-4-X-A' | grep 'A-X-4-X@@.*@@X-4-X-A' | sed -e 's/^A-X-4-X@@//g' -e 's/@@X-4-X-A$//g'` else @@ -76,6 +76,9 @@ PR="${HOME}/.bash_profile" fi ;; +zsh) + PR="${HOME}/.zprofile" + ;; *sh) PR="${HOME}/.profile" ;; Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 28 15:57:58 2010 @@ -286,6 +286,10 @@ - Issue #4026: Make the fcntl extension build under AIX. Patch by S?bastien Sabl?. +- Issue #9701: The MacOSX installer can patch the shell profile to ensure that + the "bin" directory inside the framework is on the shell's search path. This + feature now also supports the ZSH shell. + What's New in Python 3.2 Alpha 2? ================================= From python-checkins at python.org Tue Sep 28 16:01:49 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 28 Sep 2010 16:01:49 +0200 (CEST) Subject: [Python-checkins] r85060 - in python/branches/release27-maint: Mac/BuildScript/scripts/postflight.patch-profile Misc/NEWS Message-ID: <20100928140149.6B25BEE9A7@mail.python.org> Author: ronald.oussoren Date: Tue Sep 28 16:01:49 2010 New Revision: 85060 Log: Merged revisions 85059 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85059 | ronald.oussoren | 2010-09-28 15:57:58 +0200 (Tue, 28 Sep 2010) | 5 lines Add support for the ZSH shell to the "Update Shell Profile" script on MacOSX. Patch by Sylvain Mora, issue #9701. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Mac/BuildScript/scripts/postflight.patch-profile python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Mac/BuildScript/scripts/postflight.patch-profile ============================================================================== --- python/branches/release27-maint/Mac/BuildScript/scripts/postflight.patch-profile (original) +++ python/branches/release27-maint/Mac/BuildScript/scripts/postflight.patch-profile Tue Sep 28 16:01:49 2010 @@ -20,7 +20,7 @@ # Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH. BSH="`basename "${theShell}"`" case "${BSH}" in -bash|ksh|sh|*csh) +bash|ksh|sh|*csh|zsh) if [ `id -ur` = 0 ]; then P=`su - ${USER} -c 'echo A-X-4-X@@$PATH@@X-4-X-A' | grep 'A-X-4-X@@.*@@X-4-X-A' | sed -e 's/^A-X-4-X@@//g' -e 's/@@X-4-X-A$//g'` else @@ -76,6 +76,9 @@ PR="${HOME}/.bash_profile" fi ;; +zsh) + PR="${HOME}/.zprofile" + ;; *sh) PR="${HOME}/.profile" ;; Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Sep 28 16:01:49 2010 @@ -377,6 +377,11 @@ of the ``2to3`` tool, that is you can use ``2to3-2.7`` to select the Python 2.7 edition of 2to3. +- Issue #9701: The MacOSX installer can patch the shell profile to ensure that + the "bin" directory inside the framework is on the shell's search path. This + feature now also supports the ZSH shell. + + Tests ----- From python-checkins at python.org Tue Sep 28 16:04:41 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 28 Sep 2010 16:04:41 +0200 (CEST) Subject: [Python-checkins] r85061 - in python/branches/release31-maint: Mac/BuildScript/scripts/postflight.patch-profile Misc/NEWS Message-ID: <20100928140441.2EC26EEA68@mail.python.org> Author: ronald.oussoren Date: Tue Sep 28 16:04:41 2010 New Revision: 85061 Log: Merged revisions 85059 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85059 | ronald.oussoren | 2010-09-28 15:57:58 +0200 (Tue, 28 Sep 2010) | 5 lines Add support for the ZSH shell to the "Update Shell Profile" script on MacOSX. Patch by Sylvain Mora, issue #9701. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Mac/BuildScript/scripts/postflight.patch-profile python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Mac/BuildScript/scripts/postflight.patch-profile ============================================================================== --- python/branches/release31-maint/Mac/BuildScript/scripts/postflight.patch-profile (original) +++ python/branches/release31-maint/Mac/BuildScript/scripts/postflight.patch-profile Tue Sep 28 16:04:41 2010 @@ -20,7 +20,7 @@ # Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH. BSH="`basename "${theShell}"`" case "${BSH}" in -bash|ksh|sh|*csh) +bash|ksh|sh|*csh|zsh) if [ `id -ur` = 0 ]; then P=`su - ${USER} -c 'echo A-X-4-X@@$PATH@@X-4-X-A' | grep 'A-X-4-X@@.*@@X-4-X-A' | sed -e 's/^A-X-4-X@@//g' -e 's/@@X-4-X-A$//g'` else @@ -76,6 +76,9 @@ PR="${HOME}/.bash_profile" fi ;; +zsh) + PR="${HOME}/.zprofile" + ;; *sh) PR="${HOME}/.profile" ;; Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Sep 28 16:04:41 2010 @@ -605,6 +605,10 @@ variable anymore. It also forwards the LDFLAGS settings to the linker when building a shared library. +- Issue #9701: The MacOSX installer can patch the shell profile to ensure that + the "bin" directory inside the framework is on the shell's search path. This + feature now also supports the ZSH shell. + Tests ----- From python-checkins at python.org Tue Sep 28 16:38:31 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 28 Sep 2010 16:38:31 +0200 (CEST) Subject: [Python-checkins] r85062 - in python/branches/py3k: Misc/NEWS Modules/_scproxy.c Message-ID: <20100928143831.71C95EE9BD@mail.python.org> Author: ronald.oussoren Date: Tue Sep 28 16:38:31 2010 New Revision: 85062 Log: Fix for issue #9568. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/_scproxy.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 28 16:38:31 2010 @@ -263,6 +263,8 @@ - Issue #9323: Make test.regrtest.__file__ absolute, this was not always the case when running profile or trace, for example. +- Issue #9568: Fix test_urllib2_localnet on OS X 10.3. + Build ----- Modified: python/branches/py3k/Modules/_scproxy.c ============================================================================== --- python/branches/py3k/Modules/_scproxy.c (original) +++ python/branches/py3k/Modules/_scproxy.c Tue Sep 28 16:38:31 2010 @@ -80,7 +80,7 @@ v = PyBool_FromLong(cfnum_to_int32(aNum)); } } else { - v = PyBool_FromLong(1); + v = PyBool_FromLong(0); } if (v == NULL) goto error; From python-checkins at python.org Tue Sep 28 16:40:22 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 28 Sep 2010 16:40:22 +0200 (CEST) Subject: [Python-checkins] r85063 - in python/branches/release31-maint: Misc/NEWS Modules/_scproxy.c Message-ID: <20100928144022.AD8CEFAAC@mail.python.org> Author: ronald.oussoren Date: Tue Sep 28 16:40:22 2010 New Revision: 85063 Log: Merged revisions 85062 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85062 | ronald.oussoren | 2010-09-28 16:38:31 +0200 (Tue, 28 Sep 2010) | 3 lines Fix for issue #9568. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/_scproxy.c Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Sep 28 16:40:22 2010 @@ -650,6 +650,7 @@ - Issue #8193: Fix test_zlib failure with zlib 1.2.4. +- Issue #9568: Fix test_urllib2_localnet on OS X 10.3. Documentation ------------- Modified: python/branches/release31-maint/Modules/_scproxy.c ============================================================================== --- python/branches/release31-maint/Modules/_scproxy.c (original) +++ python/branches/release31-maint/Modules/_scproxy.c Tue Sep 28 16:40:22 2010 @@ -80,7 +80,7 @@ v = PyBool_FromLong(cfnum_to_int32(aNum)); } } else { - v = PyBool_FromLong(1); + v = PyBool_FromLong(0); } if (v == NULL) goto error; From python-checkins at python.org Tue Sep 28 16:43:59 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 28 Sep 2010 16:43:59 +0200 (CEST) Subject: [Python-checkins] r85064 - in python/branches/release27-maint: Mac/Modules/_scproxy.c Misc/NEWS Message-ID: <20100928144359.358ABF92D@mail.python.org> Author: ronald.oussoren Date: Tue Sep 28 16:43:59 2010 New Revision: 85064 Log: Merged revisions 85062 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85062 | ronald.oussoren | 2010-09-28 16:38:31 +0200 (Tue, 28 Sep 2010) | 3 lines Fix for issue #9568. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Mac/Modules/_scproxy.c python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Mac/Modules/_scproxy.c ============================================================================== --- python/branches/release27-maint/Mac/Modules/_scproxy.c (original) +++ python/branches/release27-maint/Mac/Modules/_scproxy.c Tue Sep 28 16:43:59 2010 @@ -73,7 +73,7 @@ v = PyBool_FromLong(cfnum_to_int32(aNum)); } } else { - v = PyBool_FromLong(1); + v = PyBool_FromLong(0); } if (v == NULL) goto error; Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Sep 28 16:43:59 2010 @@ -405,6 +405,8 @@ - Issue #8605: Skip test_gdb if Python is compiled with optimizations. +- Issue #9568: Fix test_urllib2_localnet on OS X 10.3. + Documentation ------------- From python-checkins at python.org Tue Sep 28 17:02:30 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 17:02:30 +0200 (CEST) Subject: [Python-checkins] r85065 - python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Message-ID: <20100928150230.5EA31FE0A@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 17:02:30 2010 New Revision: 85065 Log: Hmm, thread.interrupt_main cannot interrupt test_ttk_guionly. Try another method. Use sys.settrace to trace main thread. Modified: python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Modified: python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py ============================================================================== --- python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py (original) +++ python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Tue Sep 28 17:02:30 2010 @@ -2,6 +2,7 @@ import unittest import Tkinter import ttk +import collections import threading import thread import time @@ -11,6 +12,10 @@ requires('gui') +CODES_SIZE = 100 +WAIT = 10 * 60 # XXX: adjust here + + class LabeledScaleTest(unittest.TestCase): def setUp(self): @@ -107,13 +112,6 @@ def test_horizontal_range(self): - def func(): - time.sleep(60 * 5) # XXX: adjust here - thread.interrupt_main() - - t = threading.Thread(target=func) - t.start() - lscale = ttk.LabeledScale(from_=0, to=10) lscale.pack() lscale.wait_visibility() @@ -142,7 +140,6 @@ lscale.destroy() - t.join() def _test_variable_change(self): x = ttk.LabeledScale() @@ -283,4 +280,21 @@ tests_gui = (LabeledScaleTest,) if __name__ == "__main__": - run_unittest(*tests_gui) + codes = collections.deque([None] * CODES_SIZE) + def tracefunc(frame, event, arg): + codes.append(frame.f_code) + codes.popleft() + sys.settrace(tracefunc) # only main thread now + + def threadfunc(): + time.sleep(WAIT) + for code in codes: + if code is None: + continue + print(code) + t = threading.Thread(target=threadfunc) + t.start() + try: + run_unittest(*tests_gui) + finally: + t.join() From python-checkins at python.org Tue Sep 28 17:29:16 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 28 Sep 2010 17:29:16 +0200 (CEST) Subject: [Python-checkins] r85066 - python/branches/py3k/Doc/c-api/buffer.rst Message-ID: <20100928152916.E5F1BFA6A@mail.python.org> Author: antoine.pitrou Date: Tue Sep 28 17:29:16 2010 New Revision: 85066 Log: Issue #9970: improve C API documentation for memoryview objects Modified: python/branches/py3k/Doc/c-api/buffer.rst Modified: python/branches/py3k/Doc/c-api/buffer.rst ============================================================================== --- python/branches/py3k/Doc/c-api/buffer.rst (original) +++ python/branches/py3k/Doc/c-api/buffer.rst Tue Sep 28 17:29:16 2010 @@ -303,18 +303,40 @@ MemoryView objects ================== -A memoryview object exposes the C level buffer interface to Python. +A :class:`memoryview` object exposes the C level buffer interface as a +Python object which can then be passed around like any other object. -.. cfunction:: PyObject* PyMemoryView_FromObject(PyObject *obj) +.. cfunction:: PyObject *PyMemoryView_FromObject(PyObject *obj) - Return a memoryview object from an object that defines the buffer interface. + Create a memoryview object from an object that defines the buffer interface. -.. cfunction:: PyObject * PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order) +.. cfunction:: PyObject *PyMemoryView_FromBuffer(Py_buffer *view) - Return a memoryview object to a contiguous chunk of memory (in either - 'C' or 'F'ortran order) from an object that defines the buffer + Create a memoryview object wrapping the given buffer-info structure *view*. + The memoryview object then owns the buffer, which means you shouldn't + try to release it yourself: it will be released on deallocation of the + memoryview object. + + +.. cfunction:: PyObject *PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order) + + Create a memoryview object to a contiguous chunk of memory (in either + 'C' or 'F'ortran *order*) from an object that defines the buffer interface. If memory is contiguous, the memoryview object points to the original memory. Otherwise copy is made and the memoryview points to a new bytes object. + + +.. cfunction:: int PyMemoryView_Check(PyObject *obj) + + Return true if the object *obj* is a memoryview object. It is not + currently allowed to create subclasses of :class:`memoryview`. + + +.. cfunction:: Py_buffer *PyMemoryView_GET_BUFFER(PyObject *obj) + + Return a pointer to the buffer-info structure wrapped by the given + object. The object **must** be a memoryview instance; this macro doesn't + check its type, you must do it yourself or you will risk crashes. From python-checkins at python.org Tue Sep 28 17:33:25 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 28 Sep 2010 17:33:25 +0200 (CEST) Subject: [Python-checkins] r85067 - python/branches/release31-maint/Doc/c-api/buffer.rst Message-ID: <20100928153325.DFFACEEA04@mail.python.org> Author: antoine.pitrou Date: Tue Sep 28 17:33:25 2010 New Revision: 85067 Log: Merged revisions 85066 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85066 | antoine.pitrou | 2010-09-28 17:29:16 +0200 (mar., 28 sept. 2010) | 3 lines Issue #9970: improve C API documentation for memoryview objects ........ Modified: python/branches/release31-maint/Doc/c-api/buffer.rst Modified: python/branches/release31-maint/Doc/c-api/buffer.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/buffer.rst (original) +++ python/branches/release31-maint/Doc/c-api/buffer.rst Tue Sep 28 17:33:25 2010 @@ -303,18 +303,40 @@ MemoryView objects ================== -A memoryview object exposes the C level buffer interface to Python. +A :class:`memoryview` object exposes the C level buffer interface as a +Python object which can then be passed around like any other object. -.. cfunction:: PyObject* PyMemoryView_FromObject(PyObject *obj) +.. cfunction:: PyObject *PyMemoryView_FromObject(PyObject *obj) - Return a memoryview object from an object that defines the buffer interface. + Create a memoryview object from an object that defines the buffer interface. -.. cfunction:: PyObject * PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order) +.. cfunction:: PyObject *PyMemoryView_FromBuffer(Py_buffer *view) - Return a memoryview object to a contiguous chunk of memory (in either - 'C' or 'F'ortran order) from an object that defines the buffer + Create a memoryview object wrapping the given buffer-info structure *view*. + The memoryview object then owns the buffer, which means you shouldn't + try to release it yourself: it will be released on deallocation of the + memoryview object. + + +.. cfunction:: PyObject *PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order) + + Create a memoryview object to a contiguous chunk of memory (in either + 'C' or 'F'ortran *order*) from an object that defines the buffer interface. If memory is contiguous, the memoryview object points to the original memory. Otherwise copy is made and the memoryview points to a new bytes object. + + +.. cfunction:: int PyMemoryView_Check(PyObject *obj) + + Return true if the object *obj* is a memoryview object. It is not + currently allowed to create subclasses of :class:`memoryview`. + + +.. cfunction:: Py_buffer *PyMemoryView_GET_BUFFER(PyObject *obj) + + Return a pointer to the buffer-info structure wrapped by the given + object. The object **must** be a memoryview instance; this macro doesn't + check its type, you must do it yourself or you will risk crashes. From python-checkins at python.org Tue Sep 28 17:35:19 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 28 Sep 2010 17:35:19 +0200 (CEST) Subject: [Python-checkins] r85068 - in python/branches/release27-maint: Doc/c-api/buffer.rst Message-ID: <20100928153519.1E65AFE03@mail.python.org> Author: antoine.pitrou Date: Tue Sep 28 17:35:18 2010 New Revision: 85068 Log: Merged revisions 85066 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85066 | antoine.pitrou | 2010-09-28 17:29:16 +0200 (mar., 28 sept. 2010) | 3 lines Issue #9970: improve C API documentation for memoryview objects ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/c-api/buffer.rst Modified: python/branches/release27-maint/Doc/c-api/buffer.rst ============================================================================== --- python/branches/release27-maint/Doc/c-api/buffer.rst (original) +++ python/branches/release27-maint/Doc/c-api/buffer.rst Tue Sep 28 17:35:18 2010 @@ -304,14 +304,45 @@ MemoryView objects ================== -A memoryview object is an extended buffer object that could replace the buffer -object (but doesn't have to as that could be kept as a simple 1-d memoryview -object). It, unlike :ctype:`Py_buffer`, is a Python object (exposed as -:class:`memoryview` in :mod:`builtins`), so it can be used with Python code. +.. versionadded:: 2.7 -.. cfunction:: PyObject* PyMemoryView_FromObject(PyObject *obj) +A :class:`memoryview` object exposes the new C level buffer interface as a +Python object which can then be passed around like any other object. - Return a memoryview object from an object that defines the buffer interface. +.. cfunction:: PyObject *PyMemoryView_FromObject(PyObject *obj) + + Create a memoryview object from an object that defines the new buffer + interface. + + +.. cfunction:: PyObject *PyMemoryView_FromBuffer(Py_buffer *view) + + Create a memoryview object wrapping the given buffer-info structure *view*. + The memoryview object then owns the buffer, which means you shouldn't + try to release it yourself: it will be released on deallocation of the + memoryview object. + + +.. cfunction:: PyObject *PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order) + + Create a memoryview object to a contiguous chunk of memory (in either + 'C' or 'F'ortran *order*) from an object that defines the buffer + interface. If memory is contiguous, the memoryview object points to the + original memory. Otherwise copy is made and the memoryview points to a + new bytes object. + + +.. cfunction:: int PyMemoryView_Check(PyObject *obj) + + Return true if the object *obj* is a memoryview object. It is not + currently allowed to create subclasses of :class:`memoryview`. + + +.. cfunction:: Py_buffer *PyMemoryView_GET_BUFFER(PyObject *obj) + + Return a pointer to the buffer-info structure wrapped by the given + object. The object **must** be a memoryview instance; this macro doesn't + check its type, you must do it yourself or you will risk crashes. Old-style buffer objects From python-checkins at python.org Tue Sep 28 17:46:28 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 28 Sep 2010 17:46:28 +0200 (CEST) Subject: [Python-checkins] r85069 - python/branches/release27-maint/Doc/library/macosa.rst Message-ID: <20100928154628.92395EE99B@mail.python.org> Author: ronald.oussoren Date: Tue Sep 28 17:46:28 2010 New Revision: 85069 Log: Fix for issue 81275: don't say that the MacPython OSA modules will get new development in python 2.5 (!), but point users to the py-appscript project instead. Modified: python/branches/release27-maint/Doc/library/macosa.rst Modified: python/branches/release27-maint/Doc/library/macosa.rst ============================================================================== --- python/branches/release27-maint/Doc/library/macosa.rst (original) +++ python/branches/release27-maint/Doc/library/macosa.rst Tue Sep 28 17:46:28 2010 @@ -9,7 +9,8 @@ Architecture (OSA, also commonly referred to as AppleScript) for Python, allowing you to control scriptable applications from your Python program, and with a fairly pythonic interface. Development on this set of modules has -stopped, and a replacement is expected for Python 2.5. +stopped. For more up-to-date implementation of AppleScript support for Python, +see the third-party py-appscript project: . For a description of the various components of AppleScript and OSA, and to get an understanding of the architecture and terminology, you should read Apple's From python-checkins at python.org Tue Sep 28 19:55:53 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 19:55:53 +0200 (CEST) Subject: [Python-checkins] r85070 - python/branches/py3k/Tools/buildbot/test.bat Message-ID: <20100928175553.E1865EE9A5@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 19:55:53 2010 New Revision: 85070 Log: Reverted to r70917. Modified: python/branches/py3k/Tools/buildbot/test.bat Modified: python/branches/py3k/Tools/buildbot/test.bat ============================================================================== --- python/branches/py3k/Tools/buildbot/test.bat (original) +++ python/branches/py3k/Tools/buildbot/test.bat Tue Sep 28 19:55:53 2010 @@ -1,5 +1,4 @@ @rem Used by the buildbot "test" step. cd PCbuild -call rt.bat -d -q -uall -rw -n -x test_ttk_guionly -call rt.bat -d -q -uall -rw -n -v test_ttk_guionly +call rt.bat -d -q -uall -rw -n From python-checkins at python.org Tue Sep 28 20:29:57 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 20:29:57 +0200 (CEST) Subject: [Python-checkins] r85071 - in python/branches/py3k: PC/VS8.0/build_ssl.py PCbuild/build_ssl.py Message-ID: <20100928182957.EB0DEEE9D0@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 20:29:57 2010 New Revision: 85071 Log: Now perl path with spaces can be used. Modified: python/branches/py3k/PC/VS8.0/build_ssl.py python/branches/py3k/PCbuild/build_ssl.py Modified: python/branches/py3k/PC/VS8.0/build_ssl.py ============================================================================== --- python/branches/py3k/PC/VS8.0/build_ssl.py (original) +++ python/branches/py3k/PC/VS8.0/build_ssl.py Tue Sep 28 20:29:57 2010 @@ -8,7 +8,7 @@ # directory. It is likely you will already find the zlib library and # any other external packages there. # * Install ActivePerl and ensure it is somewhere on your path. -# * Run this script from the PCBuild directory. +# * Run this script from the PC/VS8.0 directory. # # it should configure and build SSL, then build the _ssl and _hashlib # Python extensions without intervention. @@ -46,7 +46,7 @@ # is available. def find_working_perl(perls): for perl in perls: - fh = os.popen(perl + ' -e "use Win32;"') + fh = os.popen('"%s" -e "use Win32;"' % perl) fh.read() rc = fh.close() if rc: @@ -196,12 +196,12 @@ # as "well known" locations perls = find_all_on_path("perl.exe", ["\\perl\\bin", "C:\\perl\\bin"]) perl = find_working_perl(perls) - if perl is None: + if perl: + print("Found a working perl at '%s'" % (perl,)) + else: print("No Perl installation was found. Existing Makefiles are used.") - - print("Found a working perl at '%s'" % (perl,)) sys.stdout.flush() - # Look for SSL 2 levels up from pcbuild - ie, same place zlib etc all live. + # Look for SSL 3 levels up from PC/VS8.0 - ie, same place zlib etc all live. ssl_dir = find_best_ssl_dir(("..\\..\\..",)) if ssl_dir is None: sys.exit(1) Modified: python/branches/py3k/PCbuild/build_ssl.py ============================================================================== --- python/branches/py3k/PCbuild/build_ssl.py (original) +++ python/branches/py3k/PCbuild/build_ssl.py Tue Sep 28 20:29:57 2010 @@ -46,7 +46,7 @@ # is available. def find_working_perl(perls): for perl in perls: - fh = os.popen(perl + ' -e "use Win32;"') + fh = os.popen('"%s" -e "use Win32;"' % perl) fh.read() rc = fh.close() if rc: @@ -196,10 +196,10 @@ # as "well known" locations perls = find_all_on_path("perl.exe", ["\\perl\\bin", "C:\\perl\\bin"]) perl = find_working_perl(perls) - if perl is None: + if perl: + print("Found a working perl at '%s'" % (perl,)) + else: print("No Perl installation was found. Existing Makefiles are used.") - - print("Found a working perl at '%s'" % (perl,)) sys.stdout.flush() # Look for SSL 2 levels up from pcbuild - ie, same place zlib etc all live. ssl_dir = find_best_ssl_dir(("..\\..",)) From python-checkins at python.org Tue Sep 28 20:36:05 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 20:36:05 +0200 (CEST) Subject: [Python-checkins] r85072 - python/branches/py3k/PC/VC6/build_ssl.py Message-ID: <20100928183605.3B7DFEEA10@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 20:36:04 2010 New Revision: 85072 Log: Updated PC/VC6 openssl build script. (for openssl-1.0.0a) Modified: python/branches/py3k/PC/VC6/build_ssl.py Modified: python/branches/py3k/PC/VC6/build_ssl.py ============================================================================== --- python/branches/py3k/PC/VC6/build_ssl.py (original) +++ python/branches/py3k/PC/VC6/build_ssl.py Tue Sep 28 20:36:04 2010 @@ -13,6 +13,11 @@ # it should configure and build SSL, then build the ssl Python extension # without intervention. +# Modified by Christian Heimes +# Now this script supports pre-generated makefiles and assembly files. +# Developers don't need an installation of Perl anymore to build Python. A svn +# checkout from our svn repository is enough. + import os, sys, re, shutil # Find all "foo.exe" files on the PATH. @@ -36,7 +41,7 @@ # is available. def find_working_perl(perls): for perl in perls: - fh = os.popen(perl + ' -e "use Win32;"') + fh = os.popen('"%s" -e "use Win32;"' % perl) fh.read() rc = fh.close() if rc: @@ -90,13 +95,9 @@ """ if not os.path.isfile(makefile): return - # 2.4 compatibility - fin = open(makefile) - if 1: # with open(makefile) as fin: + with open(makefile) as fin: lines = fin.readlines() - fin.close() - fout = open(makefile, 'w') - if 1: # with open(makefile, 'w') as fout: + with open(makefile, 'w') as fout: for line in lines: if line.startswith("PERL="): continue @@ -112,7 +113,6 @@ line = line + noalgo line = line + '\n' fout.write(line) - fout.close() def run_configure(configure, do_script): print("perl Configure "+configure) @@ -120,6 +120,22 @@ print(do_script) os.system(do_script) +def cmp(f1, f2): + bufsize = 1024 * 8 + with open(f1, 'rb') as fp1, open(f2, 'rb') as fp2: + while True: + b1 = fp1.read(bufsize) + b2 = fp2.read(bufsize) + if b1 != b2: + return False + if not b1: + return True + +def copy(src, dst): + if os.path.isfile(dst) and cmp(src, dst): + return + shutil.copy(src, dst) + def main(): debug = "-d" in sys.argv build_all = "-a" in sys.argv @@ -129,6 +145,7 @@ do_script = "ms\\do_nasm" makefile="ms\\nt.mak" m32 = makefile + dirsuffix = "32" configure += " no-idea no-rc5 no-mdc2" make_flags = "" if build_all: @@ -137,12 +154,12 @@ # as "well known" locations perls = find_all_on_path("perl.exe", ["\\perl\\bin", "C:\\perl\\bin"]) perl = find_working_perl(perls) - if perl is None: - print("No Perl installation was found. Existing Makefiles are used.") - else: + if perl: print("Found a working perl at '%s'" % (perl,)) + else: + print("No Perl installation was found. Existing Makefiles are used.") sys.stdout.flush() - # Look for SSL 3 levels up from pcbuild - ie, same place zlib etc all live. + # Look for SSL 3 levels up from PC/VC6 - ie, same place zlib etc all live. ssl_dir = find_best_ssl_dir(("..\\..\\..",)) if ssl_dir is None: sys.exit(1) @@ -173,12 +190,21 @@ # os.system("perl util\mk1mf.pl debug "+configure+" >"+makefile) fix_makefile(makefile) - shutil.copy(r"crypto\buildinf.h", r"crypto\buildinf_%s.h" % arch) - shutil.copy(r"crypto\opensslconf.h", r"crypto\opensslconf_%s.h" % arch) + copy(r"crypto\buildinf.h", r"crypto\buildinf_%s.h" % arch) + copy(r"crypto\opensslconf.h", r"crypto\opensslconf_%s.h" % arch) + + # If the assembler files don't exist in tmpXX, copy them there + if perl is None: + if not os.path.exists("tmp"+dirsuffix): + os.mkdir("tmp"+dirsuffix) + for f in os.listdir("asm"+dirsuffix): + if not f.endswith(".asm"): continue + if os.path.isfile(r"tmp%s\%s" % (dirsuffix, f)): continue + shutil.copy(r"asm%s\%s" % (dirsuffix, f), "tmp"+dirsuffix) # Now run make. - shutil.copy(r"crypto\buildinf_%s.h" % arch, r"crypto\buildinf.h") - shutil.copy(r"crypto\opensslconf_%s.h" % arch, r"crypto\opensslconf.h") + copy(r"crypto\buildinf_%s.h" % arch, r"crypto\buildinf.h") + copy(r"crypto\opensslconf_%s.h" % arch, r"crypto\opensslconf.h") #makeCommand = "nmake /nologo PERL=\"%s\" -f \"%s\"" %(perl, makefile) makeCommand = "nmake /nologo -f \"%s\"" % makefile From python-checkins at python.org Tue Sep 28 23:08:38 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Tue, 28 Sep 2010 23:08:38 +0200 (CEST) Subject: [Python-checkins] r85073 - in python/branches/py3k: Lib/ctypes/wintypes.py Misc/NEWS Message-ID: <20100928210838.4CA9AEE9B3@mail.python.org> Author: hirokazu.yamamoto Date: Tue Sep 28 23:08:38 2010 New Revision: 85073 Log: Issue #3612: Added new types to ctypes.wintypes. (CHAR and pointers) Modified: python/branches/py3k/Lib/ctypes/wintypes.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/ctypes/wintypes.py ============================================================================== --- python/branches/py3k/Lib/ctypes/wintypes.py (original) +++ python/branches/py3k/Lib/ctypes/wintypes.py Tue Sep 28 23:08:38 2010 @@ -1,49 +1,50 @@ # The most useful windows datatypes -from ctypes import * +import ctypes -BYTE = c_byte -WORD = c_ushort -DWORD = c_ulong - -WCHAR = c_wchar -UINT = c_uint -INT = c_int +BYTE = ctypes.c_byte +WORD = ctypes.c_ushort +DWORD = ctypes.c_ulong + +#UCHAR = ctypes.c_uchar +CHAR = ctypes.c_char +WCHAR = ctypes.c_wchar +UINT = ctypes.c_uint +INT = ctypes.c_int -DOUBLE = c_double -FLOAT = c_float +DOUBLE = ctypes.c_double +FLOAT = ctypes.c_float BOOLEAN = BYTE -BOOL = c_long +BOOL = ctypes.c_long -from ctypes import _SimpleCData -class VARIANT_BOOL(_SimpleCData): +class VARIANT_BOOL(ctypes._SimpleCData): _type_ = "v" def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.value) -ULONG = c_ulong -LONG = c_long +ULONG = ctypes.c_ulong +LONG = ctypes.c_long -USHORT = c_ushort -SHORT = c_short +USHORT = ctypes.c_ushort +SHORT = ctypes.c_short # in the windows header files, these are structures. -_LARGE_INTEGER = LARGE_INTEGER = c_longlong -_ULARGE_INTEGER = ULARGE_INTEGER = c_ulonglong +_LARGE_INTEGER = LARGE_INTEGER = ctypes.c_longlong +_ULARGE_INTEGER = ULARGE_INTEGER = ctypes.c_ulonglong -LPCOLESTR = LPOLESTR = OLESTR = c_wchar_p -LPCWSTR = LPWSTR = c_wchar_p -LPCSTR = LPSTR = c_char_p -LPCVOID = LPVOID = c_void_p +LPCOLESTR = LPOLESTR = OLESTR = ctypes.c_wchar_p +LPCWSTR = LPWSTR = ctypes.c_wchar_p +LPCSTR = LPSTR = ctypes.c_char_p +LPCVOID = LPVOID = ctypes.c_void_p # WPARAM is defined as UINT_PTR (unsigned type) # LPARAM is defined as LONG_PTR (signed type) -if sizeof(c_long) == sizeof(c_void_p): - WPARAM = c_ulong - LPARAM = c_long -elif sizeof(c_longlong) == sizeof(c_void_p): - WPARAM = c_ulonglong - LPARAM = c_longlong +if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p): + WPARAM = ctypes.c_ulong + LPARAM = ctypes.c_long +elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p): + WPARAM = ctypes.c_ulonglong + LPARAM = ctypes.c_longlong ATOM = WORD LANGID = WORD @@ -56,7 +57,7 @@ ################################################################ # HANDLE types -HANDLE = c_void_p # in the header files: void * +HANDLE = ctypes.c_void_p # in the header files: void * HACCEL = HANDLE HBITMAP = HANDLE @@ -93,45 +94,45 @@ ################################################################ # Some important structure definitions -class RECT(Structure): - _fields_ = [("left", c_long), - ("top", c_long), - ("right", c_long), - ("bottom", c_long)] +class RECT(ctypes.Structure): + _fields_ = [("left", LONG), + ("top", LONG), + ("right", LONG), + ("bottom", LONG)] tagRECT = _RECTL = RECTL = RECT -class _SMALL_RECT(Structure): - _fields_ = [('Left', c_short), - ('Top', c_short), - ('Right', c_short), - ('Bottom', c_short)] +class _SMALL_RECT(ctypes.Structure): + _fields_ = [('Left', SHORT), + ('Top', SHORT), + ('Right', SHORT), + ('Bottom', SHORT)] SMALL_RECT = _SMALL_RECT -class _COORD(Structure): - _fields_ = [('X', c_short), - ('Y', c_short)] - -class POINT(Structure): - _fields_ = [("x", c_long), - ("y", c_long)] +class _COORD(ctypes.Structure): + _fields_ = [('X', SHORT), + ('Y', SHORT)] + +class POINT(ctypes.Structure): + _fields_ = [("x", LONG), + ("y", LONG)] tagPOINT = _POINTL = POINTL = POINT -class SIZE(Structure): - _fields_ = [("cx", c_long), - ("cy", c_long)] +class SIZE(ctypes.Structure): + _fields_ = [("cx", LONG), + ("cy", LONG)] tagSIZE = SIZEL = SIZE def RGB(red, green, blue): return red + (green << 8) + (blue << 16) -class FILETIME(Structure): +class FILETIME(ctypes.Structure): _fields_ = [("dwLowDateTime", DWORD), ("dwHighDateTime", DWORD)] _FILETIME = FILETIME -class MSG(Structure): +class MSG(ctypes.Structure): _fields_ = [("hWnd", HWND), - ("message", c_uint), + ("message", UINT), ("wParam", WPARAM), ("lParam", LPARAM), ("time", DWORD), @@ -139,7 +140,7 @@ tagMSG = MSG MAX_PATH = 260 -class WIN32_FIND_DATAA(Structure): +class WIN32_FIND_DATAA(ctypes.Structure): _fields_ = [("dwFileAttributes", DWORD), ("ftCreationTime", FILETIME), ("ftLastAccessTime", FILETIME), @@ -148,10 +149,10 @@ ("nFileSizeLow", DWORD), ("dwReserved0", DWORD), ("dwReserved1", DWORD), - ("cFileName", c_char * MAX_PATH), - ("cAlternateFileName", c_char * 14)] + ("cFileName", CHAR * MAX_PATH), + ("cAlternateFileName", CHAR * 14)] -class WIN32_FIND_DATAW(Structure): +class WIN32_FIND_DATAW(ctypes.Structure): _fields_ = [("dwFileAttributes", DWORD), ("ftCreationTime", FILETIME), ("ftLastAccessTime", FILETIME), @@ -160,22 +161,42 @@ ("nFileSizeLow", DWORD), ("dwReserved0", DWORD), ("dwReserved1", DWORD), - ("cFileName", c_wchar * MAX_PATH), - ("cAlternateFileName", c_wchar * 14)] + ("cFileName", WCHAR * MAX_PATH), + ("cAlternateFileName", WCHAR * 14)] -__all__ = ['ATOM', 'BOOL', 'BOOLEAN', 'BYTE', 'COLORREF', 'DOUBLE', 'DWORD', - 'FILETIME', 'FLOAT', 'HACCEL', 'HANDLE', 'HBITMAP', 'HBRUSH', - 'HCOLORSPACE', 'HDC', 'HDESK', 'HDWP', 'HENHMETAFILE', 'HFONT', - 'HGDIOBJ', 'HGLOBAL', 'HHOOK', 'HICON', 'HINSTANCE', 'HKEY', - 'HKL', 'HLOCAL', 'HMENU', 'HMETAFILE', 'HMODULE', 'HMONITOR', - 'HPALETTE', 'HPEN', 'HRGN', 'HRSRC', 'HSTR', 'HTASK', 'HWINSTA', - 'HWND', 'INT', 'LANGID', 'LARGE_INTEGER', 'LCID', 'LCTYPE', - 'LGRPID', 'LONG', 'LPARAM', 'LPCOLESTR', 'LPCSTR', 'LPCVOID', - 'LPCWSTR', 'LPOLESTR', 'LPSTR', 'LPVOID', 'LPWSTR', 'MAX_PATH', - 'MSG', 'OLESTR', 'POINT', 'POINTL', 'RECT', 'RECTL', 'RGB', - 'SC_HANDLE', 'SERVICE_STATUS_HANDLE', 'SHORT', 'SIZE', 'SIZEL', - 'SMALL_RECT', 'UINT', 'ULARGE_INTEGER', 'ULONG', 'USHORT', - 'VARIANT_BOOL', 'WCHAR', 'WIN32_FIND_DATAA', 'WIN32_FIND_DATAW', - 'WORD', 'WPARAM', '_COORD', '_FILETIME', '_LARGE_INTEGER', - '_POINTL', '_RECTL', '_SMALL_RECT', '_ULARGE_INTEGER', 'tagMSG', - 'tagPOINT', 'tagRECT', 'tagSIZE'] +################################################################ +# Pointer types + +LPBOOL = PBOOL = ctypes.POINTER(BOOL) +PBOOLEAN = ctypes.POINTER(BOOLEAN) +LPBYTE = PBYTE = ctypes.POINTER(BYTE) +PCHAR = ctypes.POINTER(CHAR) +LPCOLORREF = ctypes.POINTER(COLORREF) +LPDWORD = PDWORD = ctypes.POINTER(DWORD) +LPFILETIME = PFILETIME = ctypes.POINTER(FILETIME) +PFLOAT = ctypes.POINTER(FLOAT) +LPHANDLE = PHANDLE = ctypes.POINTER(HANDLE) +PHKEY = ctypes.POINTER(HKEY) +LPHKL = ctypes.POINTER(HKL) +LPINT = PINT = ctypes.POINTER(INT) +PLARGE_INTEGER = ctypes.POINTER(LARGE_INTEGER) +PLCID = ctypes.POINTER(LCID) +LPLONG = PLONG = ctypes.POINTER(LONG) +LPMSG = PMSG = ctypes.POINTER(MSG) +LPPOINT = PPOINT = ctypes.POINTER(POINT) +PPOINTL = ctypes.POINTER(POINTL) +LPRECT = PRECT = ctypes.POINTER(RECT) +LPRECTL = PRECTL = ctypes.POINTER(RECTL) +LPSC_HANDLE = ctypes.POINTER(SC_HANDLE) +PSHORT = ctypes.POINTER(SHORT) +LPSIZE = PSIZE = ctypes.POINTER(SIZE) +LPSIZEL = PSIZEL = ctypes.POINTER(SIZEL) +PSMALL_RECT = ctypes.POINTER(SMALL_RECT) +LPUINT = PUINT = ctypes.POINTER(UINT) +PULARGE_INTEGER = ctypes.POINTER(ULARGE_INTEGER) +PULONG = ctypes.POINTER(ULONG) +PUSHORT = ctypes.POINTER(USHORT) +PWCHAR = ctypes.POINTER(WCHAR) +LPWIN32_FIND_DATAA = PWIN32_FIND_DATAA = ctypes.POINTER(WIN32_FIND_DATAA) +LPWIN32_FIND_DATAW = PWIN32_FIND_DATAW = ctypes.POINTER(WIN32_FIND_DATAW) +LPWORD = PWORD = ctypes.POINTER(WORD) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 28 23:08:38 2010 @@ -74,6 +74,8 @@ Library ------- +- Issue #3612: Added new types to ctypes.wintypes. (CHAR and pointers) + - Issue #9950: Fix socket.sendall() crash or misbehaviour when a signal is received. Now sendall() properly calls signal handlers if necessary, and retries sending if these returned successfully, including on sockets From python-checkins at python.org Tue Sep 28 23:23:11 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 28 Sep 2010 23:23:11 +0200 (CEST) Subject: [Python-checkins] r85074 - in python/branches/py3k: Include/pytime.h Misc/NEWS Modules/socketmodule.c Message-ID: <20100928212311.A7F8CEEA14@mail.python.org> Author: antoine.pitrou Date: Tue Sep 28 23:23:11 2010 New Revision: 85074 Log: Issue #9090: When a socket with a timeout fails with EWOULDBLOCK or EAGAIN, retry the select() loop instead of bailing out. This is because select() can incorrectly report a socket as ready for reading (for example, if it received some data with an invalid checksum). Modified: python/branches/py3k/Include/pytime.h python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/socketmodule.c Modified: python/branches/py3k/Include/pytime.h ============================================================================== --- python/branches/py3k/Include/pytime.h (original) +++ python/branches/py3k/Include/pytime.h Tue Sep 28 23:23:11 2010 @@ -25,6 +25,17 @@ */ PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp); +#define _PyTime_ADD_SECONDS(tv, interval) \ +do { \ + tv.tv_usec += (long) (((long) interval - interval) * 1000000); \ + tv.tv_sec += (time_t) interval + (time_t) (tv.tv_usec / 1000000); \ + tv.tv_usec %= 1000000; \ +} while (0) + +#define _PyTime_INTERVAL(tv_start, tv_end) \ + ((tv_end.tv_sec - tv_start.tv_sec) + \ + (tv_end.tv_usec - tv_start.tv_usec) * 0.000001) + /* Dummy to force linking. */ PyAPI_FUNC(void) _PyTime_Init(void); Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 28 23:23:11 2010 @@ -74,6 +74,11 @@ Library ------- +- Issue #9090: When a socket with a timeout fails with EWOULDBLOCK or EAGAIN, + retry the select() loop instead of bailing out. This is because select() + can incorrectly report a socket as ready for reading (for example, if it + received some data with an invalid checksum). + - Issue #3612: Added new types to ctypes.wintypes. (CHAR and pointers) - Issue #9950: Fix socket.sendall() crash or misbehaviour when a signal is Modified: python/branches/py3k/Modules/socketmodule.c ============================================================================== --- python/branches/py3k/Modules/socketmodule.c (original) +++ python/branches/py3k/Modules/socketmodule.c Tue Sep 28 23:23:11 2010 @@ -474,6 +474,14 @@ return NULL; } +#ifdef MS_WINDOWS +#define CHECK_ERRNO(expected) \ + (WSAGetLastError() == WSA ## expected) +#else +#define CHECK_ERRNO(expected) \ + (errno == expected) +#endif + /* Convenience function to raise an error according to errno and return a NULL pointer from a function. */ @@ -637,7 +645,7 @@ after they've reacquired the interpreter lock. Returns 1 on timeout, -1 on error, 0 otherwise. */ static int -internal_select(PySocketSockObject *s, int writing) +internal_select_ex(PySocketSockObject *s, int writing, double interval) { int n; @@ -649,6 +657,10 @@ if (s->sock_fd < 0) return 0; + /* Handling this condition here simplifies the select loops */ + if (interval < 0.0) + return 1; + /* Prefer poll, if available, since you can poll() any fd * which can't be done with select(). */ #ifdef HAVE_POLL @@ -660,7 +672,7 @@ pollfd.events = writing ? POLLOUT : POLLIN; /* s->sock_timeout is in seconds, timeout in ms */ - timeout = (int)(s->sock_timeout * 1000 + 0.5); + timeout = (int)(interval * 1000 + 0.5); n = poll(&pollfd, 1, timeout); } #else @@ -668,8 +680,8 @@ /* Construct the arguments to select */ fd_set fds; struct timeval tv; - tv.tv_sec = (int)s->sock_timeout; - tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); + tv.tv_sec = (int)interval; + tv.tv_usec = (int)((interval - tv.tv_sec) * 1e6); FD_ZERO(&fds); FD_SET(s->sock_fd, &fds); @@ -690,6 +702,53 @@ return 0; } +static int +internal_select(PySocketSockObject *s, int writing) +{ + return internal_select_ex(s, writing, s->sock_timeout); +} + +/* + Two macros for automatic retry of select() in case of false positives + (for example, select() could indicate a socket is ready for reading + but the data then discarded by the OS because of a wrong checksum). + Here is an example of use: + + BEGIN_SELECT_LOOP(s) + Py_BEGIN_ALLOW_THREADS + timeout = internal_select_ex(s, 0, interval); + if (!timeout) + outlen = recv(s->sock_fd, cbuf, len, flags); + Py_END_ALLOW_THREADS + if (timeout == 1) { + PyErr_SetString(socket_timeout, "timed out"); + return -1; + } + END_SELECT_LOOP(s) +*/ + +#define BEGIN_SELECT_LOOP(s) \ + { \ + _PyTime_timeval now, deadline = {0, 0}; \ + double interval = s->sock_timeout; \ + int has_timeout = s->sock_timeout > 0.0; \ + if (has_timeout) { \ + _PyTime_gettimeofday(&now); \ + deadline = now; \ + _PyTime_ADD_SECONDS(deadline, s->sock_timeout); \ + } \ + while (1) { \ + errno = 0; \ + +#define END_SELECT_LOOP(s) \ + if (!has_timeout || \ + (!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \ + break; \ + _PyTime_gettimeofday(&now); \ + interval = _PyTime_INTERVAL(now, deadline); \ + } \ + } \ + /* Initialize a new socket object. */ static double defaulttimeout = -1.0; /* Default timeout for new sockets */ @@ -1591,8 +1650,9 @@ if (!IS_SELECTABLE(s)) return select_error(); + BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select(s, 0); + timeout = internal_select_ex(s, 0, interval); if (!timeout) newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen); Py_END_ALLOW_THREADS @@ -1601,6 +1661,7 @@ PyErr_SetString(socket_timeout, "timed out"); return NULL; } + END_SELECT_LOOP(s) if (newfd == INVALID_SOCKET) return s->errorhandler(); @@ -2151,6 +2212,7 @@ * also possible that we return a number of bytes smaller than the request * bytes. */ + static Py_ssize_t sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags) { @@ -2171,8 +2233,9 @@ } #ifndef __VMS + BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select(s, 0); + timeout = internal_select_ex(s, 0, interval); if (!timeout) outlen = recv(s->sock_fd, cbuf, len, flags); Py_END_ALLOW_THREADS @@ -2181,6 +2244,7 @@ PyErr_SetString(socket_timeout, "timed out"); return -1; } + END_SELECT_LOOP(s) if (outlen < 0) { /* Note: the call to errorhandler() ALWAYS indirectly returned NULL, so ignore its return value */ @@ -2202,16 +2266,18 @@ segment = remaining; } + BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select(s, 0); + timeout = internal_select_ex(s, 0, interval); if (!timeout) nread = recv(s->sock_fd, read_buf, segment, flags); Py_END_ALLOW_THREADS - if (timeout == 1) { PyErr_SetString(socket_timeout, "timed out"); return -1; } + END_SELECT_LOOP(s) + if (nread < 0) { s->errorhandler(); return -1; @@ -2372,9 +2438,10 @@ return -1; } + BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS memset(&addrbuf, 0, addrlen); - timeout = internal_select(s, 0); + timeout = internal_select_ex(s, 0, interval); if (!timeout) { #ifndef MS_WINDOWS #if defined(PYOS_OS2) && !defined(PYCC_GCC) @@ -2395,6 +2462,7 @@ PyErr_SetString(socket_timeout, "timed out"); return -1; } + END_SELECT_LOOP(s) if (n < 0) { s->errorhandler(); return -1; @@ -2532,8 +2600,9 @@ buf = pbuf.buf; len = pbuf.len; + BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select(s, 1); + timeout = internal_select_ex(s, 1, interval); if (!timeout) #ifdef __VMS n = sendsegmented(s->sock_fd, buf, len, flags); @@ -2541,13 +2610,14 @@ n = send(s->sock_fd, buf, len, flags); #endif Py_END_ALLOW_THREADS - - PyBuffer_Release(&pbuf); - if (timeout == 1) { + PyBuffer_Release(&pbuf); PyErr_SetString(socket_timeout, "timed out"); return NULL; } + END_SELECT_LOOP(s) + + PyBuffer_Release(&pbuf); if (n < 0) return s->errorhandler(); return PyLong_FromSsize_t(n); @@ -2667,17 +2737,20 @@ return NULL; } + BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select(s, 1); + timeout = internal_select_ex(s, 1, interval); if (!timeout) n = sendto(s->sock_fd, buf, len, flags, SAS2SA(&addrbuf), addrlen); Py_END_ALLOW_THREADS - PyBuffer_Release(&pbuf); if (timeout == 1) { + PyBuffer_Release(&pbuf); PyErr_SetString(socket_timeout, "timed out"); return NULL; } + END_SELECT_LOOP(s) + PyBuffer_Release(&pbuf); if (n < 0) return s->errorhandler(); return PyLong_FromSsize_t(n); From python-checkins at python.org Tue Sep 28 23:52:31 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 28 Sep 2010 23:52:31 +0200 (CEST) Subject: [Python-checkins] r85075 - python/branches/py3k/Doc/c-api/objbuffer.rst Message-ID: <20100928215231.08A09FC92@mail.python.org> Author: antoine.pitrou Date: Tue Sep 28 23:52:30 2010 New Revision: 85075 Log: Discourage use of the old buffer API funcs Modified: python/branches/py3k/Doc/c-api/objbuffer.rst Modified: python/branches/py3k/Doc/c-api/objbuffer.rst ============================================================================== --- python/branches/py3k/Doc/c-api/objbuffer.rst (original) +++ python/branches/py3k/Doc/c-api/objbuffer.rst Tue Sep 28 23:52:30 2010 @@ -1,6 +1,21 @@ .. highlightlang:: c -.. _abstract-buffer: +Old buffer API +-------------- + +.. deprecated:: 3.0 + +These functions were part of the "old buffer protocol" API in Python 2. +In Python 3, these functions are still exposed for ease of porting code. +They act as a compatibility wrapper around the :ref:`new buffer API +`, but they don't give you control over the lifetime of +the resources acquired when a buffer is exported. + +Therefore, it is recommended that you call :cfunc:`PyObject_GetBuffer` +(or the ``y*`` or ``w*`` :ref:`format codes ` with the +:cfunc:`PyArg_ParseTuple` family of functions) to get a buffer view over +an object, and :cfunc:`PyBuffer_Release` when the buffer view can be released. + Buffer Protocol =============== From python-checkins at python.org Tue Sep 28 23:53:46 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 28 Sep 2010 23:53:46 +0200 (CEST) Subject: [Python-checkins] r85076 - in python/branches/release31-maint: Doc/c-api/objbuffer.rst Message-ID: <20100928215346.B05D6EEA42@mail.python.org> Author: antoine.pitrou Date: Tue Sep 28 23:53:46 2010 New Revision: 85076 Log: Merged revisions 85075 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85075 | antoine.pitrou | 2010-09-28 23:52:30 +0200 (mar., 28 sept. 2010) | 3 lines Discourage use of the old buffer API funcs ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/c-api/objbuffer.rst Modified: python/branches/release31-maint/Doc/c-api/objbuffer.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/objbuffer.rst (original) +++ python/branches/release31-maint/Doc/c-api/objbuffer.rst Tue Sep 28 23:53:46 2010 @@ -1,6 +1,21 @@ .. highlightlang:: c -.. _abstract-buffer: +Old buffer API +-------------- + +.. deprecated:: 3.0 + +These functions were part of the "old buffer protocol" API in Python 2. +In Python 3, these functions are still exposed for ease of porting code. +They act as a compatibility wrapper around the :ref:`new buffer API +`, but they don't give you control over the lifetime of +the resources acquired when a buffer is exported. + +Therefore, it is recommended that you call :cfunc:`PyObject_GetBuffer` +(or the ``y*`` or ``w*`` :ref:`format codes ` with the +:cfunc:`PyArg_ParseTuple` family of functions) to get a buffer view over +an object, and :cfunc:`PyBuffer_Release` when the buffer view can be released. + Buffer Protocol =============== From python-checkins at python.org Wed Sep 29 00:03:27 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 00:03:27 +0200 (CEST) Subject: [Python-checkins] r85077 - python/branches/py3k/Modules/socketmodule.c Message-ID: <20100928220327.8C28DF651@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 00:03:27 2010 New Revision: 85077 Log: Fix compilation under Windows Modified: python/branches/py3k/Modules/socketmodule.c Modified: python/branches/py3k/Modules/socketmodule.c ============================================================================== --- python/branches/py3k/Modules/socketmodule.c (original) +++ python/branches/py3k/Modules/socketmodule.c Wed Sep 29 00:03:27 2010 @@ -475,6 +475,9 @@ } #ifdef MS_WINDOWS +#ifndef WSAEAGAIN +#define WSAEAGAIN WSAEWOULDBLOCK +#endif #define CHECK_ERRNO(expected) \ (WSAGetLastError() == WSA ## expected) #else From python-checkins at python.org Wed Sep 29 00:25:18 2010 From: python-checkins at python.org (r.david.murray) Date: Wed, 29 Sep 2010 00:25:18 +0200 (CEST) Subject: [Python-checkins] r85078 - in python/branches/py3k: Misc/NEWS runtests.sh Message-ID: <20100928222518.63C95EE98E@mail.python.org> Author: r.david.murray Date: Wed Sep 29 00:25:18 2010 New Revision: 85078 Log: #9628: fix runtests.sh -x option so more than one test can be excluded. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/runtests.sh Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 29 00:25:18 2010 @@ -260,6 +260,8 @@ Tests ----- +- Issue #9628: fix runtests.sh -x option so more than one test can be excluded. + - Issue #9899: Fix test_tkinter.test_font on various platforms. Patch by Ned Deily. Modified: python/branches/py3k/runtests.sh ============================================================================== --- python/branches/py3k/runtests.sh (original) +++ python/branches/py3k/runtests.sh Wed Sep 29 00:25:18 2010 @@ -55,7 +55,7 @@ TESTS=`(cd Lib/test; ls test_*.py | sed 's/\.py//')` ;; *-x) - PAT="^(`echo $@ | sed 's/\.py//' | sed 's/ /|/'`)$" + PAT="^(`echo $@ | sed 's/\.py//g' | sed 's/ /|/g'`)$" TESTS=`(cd Lib/test; ls test_*.py | sed 's/\.py//' | egrep -v "$PAT")` ;; *) From python-checkins at python.org Wed Sep 29 00:34:05 2010 From: python-checkins at python.org (r.david.murray) Date: Wed, 29 Sep 2010 00:34:05 +0200 (CEST) Subject: [Python-checkins] r85079 - in python/branches/release31-maint: Misc/NEWS runtests.sh Message-ID: <20100928223405.D916BF9FC@mail.python.org> Author: r.david.murray Date: Wed Sep 29 00:34:03 2010 New Revision: 85079 Log: Merged revisions 85078 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85078 | r.david.murray | 2010-09-28 18:25:18 -0400 (Tue, 28 Sep 2010) | 2 lines #9628: fix runtests.sh -x option so more than one test can be excluded. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/runtests.sh Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Wed Sep 29 00:34:03 2010 @@ -611,6 +611,7 @@ Tests ----- +- Issue #9628: fix runtests.sh -x option so more than one test can be excluded. - Issue #9894: Do not hardcode ENOENT in test_subprocess. Modified: python/branches/release31-maint/runtests.sh ============================================================================== --- python/branches/release31-maint/runtests.sh (original) +++ python/branches/release31-maint/runtests.sh Wed Sep 29 00:34:03 2010 @@ -55,7 +55,7 @@ TESTS=`(cd Lib/test; ls test_*.py | sed 's/\.py//')` ;; *-x) - PAT="^(`echo $@ | sed 's/\.py//' | sed 's/ /|/'`)$" + PAT="^(`echo $@ | sed 's/\.py//g' | sed 's/ /|/g'`)$" TESTS=`(cd Lib/test; ls test_*.py | sed 's/\.py//' | egrep -v "$PAT")` ;; *) From python-checkins at python.org Wed Sep 29 01:04:06 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 01:04:06 +0200 (CEST) Subject: [Python-checkins] r85080 - python/branches/py3k/Doc/c-api/buffer.rst Message-ID: <20100928230406.462EFEE993@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 01:04:04 2010 New Revision: 85080 Log: Various improvements to the docs of the buffer API Modified: python/branches/py3k/Doc/c-api/buffer.rst Modified: python/branches/py3k/Doc/c-api/buffer.rst ============================================================================== --- python/branches/py3k/Doc/c-api/buffer.rst (original) +++ python/branches/py3k/Doc/c-api/buffer.rst Wed Sep 29 01:04:04 2010 @@ -2,8 +2,8 @@ .. _bufferobjects: -Buffer Objects --------------- +Buffer API +---------- .. sectionauthor:: Greg Stein .. sectionauthor:: Benjamin Peterson @@ -17,30 +17,56 @@ format. Clients of the object can use the buffer interface to access the object data directly, without needing to copy it first. -Two examples of objects that support the buffer interface are bytes and -arrays. The bytes object exposes the character contents in the buffer -interface's byte-oriented form. An array can also expose its contents, but it -should be noted that array elements may be multi-byte values. - -An example user of the buffer interface is the file object's :meth:`write` -method. Any object that can export a series of bytes through the buffer -interface can be written to a file. There are a number of format codes to -:cfunc:`PyArg_ParseTuple` that operate against an object's buffer interface, -returning data from the target object. +Examples of objects that support the buffer interface are :class:`bytes`, +:class:`bytearray` and :class:`array.array`. The bytes and bytearray objects +exposes their bytes contents in the buffer interface's byte-oriented form. +An :class:`array.array` can also expose its contents, but it should be noted +that array elements may be multi-byte values. + +An example consumer of the buffer interface is the :meth:`~io.BufferedIOBase.write` +method of file objects: any object that can export a series of bytes through +the buffer interface can be written to a file. While :meth:`write` only +needs read-only access to the internal contents of the object passed to it, +other methods such as :meth:`~io.BufferedIOBase.readinto` need write access +to the contents of their argument. The buffer interface allows objects to +selectively allow or reject exporting of read-write and read-only buffers. + +There are two ways for a consumer of the buffer interface to acquire a buffer +over a target object: + +* call :cfunc:`PyObject_GetBuffer` with the right parameters; + +* call :cfunc:`PyArg_ParseTuple` (or one of its siblings) with one of the + ``y*``, ``w*`` or ``s*`` :ref:`format codes `. + +In both cases, :cfunc:`PyBuffer_Release` must be called when the buffer +isn't needed anymore. Failure to do so could lead to various issues such as +resource leaks. + .. index:: single: PyBufferProcs -More information on the buffer interface is provided in the section -:ref:`buffer-structs`, under the description for :ctype:`PyBufferProcs`. +How the buffer interface is exposed by a type object is described in the +section :ref:`buffer-structs`, under the description for :ctype:`PyBufferProcs`. + + +Buffer objects +============== -Buffer objects are useful as a way to expose the data from another object's -buffer interface to the Python programmer. They can also be used as a zero-copy +Buffer objects are useful as a way to expose the binary data from another +object to the Python programmer. They can also be used as a zero-copy slicing mechanism. Using their ability to reference a block of memory, it is possible to expose any data to the Python programmer quite easily. The memory could be a large, constant array in a C extension, it could be a raw block of memory for manipulation before passing to an operating system library, or it could be used to pass around structured data in its native, in-memory format. +Contrary to most data types exposed by the Python interpreter, buffer objects +are not :ctype:`PyObject` pointers but rather simple C structures. This +allows them to be created and copied very simply. When a generic wrapper +around a buffer object is needed, a :ref:`memoryview ` object +can be created. + .. ctype:: Py_buffer @@ -133,18 +159,23 @@ .. cfunction:: int PyObject_CheckBuffer(PyObject *obj) - Return 1 if *obj* supports the buffer interface otherwise 0. + Return 1 if *obj* supports the buffer interface otherwise 0. When 1 is + returned, it doesn't guarantee that :cfunc:`PyObject_GetBuffer` will + succeed. .. cfunction:: int PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags) - Export *obj* into a :ctype:`Py_buffer`, *view*. These arguments must - never be *NULL*. The *flags* argument is a bit field indicating what - kind of buffer the caller is prepared to deal with and therefore what - kind of buffer the exporter is allowed to return. The buffer interface - allows for complicated memory sharing possibilities, but some caller may - not be able to handle all the complexity but may want to see if the - exporter will let them take a simpler view to its memory. + Export a view over some internal data from the target object *obj*. + *obj* must not be NULL, and *view* must point to an existing + :ctype:`Py_buffer` structure allocated by the caller (most uses of + this function will simply declare a local variable of type + :ctype:`Py_buffer`). The *flags* argument is a bit field indicating + what kind of buffer is requested. The buffer interface allows + for complicated memory layout possibilities; however, some callers + won't want to handle all the complexity and instead request a simple + view of the target object (using :cmacro:`PyBUF_SIMPLE` for a read-only + view and :cmacro:`PyBUF_WRITABLE` for a read-write view). Some exporters may not be able to share memory in every possible way and may need to raise errors to signal to some consumers that something is @@ -154,26 +185,31 @@ :cdata:`Py_buffer` structure is filled in with non-default values and/or raise an error if the object can't support a simpler view of its memory. - 0 is returned on success and -1 on error. + On success, 0 is returned and the *view* structure is filled with useful + values. On error, -1 is returned and an exception is raised; the *view* + is left in an undefined state. The following table gives possible values to the *flags* arguments. +------------------------------+---------------------------------------------------+ | Flag | Description | +==============================+===================================================+ - | :cmacro:`PyBUF_SIMPLE` | This is the default flag state. The returned | - | | buffer may or may not have writable memory. The | - | | format of the data will be assumed to be unsigned | - | | bytes. This is a "stand-alone" flag constant. It | + | .. cmacro:: PyBUF_SIMPLE | This is the default flag. The returned buffer | + | | exposes a read-only memory area. The format of | + | | data is assumed to be raw unsigned bytes, without | + | | any particular structure. This is a "stand-alone"| + | | flag constant. It | | | never needs to be '|'d to the others. The exporter| | | will raise an error if it cannot provide such a | | | contiguous buffer of bytes. | | | | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_WRITABLE` | The returned buffer must be writable. If it is | - | | not writable, then raise an error. | + | .. cmacro:: PyBUF_WRITABLE | Like :cmacro:`PyBUF_SIMPLE`, but the returned | + | | buffer is writable. If the exporter doesn't | + | | support | + | | writable buffers, an error is raised. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_STRIDES` | This implies :cmacro:`PyBUF_ND`. The returned | + | .. cmacro:: PyBUF_STRIDES | This implies :cmacro:`PyBUF_ND`. The returned | | | buffer must provide strides information (i.e. the | | | strides cannot be NULL). This would be used when | | | the consumer can handle strided, discontiguous | @@ -183,19 +219,17 @@ | | not possible (i.e. without the suboffsets). | | | | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_ND` | The returned buffer must provide shape | + | .. cmacro:: PyBUF_ND | The returned buffer must provide shape | | | information. The memory will be assumed C-style | | | contiguous (last dimension varies the | | | fastest). The exporter may raise an error if it | | | cannot provide this kind of contiguous buffer. If | | | this is not given then shape will be *NULL*. | | | | - | | | - | | | +------------------------------+---------------------------------------------------+ - |:cmacro:`PyBUF_C_CONTIGUOUS` | These flags indicate that the contiguity returned | - |:cmacro:`PyBUF_F_CONTIGUOUS` | buffer must be respectively, C-contiguous (last | - |:cmacro:`PyBUF_ANY_CONTIGUOUS`| dimension varies the fastest), Fortran contiguous | + |.. cmacro:: PyBUF_C_CONTIGUOUS| These flags indicate that the contiguity returned | + | PyBUF_F_CONTIGUOUS| buffer must be respectively, C-contiguous (last | + | PyBUF_ANY_CONTIGUOUS| dimension varies the fastest), Fortran contiguous | | | (first dimension varies the fastest) or either | | | one. All of these flags imply | | | :cmacro:`PyBUF_STRIDES` and guarantee that the | @@ -203,7 +237,7 @@ | | correctly. | | | | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_INDIRECT` | This flag indicates the returned buffer must have | + | .. cmacro:: PyBUF_INDIRECT | This flag indicates the returned buffer must have | | | suboffsets information (which can be NULL if no | | | suboffsets are needed). This can be used when | | | the consumer can handle indirect array | @@ -213,7 +247,7 @@ | | | | | | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_FORMAT` | The returned buffer must have true format | + | .. cmacro:: PyBUF_FORMAT | The returned buffer must have true format | | | information if this flag is provided. This would | | | be used when the consumer is going to be checking | | | for what 'kind' of data is actually stored. An | @@ -223,28 +257,28 @@ | | returned as *NULL* (which means ``'B'``, or | | | unsigned bytes) | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_STRIDED` | This is equivalent to ``(PyBUF_STRIDES | | + | .. cmacro:: PyBUF_STRIDED | This is equivalent to ``(PyBUF_STRIDES | | | | PyBUF_WRITABLE)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_STRIDED_RO` | This is equivalent to ``(PyBUF_STRIDES)``. | + | .. cmacro:: PyBUF_STRIDED_RO | This is equivalent to ``(PyBUF_STRIDES)``. | | | | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_RECORDS` | This is equivalent to ``(PyBUF_STRIDES | | + | .. cmacro:: PyBUF_RECORDS | This is equivalent to ``(PyBUF_STRIDES | | | | PyBUF_FORMAT | PyBUF_WRITABLE)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_RECORDS_RO` | This is equivalent to ``(PyBUF_STRIDES | | + | .. cmacro:: PyBUF_RECORDS_RO | This is equivalent to ``(PyBUF_STRIDES | | | | PyBUF_FORMAT)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_FULL` | This is equivalent to ``(PyBUF_INDIRECT | | + | .. cmacro:: PyBUF_FULL | This is equivalent to ``(PyBUF_INDIRECT | | | | PyBUF_FORMAT | PyBUF_WRITABLE)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_FULL_RO` | This is equivalent to ``(PyBUF_INDIRECT | | + | .. cmacro:: PyBUF_FULL_RO | This is equivalent to ``(PyBUF_INDIRECT | | | | PyBUF_FORMAT)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_CONTIG` | This is equivalent to ``(PyBUF_ND | | + | .. cmacro:: PyBUF_CONTIG | This is equivalent to ``(PyBUF_ND | | | | PyBUF_WRITABLE)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_CONTIG_RO` | This is equivalent to ``(PyBUF_ND)``. | + | .. cmacro:: PyBUF_CONTIG_RO | This is equivalent to ``(PyBUF_ND)``. | | | | +------------------------------+---------------------------------------------------+ @@ -299,6 +333,7 @@ .. index:: object: memoryview +.. _memoryviewobjects: MemoryView objects ================== From python-checkins at python.org Wed Sep 29 01:05:17 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 01:05:17 +0200 (CEST) Subject: [Python-checkins] r85081 - in python/branches/release31-maint: Doc/c-api/buffer.rst Message-ID: <20100928230517.D966DF7BB@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 01:05:17 2010 New Revision: 85081 Log: Merged revisions 85080 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85080 | antoine.pitrou | 2010-09-29 01:04:04 +0200 (mer., 29 sept. 2010) | 3 lines Various improvements to the docs of the buffer API ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/c-api/buffer.rst Modified: python/branches/release31-maint/Doc/c-api/buffer.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/buffer.rst (original) +++ python/branches/release31-maint/Doc/c-api/buffer.rst Wed Sep 29 01:05:17 2010 @@ -2,8 +2,8 @@ .. _bufferobjects: -Buffer Objects --------------- +Buffer API +---------- .. sectionauthor:: Greg Stein .. sectionauthor:: Benjamin Peterson @@ -17,30 +17,56 @@ format. Clients of the object can use the buffer interface to access the object data directly, without needing to copy it first. -Two examples of objects that support the buffer interface are bytes and -arrays. The bytes object exposes the character contents in the buffer -interface's byte-oriented form. An array can also expose its contents, but it -should be noted that array elements may be multi-byte values. - -An example user of the buffer interface is the file object's :meth:`write` -method. Any object that can export a series of bytes through the buffer -interface can be written to a file. There are a number of format codes to -:cfunc:`PyArg_ParseTuple` that operate against an object's buffer interface, -returning data from the target object. +Examples of objects that support the buffer interface are :class:`bytes`, +:class:`bytearray` and :class:`array.array`. The bytes and bytearray objects +exposes their bytes contents in the buffer interface's byte-oriented form. +An :class:`array.array` can also expose its contents, but it should be noted +that array elements may be multi-byte values. + +An example consumer of the buffer interface is the :meth:`~io.BufferedIOBase.write` +method of file objects: any object that can export a series of bytes through +the buffer interface can be written to a file. While :meth:`write` only +needs read-only access to the internal contents of the object passed to it, +other methods such as :meth:`~io.BufferedIOBase.readinto` need write access +to the contents of their argument. The buffer interface allows objects to +selectively allow or reject exporting of read-write and read-only buffers. + +There are two ways for a consumer of the buffer interface to acquire a buffer +over a target object: + +* call :cfunc:`PyObject_GetBuffer` with the right parameters; + +* call :cfunc:`PyArg_ParseTuple` (or one of its siblings) with one of the + ``y*``, ``w*`` or ``s*`` :ref:`format codes `. + +In both cases, :cfunc:`PyBuffer_Release` must be called when the buffer +isn't needed anymore. Failure to do so could lead to various issues such as +resource leaks. + .. index:: single: PyBufferProcs -More information on the buffer interface is provided in the section -:ref:`buffer-structs`, under the description for :ctype:`PyBufferProcs`. +How the buffer interface is exposed by a type object is described in the +section :ref:`buffer-structs`, under the description for :ctype:`PyBufferProcs`. + + +Buffer objects +============== -Buffer objects are useful as a way to expose the data from another object's -buffer interface to the Python programmer. They can also be used as a zero-copy +Buffer objects are useful as a way to expose the binary data from another +object to the Python programmer. They can also be used as a zero-copy slicing mechanism. Using their ability to reference a block of memory, it is possible to expose any data to the Python programmer quite easily. The memory could be a large, constant array in a C extension, it could be a raw block of memory for manipulation before passing to an operating system library, or it could be used to pass around structured data in its native, in-memory format. +Contrary to most data types exposed by the Python interpreter, buffer objects +are not :ctype:`PyObject` pointers but rather simple C structures. This +allows them to be created and copied very simply. When a generic wrapper +around a buffer object is needed, a :ref:`memoryview ` object +can be created. + .. ctype:: Py_buffer @@ -133,18 +159,23 @@ .. cfunction:: int PyObject_CheckBuffer(PyObject *obj) - Return 1 if *obj* supports the buffer interface otherwise 0. + Return 1 if *obj* supports the buffer interface otherwise 0. When 1 is + returned, it doesn't guarantee that :cfunc:`PyObject_GetBuffer` will + succeed. .. cfunction:: int PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags) - Export *obj* into a :ctype:`Py_buffer`, *view*. These arguments must - never be *NULL*. The *flags* argument is a bit field indicating what - kind of buffer the caller is prepared to deal with and therefore what - kind of buffer the exporter is allowed to return. The buffer interface - allows for complicated memory sharing possibilities, but some caller may - not be able to handle all the complexity but may want to see if the - exporter will let them take a simpler view to its memory. + Export a view over some internal data from the target object *obj*. + *obj* must not be NULL, and *view* must point to an existing + :ctype:`Py_buffer` structure allocated by the caller (most uses of + this function will simply declare a local variable of type + :ctype:`Py_buffer`). The *flags* argument is a bit field indicating + what kind of buffer is requested. The buffer interface allows + for complicated memory layout possibilities; however, some callers + won't want to handle all the complexity and instead request a simple + view of the target object (using :cmacro:`PyBUF_SIMPLE` for a read-only + view and :cmacro:`PyBUF_WRITABLE` for a read-write view). Some exporters may not be able to share memory in every possible way and may need to raise errors to signal to some consumers that something is @@ -154,26 +185,31 @@ :cdata:`Py_buffer` structure is filled in with non-default values and/or raise an error if the object can't support a simpler view of its memory. - 0 is returned on success and -1 on error. + On success, 0 is returned and the *view* structure is filled with useful + values. On error, -1 is returned and an exception is raised; the *view* + is left in an undefined state. The following table gives possible values to the *flags* arguments. +------------------------------+---------------------------------------------------+ | Flag | Description | +==============================+===================================================+ - | :cmacro:`PyBUF_SIMPLE` | This is the default flag state. The returned | - | | buffer may or may not have writable memory. The | - | | format of the data will be assumed to be unsigned | - | | bytes. This is a "stand-alone" flag constant. It | + | .. cmacro:: PyBUF_SIMPLE | This is the default flag. The returned buffer | + | | exposes a read-only memory area. The format of | + | | data is assumed to be raw unsigned bytes, without | + | | any particular structure. This is a "stand-alone"| + | | flag constant. It | | | never needs to be '|'d to the others. The exporter| | | will raise an error if it cannot provide such a | | | contiguous buffer of bytes. | | | | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_WRITABLE` | The returned buffer must be writable. If it is | - | | not writable, then raise an error. | + | .. cmacro:: PyBUF_WRITABLE | Like :cmacro:`PyBUF_SIMPLE`, but the returned | + | | buffer is writable. If the exporter doesn't | + | | support | + | | writable buffers, an error is raised. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_STRIDES` | This implies :cmacro:`PyBUF_ND`. The returned | + | .. cmacro:: PyBUF_STRIDES | This implies :cmacro:`PyBUF_ND`. The returned | | | buffer must provide strides information (i.e. the | | | strides cannot be NULL). This would be used when | | | the consumer can handle strided, discontiguous | @@ -183,19 +219,17 @@ | | not possible (i.e. without the suboffsets). | | | | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_ND` | The returned buffer must provide shape | + | .. cmacro:: PyBUF_ND | The returned buffer must provide shape | | | information. The memory will be assumed C-style | | | contiguous (last dimension varies the | | | fastest). The exporter may raise an error if it | | | cannot provide this kind of contiguous buffer. If | | | this is not given then shape will be *NULL*. | | | | - | | | - | | | +------------------------------+---------------------------------------------------+ - |:cmacro:`PyBUF_C_CONTIGUOUS` | These flags indicate that the contiguity returned | - |:cmacro:`PyBUF_F_CONTIGUOUS` | buffer must be respectively, C-contiguous (last | - |:cmacro:`PyBUF_ANY_CONTIGUOUS`| dimension varies the fastest), Fortran contiguous | + |.. cmacro:: PyBUF_C_CONTIGUOUS| These flags indicate that the contiguity returned | + | PyBUF_F_CONTIGUOUS| buffer must be respectively, C-contiguous (last | + | PyBUF_ANY_CONTIGUOUS| dimension varies the fastest), Fortran contiguous | | | (first dimension varies the fastest) or either | | | one. All of these flags imply | | | :cmacro:`PyBUF_STRIDES` and guarantee that the | @@ -203,7 +237,7 @@ | | correctly. | | | | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_INDIRECT` | This flag indicates the returned buffer must have | + | .. cmacro:: PyBUF_INDIRECT | This flag indicates the returned buffer must have | | | suboffsets information (which can be NULL if no | | | suboffsets are needed). This can be used when | | | the consumer can handle indirect array | @@ -213,7 +247,7 @@ | | | | | | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_FORMAT` | The returned buffer must have true format | + | .. cmacro:: PyBUF_FORMAT | The returned buffer must have true format | | | information if this flag is provided. This would | | | be used when the consumer is going to be checking | | | for what 'kind' of data is actually stored. An | @@ -223,28 +257,28 @@ | | returned as *NULL* (which means ``'B'``, or | | | unsigned bytes) | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_STRIDED` | This is equivalent to ``(PyBUF_STRIDES | | + | .. cmacro:: PyBUF_STRIDED | This is equivalent to ``(PyBUF_STRIDES | | | | PyBUF_WRITABLE)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_STRIDED_RO` | This is equivalent to ``(PyBUF_STRIDES)``. | + | .. cmacro:: PyBUF_STRIDED_RO | This is equivalent to ``(PyBUF_STRIDES)``. | | | | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_RECORDS` | This is equivalent to ``(PyBUF_STRIDES | | + | .. cmacro:: PyBUF_RECORDS | This is equivalent to ``(PyBUF_STRIDES | | | | PyBUF_FORMAT | PyBUF_WRITABLE)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_RECORDS_RO` | This is equivalent to ``(PyBUF_STRIDES | | + | .. cmacro:: PyBUF_RECORDS_RO | This is equivalent to ``(PyBUF_STRIDES | | | | PyBUF_FORMAT)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_FULL` | This is equivalent to ``(PyBUF_INDIRECT | | + | .. cmacro:: PyBUF_FULL | This is equivalent to ``(PyBUF_INDIRECT | | | | PyBUF_FORMAT | PyBUF_WRITABLE)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_FULL_RO` | This is equivalent to ``(PyBUF_INDIRECT | | + | .. cmacro:: PyBUF_FULL_RO | This is equivalent to ``(PyBUF_INDIRECT | | | | PyBUF_FORMAT)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_CONTIG` | This is equivalent to ``(PyBUF_ND | | + | .. cmacro:: PyBUF_CONTIG | This is equivalent to ``(PyBUF_ND | | | | PyBUF_WRITABLE)``. | +------------------------------+---------------------------------------------------+ - | :cmacro:`PyBUF_CONTIG_RO` | This is equivalent to ``(PyBUF_ND)``. | + | .. cmacro:: PyBUF_CONTIG_RO | This is equivalent to ``(PyBUF_ND)``. | | | | +------------------------------+---------------------------------------------------+ @@ -299,6 +333,7 @@ .. index:: object: memoryview +.. _memoryviewobjects: MemoryView objects ================== From python-checkins at python.org Wed Sep 29 01:39:41 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 01:39:41 +0200 (CEST) Subject: [Python-checkins] r85082 - in python/branches/py3k/Doc/c-api: abstract.rst buffer.rst concrete.rst objbuffer.rst Message-ID: <20100928233941.B5DB5EE9B1@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 01:39:41 2010 New Revision: 85082 Log: Buffers are not sequence objects (!). Put them in the abstract objects layers instead. Modified: python/branches/py3k/Doc/c-api/abstract.rst python/branches/py3k/Doc/c-api/buffer.rst python/branches/py3k/Doc/c-api/concrete.rst python/branches/py3k/Doc/c-api/objbuffer.rst Modified: python/branches/py3k/Doc/c-api/abstract.rst ============================================================================== --- python/branches/py3k/Doc/c-api/abstract.rst (original) +++ python/branches/py3k/Doc/c-api/abstract.rst Wed Sep 29 01:39:41 2010 @@ -22,4 +22,5 @@ sequence.rst mapping.rst iter.rst + buffer.rst objbuffer.rst Modified: python/branches/py3k/Doc/c-api/buffer.rst ============================================================================== --- python/branches/py3k/Doc/c-api/buffer.rst (original) +++ python/branches/py3k/Doc/c-api/buffer.rst Wed Sep 29 01:39:41 2010 @@ -2,8 +2,8 @@ .. _bufferobjects: -Buffer API ----------- +Buffer Protocol +--------------- .. sectionauthor:: Greg Stein .. sectionauthor:: Benjamin Peterson @@ -50,21 +50,22 @@ section :ref:`buffer-structs`, under the description for :ctype:`PyBufferProcs`. -Buffer objects -============== +The buffer structure +==================== -Buffer objects are useful as a way to expose the binary data from another -object to the Python programmer. They can also be used as a zero-copy -slicing mechanism. Using their ability to reference a block of memory, it is -possible to expose any data to the Python programmer quite easily. The memory -could be a large, constant array in a C extension, it could be a raw block of -memory for manipulation before passing to an operating system library, or it -could be used to pass around structured data in its native, in-memory format. +Buffer structures (or simply "buffers") are useful as a way to expose the +binary data from another object to the Python programmer. They can also be +used as a zero-copy slicing mechanism. Using their ability to reference a +block of memory, it is possible to expose any data to the Python programmer +quite easily. The memory could be a large, constant array in a C extension, +it could be a raw block of memory for manipulation before passing to an +operating system library, or it could be used to pass around structured data +in its native, in-memory format. -Contrary to most data types exposed by the Python interpreter, buffer objects +Contrary to most data types exposed by the Python interpreter, buffers are not :ctype:`PyObject` pointers but rather simple C structures. This allows them to be created and copied very simply. When a generic wrapper -around a buffer object is needed, a :ref:`memoryview ` object +around a buffer is needed, a :ref:`memoryview ` object can be created. Modified: python/branches/py3k/Doc/c-api/concrete.rst ============================================================================== --- python/branches/py3k/Doc/c-api/concrete.rst (original) +++ python/branches/py3k/Doc/c-api/concrete.rst Wed Sep 29 01:39:41 2010 @@ -68,7 +68,6 @@ bytes.rst bytearray.rst unicode.rst - buffer.rst tuple.rst list.rst Modified: python/branches/py3k/Doc/c-api/objbuffer.rst ============================================================================== --- python/branches/py3k/Doc/c-api/objbuffer.rst (original) +++ python/branches/py3k/Doc/c-api/objbuffer.rst Wed Sep 29 01:39:41 2010 @@ -1,15 +1,16 @@ .. highlightlang:: c -Old buffer API --------------- +Old Buffer Protocol +------------------- .. deprecated:: 3.0 These functions were part of the "old buffer protocol" API in Python 2. -In Python 3, these functions are still exposed for ease of porting code. -They act as a compatibility wrapper around the :ref:`new buffer API -`, but they don't give you control over the lifetime of -the resources acquired when a buffer is exported. +In Python 3, this protocol doesn't exist anymore but the functions are still +exposed to ease porting 2.x code. They act as a compatibility wrapper +around the :ref:`new buffer protocol `, but they don't give +you control over the lifetime of the resources acquired when a buffer is +exported. Therefore, it is recommended that you call :cfunc:`PyObject_GetBuffer` (or the ``y*`` or ``w*`` :ref:`format codes ` with the @@ -17,10 +18,6 @@ an object, and :cfunc:`PyBuffer_Release` when the buffer view can be released. -Buffer Protocol -=============== - - .. cfunction:: int PyObject_AsCharBuffer(PyObject *obj, const char **buffer, Py_ssize_t *buffer_len) Returns a pointer to a read-only memory location usable as character-based From python-checkins at python.org Wed Sep 29 01:41:31 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 01:41:31 +0200 (CEST) Subject: [Python-checkins] r85083 - in python/branches/release31-maint/Doc/c-api: abstract.rst buffer.rst concrete.rst objbuffer.rst Message-ID: <20100928234131.E8581EEA1E@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 01:41:31 2010 New Revision: 85083 Log: Merged revisions 85082 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85082 | antoine.pitrou | 2010-09-29 01:39:41 +0200 (mer., 29 sept. 2010) | 4 lines Buffers are not sequence objects (!). Put them in the abstract objects layers instead. ........ Modified: python/branches/release31-maint/Doc/c-api/abstract.rst python/branches/release31-maint/Doc/c-api/buffer.rst python/branches/release31-maint/Doc/c-api/concrete.rst python/branches/release31-maint/Doc/c-api/objbuffer.rst Modified: python/branches/release31-maint/Doc/c-api/abstract.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/abstract.rst (original) +++ python/branches/release31-maint/Doc/c-api/abstract.rst Wed Sep 29 01:41:31 2010 @@ -22,4 +22,5 @@ sequence.rst mapping.rst iter.rst + buffer.rst objbuffer.rst Modified: python/branches/release31-maint/Doc/c-api/buffer.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/buffer.rst (original) +++ python/branches/release31-maint/Doc/c-api/buffer.rst Wed Sep 29 01:41:31 2010 @@ -2,8 +2,8 @@ .. _bufferobjects: -Buffer API ----------- +Buffer Protocol +--------------- .. sectionauthor:: Greg Stein .. sectionauthor:: Benjamin Peterson @@ -50,21 +50,22 @@ section :ref:`buffer-structs`, under the description for :ctype:`PyBufferProcs`. -Buffer objects -============== +The buffer structure +==================== -Buffer objects are useful as a way to expose the binary data from another -object to the Python programmer. They can also be used as a zero-copy -slicing mechanism. Using their ability to reference a block of memory, it is -possible to expose any data to the Python programmer quite easily. The memory -could be a large, constant array in a C extension, it could be a raw block of -memory for manipulation before passing to an operating system library, or it -could be used to pass around structured data in its native, in-memory format. +Buffer structures (or simply "buffers") are useful as a way to expose the +binary data from another object to the Python programmer. They can also be +used as a zero-copy slicing mechanism. Using their ability to reference a +block of memory, it is possible to expose any data to the Python programmer +quite easily. The memory could be a large, constant array in a C extension, +it could be a raw block of memory for manipulation before passing to an +operating system library, or it could be used to pass around structured data +in its native, in-memory format. -Contrary to most data types exposed by the Python interpreter, buffer objects +Contrary to most data types exposed by the Python interpreter, buffers are not :ctype:`PyObject` pointers but rather simple C structures. This allows them to be created and copied very simply. When a generic wrapper -around a buffer object is needed, a :ref:`memoryview ` object +around a buffer is needed, a :ref:`memoryview ` object can be created. Modified: python/branches/release31-maint/Doc/c-api/concrete.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/concrete.rst (original) +++ python/branches/release31-maint/Doc/c-api/concrete.rst Wed Sep 29 01:41:31 2010 @@ -68,7 +68,6 @@ bytes.rst bytearray.rst unicode.rst - buffer.rst tuple.rst list.rst Modified: python/branches/release31-maint/Doc/c-api/objbuffer.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/objbuffer.rst (original) +++ python/branches/release31-maint/Doc/c-api/objbuffer.rst Wed Sep 29 01:41:31 2010 @@ -1,15 +1,16 @@ .. highlightlang:: c -Old buffer API --------------- +Old Buffer Protocol +------------------- .. deprecated:: 3.0 These functions were part of the "old buffer protocol" API in Python 2. -In Python 3, these functions are still exposed for ease of porting code. -They act as a compatibility wrapper around the :ref:`new buffer API -`, but they don't give you control over the lifetime of -the resources acquired when a buffer is exported. +In Python 3, this protocol doesn't exist anymore but the functions are still +exposed to ease porting 2.x code. They act as a compatibility wrapper +around the :ref:`new buffer protocol `, but they don't give +you control over the lifetime of the resources acquired when a buffer is +exported. Therefore, it is recommended that you call :cfunc:`PyObject_GetBuffer` (or the ``y*`` or ``w*`` :ref:`format codes ` with the @@ -17,10 +18,6 @@ an object, and :cfunc:`PyBuffer_Release` when the buffer view can be released. -Buffer Protocol -=============== - - .. cfunction:: int PyObject_AsCharBuffer(PyObject *obj, const char **buffer, Py_ssize_t *buffer_len) Returns a pointer to a read-only memory location usable as character-based From python-checkins at python.org Wed Sep 29 01:59:51 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 01:59:51 +0200 (CEST) Subject: [Python-checkins] r85084 - in python/branches/py3k/Doc/c-api: buffer.rst concrete.rst memoryview.rst Message-ID: <20100928235951.6FAC2EEA26@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 01:59:51 2010 New Revision: 85084 Log: Give a dedicated page to memoryview objects, so that they can be part of the concrete objects layer, while the buffer protocol is part of the abstract objects layer. Added: python/branches/py3k/Doc/c-api/memoryview.rst Modified: python/branches/py3k/Doc/c-api/buffer.rst python/branches/py3k/Doc/c-api/concrete.rst Modified: python/branches/py3k/Doc/c-api/buffer.rst ============================================================================== --- python/branches/py3k/Doc/c-api/buffer.rst (original) +++ python/branches/py3k/Doc/c-api/buffer.rst Wed Sep 29 01:59:51 2010 @@ -65,7 +65,7 @@ Contrary to most data types exposed by the Python interpreter, buffers are not :ctype:`PyObject` pointers but rather simple C structures. This allows them to be created and copied very simply. When a generic wrapper -around a buffer is needed, a :ref:`memoryview ` object +around a buffer is needed, a :ref:`memoryview ` object can be created. @@ -154,7 +154,7 @@ value. -Buffer related functions +Buffer-related functions ======================== @@ -330,49 +330,3 @@ only share a contiguous chunk of memory of "unsigned bytes" of the given length. Return 0 on success and -1 (with raising an error) on error. - -.. index:: - object: memoryview - -.. _memoryviewobjects: - -MemoryView objects -================== - -A :class:`memoryview` object exposes the C level buffer interface as a -Python object which can then be passed around like any other object. - - -.. cfunction:: PyObject *PyMemoryView_FromObject(PyObject *obj) - - Create a memoryview object from an object that defines the buffer interface. - - -.. cfunction:: PyObject *PyMemoryView_FromBuffer(Py_buffer *view) - - Create a memoryview object wrapping the given buffer-info structure *view*. - The memoryview object then owns the buffer, which means you shouldn't - try to release it yourself: it will be released on deallocation of the - memoryview object. - - -.. cfunction:: PyObject *PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order) - - Create a memoryview object to a contiguous chunk of memory (in either - 'C' or 'F'ortran *order*) from an object that defines the buffer - interface. If memory is contiguous, the memoryview object points to the - original memory. Otherwise copy is made and the memoryview points to a - new bytes object. - - -.. cfunction:: int PyMemoryView_Check(PyObject *obj) - - Return true if the object *obj* is a memoryview object. It is not - currently allowed to create subclasses of :class:`memoryview`. - - -.. cfunction:: Py_buffer *PyMemoryView_GET_BUFFER(PyObject *obj) - - Return a pointer to the buffer-info structure wrapped by the given - object. The object **must** be a memoryview instance; this macro doesn't - check its type, you must do it yourself or you will risk crashes. Modified: python/branches/py3k/Doc/c-api/concrete.rst ============================================================================== --- python/branches/py3k/Doc/c-api/concrete.rst (original) +++ python/branches/py3k/Doc/c-api/concrete.rst Wed Sep 29 01:59:51 2010 @@ -99,6 +99,7 @@ iterator.rst descriptor.rst slice.rst + memoryview.rst weakref.rst capsule.rst cell.rst Added: python/branches/py3k/Doc/c-api/memoryview.rst ============================================================================== --- (empty file) +++ python/branches/py3k/Doc/c-api/memoryview.rst Wed Sep 29 01:59:51 2010 @@ -0,0 +1,52 @@ +.. highlightlang:: c + +.. _memoryview-objects: + +.. index:: + object: memoryview + +MemoryView objects +------------------ + +A :class:`memoryview` object exposes the C level :ref:`buffer interface +` as a Python object which can then be passed around like +any other object. + + +.. cfunction:: PyObject *PyMemoryView_FromObject(PyObject *obj) + + Create a memoryview object from an object that provides the buffer interface. + If *obj* supports writable buffer exports, the memoryview object will be + readable and writable, other it will be read-only. + + +.. cfunction:: PyObject *PyMemoryView_FromBuffer(Py_buffer *view) + + Create a memoryview object wrapping the given buffer structure *view*. + The memoryview object then owns the buffer represented by *view*, which + means you shouldn't try to call :cfunc:`PyBuffer_Release` yourself: it + will be done on deallocation of the memoryview object. + + +.. cfunction:: PyObject *PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order) + + Create a memoryview object to a contiguous chunk of memory (in either + 'C' or 'F'ortran *order*) from an object that defines the buffer + interface. If memory is contiguous, the memoryview object points to the + original memory. Otherwise copy is made and the memoryview points to a + new bytes object. + + +.. cfunction:: int PyMemoryView_Check(PyObject *obj) + + Return true if the object *obj* is a memoryview object. It is not + currently allowed to create subclasses of :class:`memoryview`. + + +.. cfunction:: Py_buffer *PyMemoryView_GET_BUFFER(PyObject *obj) + + Return a pointer to the buffer structure wrapped by the given + memoryview object. The object **must** be a memoryview instance; + this macro doesn't check its type, you must do it yourself or you + will risk crashes. + From python-checkins at python.org Wed Sep 29 02:01:41 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 02:01:41 +0200 (CEST) Subject: [Python-checkins] r85085 - in python/branches/release31-maint: Doc/c-api/buffer.rst Doc/c-api/concrete.rst Doc/c-api/memoryview.rst Message-ID: <20100929000141.313D2EEA04@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 02:01:41 2010 New Revision: 85085 Log: Merged revisions 85084 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85084 | antoine.pitrou | 2010-09-29 01:59:51 +0200 (mer., 29 sept. 2010) | 5 lines Give a dedicated page to memoryview objects, so that they can be part of the concrete objects layer, while the buffer protocol is part of the abstract objects layer. ........ Added: python/branches/release31-maint/Doc/c-api/memoryview.rst - copied unchanged from r85084, /python/branches/py3k/Doc/c-api/memoryview.rst Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/c-api/buffer.rst python/branches/release31-maint/Doc/c-api/concrete.rst Modified: python/branches/release31-maint/Doc/c-api/buffer.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/buffer.rst (original) +++ python/branches/release31-maint/Doc/c-api/buffer.rst Wed Sep 29 02:01:41 2010 @@ -65,7 +65,7 @@ Contrary to most data types exposed by the Python interpreter, buffers are not :ctype:`PyObject` pointers but rather simple C structures. This allows them to be created and copied very simply. When a generic wrapper -around a buffer is needed, a :ref:`memoryview ` object +around a buffer is needed, a :ref:`memoryview ` object can be created. @@ -154,7 +154,7 @@ value. -Buffer related functions +Buffer-related functions ======================== @@ -330,49 +330,3 @@ only share a contiguous chunk of memory of "unsigned bytes" of the given length. Return 0 on success and -1 (with raising an error) on error. - -.. index:: - object: memoryview - -.. _memoryviewobjects: - -MemoryView objects -================== - -A :class:`memoryview` object exposes the C level buffer interface as a -Python object which can then be passed around like any other object. - - -.. cfunction:: PyObject *PyMemoryView_FromObject(PyObject *obj) - - Create a memoryview object from an object that defines the buffer interface. - - -.. cfunction:: PyObject *PyMemoryView_FromBuffer(Py_buffer *view) - - Create a memoryview object wrapping the given buffer-info structure *view*. - The memoryview object then owns the buffer, which means you shouldn't - try to release it yourself: it will be released on deallocation of the - memoryview object. - - -.. cfunction:: PyObject *PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order) - - Create a memoryview object to a contiguous chunk of memory (in either - 'C' or 'F'ortran *order*) from an object that defines the buffer - interface. If memory is contiguous, the memoryview object points to the - original memory. Otherwise copy is made and the memoryview points to a - new bytes object. - - -.. cfunction:: int PyMemoryView_Check(PyObject *obj) - - Return true if the object *obj* is a memoryview object. It is not - currently allowed to create subclasses of :class:`memoryview`. - - -.. cfunction:: Py_buffer *PyMemoryView_GET_BUFFER(PyObject *obj) - - Return a pointer to the buffer-info structure wrapped by the given - object. The object **must** be a memoryview instance; this macro doesn't - check its type, you must do it yourself or you will risk crashes. Modified: python/branches/release31-maint/Doc/c-api/concrete.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/concrete.rst (original) +++ python/branches/release31-maint/Doc/c-api/concrete.rst Wed Sep 29 02:01:41 2010 @@ -99,6 +99,7 @@ iterator.rst descriptor.rst slice.rst + memoryview.rst weakref.rst capsule.rst cobject.rst From python-checkins at python.org Wed Sep 29 03:08:05 2010 From: python-checkins at python.org (r.david.murray) Date: Wed, 29 Sep 2010 03:08:05 +0200 (CEST) Subject: [Python-checkins] r85086 - in python/branches/py3k: Lib/test/regrtest.py Misc/ACKS Misc/NEWS Message-ID: <20100929010805.5F5B0FBDE@mail.python.org> Author: r.david.murray Date: Wed Sep 29 03:08:05 2010 New Revision: 85086 Log: #7110: have regrtest print test failures and tracebacks to stderr not stdout. Patch by Sandro Tosi. Modified: python/branches/py3k/Lib/test/regrtest.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/test/regrtest.py ============================================================================== --- python/branches/py3k/Lib/test/regrtest.py (original) +++ python/branches/py3k/Lib/test/regrtest.py Wed Sep 29 03:08:05 2010 @@ -957,16 +957,16 @@ except KeyboardInterrupt: raise except support.TestFailed as msg: - print("test", test, "failed --", msg) - sys.stdout.flush() + print("test", test, "failed --", msg, file=sys.stderr) + sys.stderr.flush() return FAILED, test_time except: type, value = sys.exc_info()[:2] - print("test", test, "crashed --", str(type) + ":", value) - sys.stdout.flush() + print("test", test, "crashed --", str(type) + ":", value, file=sys.stderr) + sys.stderr.flush() if verbose or debug: - traceback.print_exc(file=sys.stdout) - sys.stdout.flush() + traceback.print_exc(file=sys.stderr) + sys.stderr.flush() return FAILED, test_time else: if refleak: Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Wed Sep 29 03:08:05 2010 @@ -818,6 +818,7 @@ R Lindsay Todd Bennett Todd Matias Torchinsky +Sandro Tosi Richard Townsend Laurence Tratt John Tromp Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 29 03:08:05 2010 @@ -260,6 +260,9 @@ Tests ----- +- Issue #7110: regrtest now sends test failure reports and single-failure + tracebacks to stderr rather than stdout. + - Issue #9628: fix runtests.sh -x option so more than one test can be excluded. - Issue #9899: Fix test_tkinter.test_font on various platforms. Patch by From python-checkins at python.org Wed Sep 29 03:22:20 2010 From: python-checkins at python.org (r.david.murray) Date: Wed, 29 Sep 2010 03:22:20 +0200 (CEST) Subject: [Python-checkins] r85087 - python/branches/py3k/Lib/test/test_sqlite.py Message-ID: <20100929012220.C8930EE9B8@mail.python.org> Author: r.david.murray Date: Wed Sep 29 03:22:20 2010 New Revision: 85087 Log: Have test_sqlite print version info when run in verbose mode. Modified: python/branches/py3k/Lib/test/test_sqlite.py Modified: python/branches/py3k/Lib/test/test_sqlite.py ============================================================================== --- python/branches/py3k/Lib/test/test_sqlite.py (original) +++ python/branches/py3k/Lib/test/test_sqlite.py Wed Sep 29 03:22:20 2010 @@ -1,13 +1,18 @@ -from test.support import run_unittest, import_module +from test.support import run_unittest, import_module, verbose # Skip test if _sqlite3 module not installed import_module('_sqlite3') +import sqlite3 from sqlite3.test import (dbapi, types, userfunctions, factory, transactions, hooks, regression, dump) def test_main(): + if verbose: + print("test_sqlite: testing with version", + "{!r}, sqlite_version {!r}".format(sqlite3.version, + sqlite3.sqlite_version)) run_unittest(dbapi.suite(), types.suite(), userfunctions.suite(), factory.suite(), transactions.suite(), hooks.suite(), regression.suite(), dump.suite()) From python-checkins at python.org Wed Sep 29 03:24:59 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 03:24:59 +0200 (CEST) Subject: [Python-checkins] r85088 - python/branches/py3k/Lib/linecache.py Message-ID: <20100929012459.9914FE96E@mail.python.org> Author: victor.stinner Date: Wed Sep 29 03:24:59 2010 New Revision: 85088 Log: linecache.updatecache() returns an empty list on error lines variable was not set on IOError Modified: python/branches/py3k/Lib/linecache.py Modified: python/branches/py3k/Lib/linecache.py ============================================================================== --- python/branches/py3k/Lib/linecache.py (original) +++ python/branches/py3k/Lib/linecache.py Wed Sep 29 03:24:59 2010 @@ -128,7 +128,7 @@ with open(fullname, 'r', encoding=coding) as fp: lines = fp.readlines() except IOError: - pass + lines = [] if lines and not lines[-1].endswith('\n'): lines[-1] += '\n' size, mtime = stat.st_size, stat.st_mtime From python-checkins at python.org Wed Sep 29 03:26:06 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 03:26:06 +0200 (CEST) Subject: [Python-checkins] r85089 - in python/branches/release31-maint: Lib/linecache.py Message-ID: <20100929012606.C1C24E96E@mail.python.org> Author: victor.stinner Date: Wed Sep 29 03:26:06 2010 New Revision: 85089 Log: Merged revisions 85088 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85088 | victor.stinner | 2010-09-29 03:24:59 +0200 (mer., 29 sept. 2010) | 4 lines linecache.updatecache() returns an empty list on error lines variable was not set on IOError ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/linecache.py Modified: python/branches/release31-maint/Lib/linecache.py ============================================================================== --- python/branches/release31-maint/Lib/linecache.py (original) +++ python/branches/release31-maint/Lib/linecache.py Wed Sep 29 03:26:06 2010 @@ -128,7 +128,7 @@ with open(fullname, 'r', encoding=coding) as fp: lines = fp.readlines() except IOError: - pass + lines = [] if lines and not lines[-1].endswith('\n'): lines[-1] += '\n' size, mtime = stat.st_size, stat.st_mtime From python-checkins at python.org Wed Sep 29 03:30:45 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 03:30:45 +0200 (CEST) Subject: [Python-checkins] r85090 - python/branches/py3k/Lib/linecache.py Message-ID: <20100929013045.4E186EE999@mail.python.org> Author: victor.stinner Date: Wed Sep 29 03:30:45 2010 New Revision: 85090 Log: linecache.updatecache(): don't the lines into the cache on IOError Use the same behaviour than Python 2.7. Modified: python/branches/py3k/Lib/linecache.py Modified: python/branches/py3k/Lib/linecache.py ============================================================================== --- python/branches/py3k/Lib/linecache.py (original) +++ python/branches/py3k/Lib/linecache.py Wed Sep 29 03:30:45 2010 @@ -128,7 +128,7 @@ with open(fullname, 'r', encoding=coding) as fp: lines = fp.readlines() except IOError: - lines = [] + return [] if lines and not lines[-1].endswith('\n'): lines[-1] += '\n' size, mtime = stat.st_size, stat.st_mtime From python-checkins at python.org Wed Sep 29 03:31:23 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 03:31:23 +0200 (CEST) Subject: [Python-checkins] r85091 - in python/branches/release31-maint: Lib/linecache.py Message-ID: <20100929013123.7060FEE9CC@mail.python.org> Author: victor.stinner Date: Wed Sep 29 03:31:23 2010 New Revision: 85091 Log: Merged revisions 85090 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85090 | victor.stinner | 2010-09-29 03:30:45 +0200 (mer., 29 sept. 2010) | 4 lines linecache.updatecache(): don't the lines into the cache on IOError Use the same behaviour than Python 2.7. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/linecache.py Modified: python/branches/release31-maint/Lib/linecache.py ============================================================================== --- python/branches/release31-maint/Lib/linecache.py (original) +++ python/branches/release31-maint/Lib/linecache.py Wed Sep 29 03:31:23 2010 @@ -128,7 +128,7 @@ with open(fullname, 'r', encoding=coding) as fp: lines = fp.readlines() except IOError: - lines = [] + return [] if lines and not lines[-1].endswith('\n'): lines[-1] += '\n' size, mtime = stat.st_size, stat.st_mtime From solipsis at pitrou.net Wed Sep 29 04:50:31 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 29 Sep 2010 04:50:31 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r85088): sum=5 Message-ID: py3k results for svn r85088 (hg cset b5cbb9056d86) -------------------------------------------------- test_robotparser leaked [0, 0, 5] references, sum=5 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogrMD5wA', '-x'] From python-checkins at python.org Wed Sep 29 10:38:37 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Wed, 29 Sep 2010 10:38:37 +0200 (CEST) Subject: [Python-checkins] r85092 - in python/branches/release27-maint: PC/VS7.1/rt.bat PC/VS8.0/rt.bat PCbuild/rt.bat Message-ID: <20100929083837.E27FCFDCA@mail.python.org> Author: hirokazu.yamamoto Date: Wed Sep 29 10:38:37 2010 New Revision: 85092 Log: Recorded rollback of revisions 84857,84870,84875 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/PC/VS7.1/rt.bat python/branches/release27-maint/PC/VS8.0/rt.bat python/branches/release27-maint/PCbuild/rt.bat Modified: python/branches/release27-maint/PC/VS7.1/rt.bat ============================================================================== --- python/branches/release27-maint/PC/VS7.1/rt.bat (original) +++ python/branches/release27-maint/PC/VS7.1/rt.bat Wed Sep 29 10:38:37 2010 @@ -27,7 +27,7 @@ set exe=python set qmode= set dashO= -PATH %PATH%;%~dp0..\..\..\tcltk\bin +PATH %PATH%;..\..\..\tcltk\bin :CheckOpts if "%1"=="-O" (set dashO=-O) & shift & goto CheckOpts Modified: python/branches/release27-maint/PC/VS8.0/rt.bat ============================================================================== --- python/branches/release27-maint/PC/VS8.0/rt.bat (original) +++ python/branches/release27-maint/PC/VS8.0/rt.bat Wed Sep 29 10:38:37 2010 @@ -27,7 +27,7 @@ set exe=python set qmode= set dashO= -PATH %PATH%;%~dp0..\..\..\tcltk\bin +PATH %PATH%;..\..\..\tcltk\bin :CheckOpts if "%1"=="-O" (set dashO=-O) & shift & goto CheckOpts Modified: python/branches/release27-maint/PCbuild/rt.bat ============================================================================== --- python/branches/release27-maint/PCbuild/rt.bat (original) +++ python/branches/release27-maint/PCbuild/rt.bat Wed Sep 29 10:38:37 2010 @@ -30,7 +30,7 @@ set suffix= set qmode= set dashO= -set tcltk=tcltk +set tcltk= :CheckOpts if "%1"=="-O" (set dashO=-O) & shift & goto CheckOpts @@ -38,7 +38,7 @@ if "%1"=="-d" (set suffix=_d) & shift & goto CheckOpts if "%1"=="-x64" (set prefix=amd64) & (set tcltk=tcltk64) & shift & goto CheckOpts -PATH %PATH%;%~dp0..\..\%tcltk%\bin +PATH %PATH%;..\..\%tcltk%\bin set exe=%prefix%\python%suffix% set cmd=%exe% %dashO% -Wd -3 -E -tt ../lib/test/regrtest.py %1 %2 %3 %4 %5 %6 %7 %8 %9 if defined qmode goto Qmode From python-checkins at python.org Wed Sep 29 12:25:54 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 12:25:54 +0200 (CEST) Subject: [Python-checkins] r85093 - in python/branches/py3k: Doc/c-api/unicode.rst Include/unicodeobject.h Misc/NEWS Objects/unicodeobject.c Message-ID: <20100929102554.EE76CEE9AA@mail.python.org> Author: victor.stinner Date: Wed Sep 29 12:25:54 2010 New Revision: 85093 Log: Issue #9979: Create function PyUnicode_AsWideCharString(). Modified: python/branches/py3k/Doc/c-api/unicode.rst python/branches/py3k/Include/unicodeobject.h python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Doc/c-api/unicode.rst ============================================================================== --- python/branches/py3k/Doc/c-api/unicode.rst (original) +++ python/branches/py3k/Doc/c-api/unicode.rst Wed Sep 29 12:25:54 2010 @@ -462,6 +462,19 @@ required by the application. +.. cfunction:: wchar_t* PyUnicode_AsWideCharString(PyUnicodeObject *unicode, Py_ssize_t *size) + + Convert the Unicode object to a wide character string. The output string + always ends with a nul character. If *size* is not *NULL*, write the number + of wide characters (including the nul character) into *\*size*. + + Returns a buffer allocated by :cfunc:`PyMem_Alloc` (use :cfunc:`PyMem_Free` + to free it) on success. On error, returns *NULL*, *\*size* is undefined and + raises a :exc:`MemoryError`. + + .. versionadded:: 3.2 + + .. _builtincodecs: Built-in Codecs Modified: python/branches/py3k/Include/unicodeobject.h ============================================================================== --- python/branches/py3k/Include/unicodeobject.h (original) +++ python/branches/py3k/Include/unicodeobject.h Wed Sep 29 12:25:54 2010 @@ -99,8 +99,8 @@ #endif /* If the compiler provides a wchar_t type we try to support it - through the interface functions PyUnicode_FromWideChar() and - PyUnicode_AsWideChar(). */ + through the interface functions PyUnicode_FromWideChar(), + PyUnicode_AsWideChar() and PyUnicode_AsWideCharString(). */ #ifdef HAVE_USABLE_WCHAR_T # ifndef HAVE_WCHAR_H @@ -156,6 +156,7 @@ # define PyUnicode_AsUnicode PyUnicodeUCS2_AsUnicode # define PyUnicode_AsUnicodeEscapeString PyUnicodeUCS2_AsUnicodeEscapeString # define PyUnicode_AsWideChar PyUnicodeUCS2_AsWideChar +# define PyUnicode_AsWideCharString PyUnicodeUCS2_AsWideCharString # define PyUnicode_ClearFreeList PyUnicodeUCS2_ClearFreelist # define PyUnicode_Compare PyUnicodeUCS2_Compare # define PyUnicode_CompareWithASCII PyUnicodeUCS2_CompareASCII @@ -239,6 +240,7 @@ # define PyUnicode_AsUnicode PyUnicodeUCS4_AsUnicode # define PyUnicode_AsUnicodeEscapeString PyUnicodeUCS4_AsUnicodeEscapeString # define PyUnicode_AsWideChar PyUnicodeUCS4_AsWideChar +# define PyUnicode_AsWideCharString PyUnicodeUCS4_AsWideCharString # define PyUnicode_ClearFreeList PyUnicodeUCS4_ClearFreelist # define PyUnicode_Compare PyUnicodeUCS4_Compare # define PyUnicode_CompareWithASCII PyUnicodeUCS4_CompareWithASCII @@ -570,6 +572,19 @@ Py_ssize_t size /* size of buffer */ ); +/* Convert the Unicode object to a wide character string. The output string + always ends with a nul character. If size is not NULL, write the number of + wide characters (including the nul character) into *size. + + Returns a buffer allocated by PyMem_Alloc() (use PyMem_Free() to free it) + on success. On error, returns NULL, *size is undefined and raises a + MemoryError. */ + +PyAPI_FUNC(wchar_t*) PyUnicode_AsWideCharString( + PyUnicodeObject *unicode, /* Unicode object */ + Py_ssize_t *size /* number of characters of the result */ + ); + #endif /* --- Unicode ordinals --------------------------------------------------- */ Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 29 12:25:54 2010 @@ -10,6 +10,8 @@ Core and Builtins ----------------- +- Issue #9979: Create function PyUnicode_AsWideCharString(). + - Issue #7397: Mention that importlib.import_module() is probably what someone really wants to be using in __import__'s docstring. Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Wed Sep 29 12:25:54 2010 @@ -1153,9 +1153,26 @@ return ret; } -Py_ssize_t PyUnicode_AsWideChar(PyUnicodeObject *unicode, - wchar_t *w, - Py_ssize_t size) +static void +unicode_aswidechar(PyUnicodeObject *unicode, + wchar_t *w, + Py_ssize_t size) +{ +#if Py_UNICODE_SIZE == SIZEOF_WCHAR_T + memcpy(w, unicode->str, size * sizeof(wchar_t)); +#else + register Py_UNICODE *u; + register Py_ssize_t i; + u = PyUnicode_AS_UNICODE(unicode); + for (i = size; i > 0; i--) + *w++ = *u++; +#endif +} + +Py_ssize_t +PyUnicode_AsWideChar(PyUnicodeObject *unicode, + wchar_t *w, + Py_ssize_t size) { if (unicode == NULL) { PyErr_BadInternalCall(); @@ -1166,17 +1183,7 @@ if (size > PyUnicode_GET_SIZE(unicode)) size = PyUnicode_GET_SIZE(unicode) + 1; -#if Py_UNICODE_SIZE == SIZEOF_WCHAR_T - memcpy(w, unicode->str, size * sizeof(wchar_t)); -#else - { - register Py_UNICODE *u; - register Py_ssize_t i; - u = PyUnicode_AS_UNICODE(unicode); - for (i = size; i > 0; i--) - *w++ = *u++; - } -#endif + unicode_aswidechar(unicode, w, size); if (size > PyUnicode_GET_SIZE(unicode)) return PyUnicode_GET_SIZE(unicode); @@ -1184,6 +1191,33 @@ return size; } +wchar_t* +PyUnicode_AsWideCharString(PyUnicodeObject *unicode, + Py_ssize_t *size) +{ + wchar_t* buffer; + Py_ssize_t buflen; + + if (unicode == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + + if ((PY_SSIZE_T_MAX / sizeof(wchar_t) - 1) < PyUnicode_GET_SIZE(unicode)) { + PyErr_NoMemory(); + return NULL; + } + + buflen = PyUnicode_GET_SIZE(unicode) + 1; /* copy L'\0' */ + buffer = PyMem_MALLOC(buflen * sizeof(wchar_t)); + if (buffer == NULL) { + PyErr_NoMemory(); + return NULL; + } + unicode_aswidechar(unicode, buffer, buflen); + return buffer; +} + #endif PyObject *PyUnicode_FromOrdinal(int ordinal) From python-checkins at python.org Wed Sep 29 12:28:52 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 12:28:52 +0200 (CEST) Subject: [Python-checkins] r85094 - python/branches/py3k/Python/import.c Message-ID: <20100929102852.24D66D2FD@mail.python.org> Author: victor.stinner Date: Wed Sep 29 12:28:51 2010 New Revision: 85094 Log: Issue #9979: Use PyUnicode_AsWideCharString() in import.c Don't truncate path if it is too long anymore, and allocate fewer memory (but allocate it on the heap, not on the stack). Modified: python/branches/py3k/Python/import.c Modified: python/branches/py3k/Python/import.c ============================================================================== --- python/branches/py3k/Python/import.c (original) +++ python/branches/py3k/Python/import.c Wed Sep 29 12:28:51 2010 @@ -1961,21 +1961,21 @@ _Py_fopen(PyObject *unicode, const char *mode) { #ifdef MS_WINDOWS - wchar_t path[MAXPATHLEN+1]; + wchar_t *path; wchar_t wmode[10]; - Py_ssize_t len; int usize; - - len = PyUnicode_AsWideChar((PyUnicodeObject*)unicode, path, MAXPATHLEN); - if (len == -1) - return NULL; - path[len] = L'\0'; + FILE *f; usize = MultiByteToWideChar(CP_ACP, 0, mode, -1, wmode, sizeof(wmode)); if (usize == 0) return NULL; - return _wfopen(path, wmode); + path = PyUnicode_AsWideCharString((PyUnicodeObject*)unicode, NULL); + if (path == NULL) + return NULL; + f = _wfopen(path, wmode); + PyMem_Free(path); + return f; #else FILE *f; PyObject *bytes = PyUnicode_EncodeFSDefault(unicode); @@ -1997,17 +1997,15 @@ _Py_stat(PyObject *unicode, struct stat *statbuf) { #ifdef MS_WINDOWS - wchar_t path[MAXPATHLEN+1]; - Py_ssize_t len; + wchar_t *path; int err; struct _stat wstatbuf; - len = PyUnicode_AsWideChar((PyUnicodeObject*)unicode, path, MAXPATHLEN); - if (len == -1) + path = PyUnicode_AsWideCharString((PyUnicodeObject*)unicode, NULL); + if (path == NULL) return -1; - path[len] = L'\0'; - err = _wstat(path, &wstatbuf); + PyMem_Free(path); if (!err) statbuf->st_mode = wstatbuf.st_mode; return err; @@ -3724,7 +3722,7 @@ #else /* MS_WINDOWS */ PyObject *pathobj; DWORD rv; - wchar_t path[MAXPATHLEN+1]; + wchar_t *path; Py_ssize_t len; if (!_PyArg_NoKeywords("NullImporter()", kwds)) @@ -3739,15 +3737,15 @@ return -1; } - len = PyUnicode_AsWideChar((PyUnicodeObject*)pathobj, - path, sizeof(path) / sizeof(path[0])); - if (len == -1) + path = PyUnicode_AsWideCharString((PyUnicodeObject*)pathobj, NULL); + if (path == NULL) return -1; /* see issue1293 and issue3677: * stat() on Windows doesn't recognise paths like * "e:\\shared\\" and "\\\\whiterab-c2znlh\\shared" as dirs. */ rv = GetFileAttributesW(path); + PyMem_Free(path); if (rv != INVALID_FILE_ATTRIBUTES) { /* it exists */ if (rv & FILE_ATTRIBUTE_DIRECTORY) { From python-checkins at python.org Wed Sep 29 12:30:43 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 12:30:43 +0200 (CEST) Subject: [Python-checkins] r85095 - python/branches/py3k/Modules/_localemodule.c Message-ID: <20100929103043.B038BF92E@mail.python.org> Author: victor.stinner Date: Wed Sep 29 12:30:43 2010 New Revision: 85095 Log: Issue #9979: Use PyUnicode_AsWideCharString() for _locale.strcoll() It simplifies the code and prepare the surrogates support. Modified: python/branches/py3k/Modules/_localemodule.c Modified: python/branches/py3k/Modules/_localemodule.c ============================================================================== --- python/branches/py3k/Modules/_localemodule.c (original) +++ python/branches/py3k/Modules/_localemodule.c Wed Sep 29 12:30:43 2010 @@ -242,29 +242,16 @@ { PyObject *os1, *os2, *result = NULL; wchar_t *ws1 = NULL, *ws2 = NULL; - Py_ssize_t len1, len2; if (!PyArg_ParseTuple(args, "UU:strcoll", &os1, &os2)) return NULL; /* Convert the unicode strings to wchar[]. */ - len1 = PyUnicode_GET_SIZE(os1) + 1; - ws1 = PyMem_MALLOC(len1 * sizeof(wchar_t)); - if (!ws1) { - PyErr_NoMemory(); - goto done; - } - if (PyUnicode_AsWideChar((PyUnicodeObject*)os1, ws1, len1) == -1) + ws1 = PyUnicode_AsWideCharString((PyUnicodeObject*)os1, NULL); + if (ws1 == NULL) goto done; - ws1[len1 - 1] = 0; - len2 = PyUnicode_GET_SIZE(os2) + 1; - ws2 = PyMem_MALLOC(len2 * sizeof(wchar_t)); - if (!ws2) { - PyErr_NoMemory(); - goto done; - } - if (PyUnicode_AsWideChar((PyUnicodeObject*)os2, ws2, len2) == -1) + ws2 = PyUnicode_AsWideCharString((PyUnicodeObject*)os2, NULL); + if (ws2 == NULL) goto done; - ws2[len2 - 1] = 0; /* Collate the strings. */ result = PyLong_FromLong(wcscoll(ws1, ws2)); done: From python-checkins at python.org Wed Sep 29 12:34:19 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 12:34:19 +0200 (CEST) Subject: [Python-checkins] r85096 - python/branches/py3k/Modules/timemodule.c Message-ID: <20100929103419.7FCDDFE09@mail.python.org> Author: victor.stinner Date: Wed Sep 29 12:34:19 2010 New Revision: 85096 Log: Issue #9979: Use PyUnicode_AsWideCharString() in time.strftime() Allocate memory with PyMem_Alloc() instead of the PyBytes API. Prepare the surrogates support. Modified: python/branches/py3k/Modules/timemodule.c Modified: python/branches/py3k/Modules/timemodule.c ============================================================================== --- python/branches/py3k/Modules/timemodule.c (original) +++ python/branches/py3k/Modules/timemodule.c Wed Sep 29 12:34:19 2010 @@ -394,10 +394,15 @@ PyObject *tup = NULL; struct tm buf; const time_char *fmt; - PyObject *format, *tmpfmt; +#ifdef HAVE_WCSFTIME + wchar_t *format; +#else + PyObject *format; +#endif size_t fmtlen, buflen; - time_char *outbuf = 0; + time_char *outbuf = NULL; size_t i; + PyObject *ret = NULL; memset((void *) &buf, '\0', sizeof(buf)); @@ -482,19 +487,10 @@ buf.tm_isdst = 1; #ifdef HAVE_WCSFTIME - tmpfmt = PyBytes_FromStringAndSize(NULL, - sizeof(wchar_t) * (PyUnicode_GetSize(format)+1)); - if (!tmpfmt) - return NULL; - /* This assumes that PyUnicode_AsWideChar doesn't do any UTF-16 - expansion. */ - if (PyUnicode_AsWideChar((PyUnicodeObject*)format, - (wchar_t*)PyBytes_AS_STRING(tmpfmt), - PyUnicode_GetSize(format)+1) == (size_t)-1) - /* This shouldn't fail. */ - Py_FatalError("PyUnicode_AsWideChar failed"); - format = tmpfmt; - fmt = (wchar_t*)PyBytes_AS_STRING(format); + format = PyUnicode_AsWideCharString((PyUnicodeObject*)format, NULL); + if (format == NULL) + return NULL; + fmt = format; #else /* Convert the unicode string to an ascii one */ format = PyUnicode_AsEncodedString(format, TZNAME_ENCODING, NULL); @@ -528,8 +524,8 @@ for (i = 1024; ; i += i) { outbuf = (time_char *)PyMem_Malloc(i*sizeof(time_char)); if (outbuf == NULL) { - Py_DECREF(format); - return PyErr_NoMemory(); + PyErr_NoMemory(); + break; } buflen = format_time(outbuf, i, fmt, &buf); if (buflen > 0 || i >= 256 * fmtlen) { @@ -538,7 +534,6 @@ More likely, the format yields an empty result, e.g. an empty format, or %Z when the timezone is unknown. */ - PyObject *ret; #ifdef HAVE_WCSFTIME ret = PyUnicode_FromWideChar(outbuf, buflen); #else @@ -546,19 +541,23 @@ TZNAME_ENCODING, NULL); #endif PyMem_Free(outbuf); - Py_DECREF(format); - return ret; + break; } PyMem_Free(outbuf); #if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__) /* VisualStudio .NET 2005 does this properly */ if (buflen == 0 && errno == EINVAL) { PyErr_SetString(PyExc_ValueError, "Invalid format string"); - Py_DECREF(format); - return 0; + break; } #endif } +#ifdef HAVE_WCSFTIME + PyMem_Free(format); +#else + Py_DECREF(format); +#endif + return ret; } #undef time_char From python-checkins at python.org Wed Sep 29 12:37:16 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 12:37:16 +0200 (CEST) Subject: [Python-checkins] r85097 - in python/branches/py3k/Modules/_ctypes: callproc.c cfield.c Message-ID: <20100929103716.8F66AF000@mail.python.org> Author: victor.stinner Date: Wed Sep 29 12:37:16 2010 New Revision: 85097 Log: Issue #9979: Use PyUnicode_AsWideCharString() in _ctypes module * Convert unicode to wide character string before creating the PyCapsule object * Catch integer overflow * Avoid useless memset() * Prepare the support of surrogates Modified: python/branches/py3k/Modules/_ctypes/callproc.c python/branches/py3k/Modules/_ctypes/cfield.c Modified: python/branches/py3k/Modules/_ctypes/callproc.c ============================================================================== --- python/branches/py3k/Modules/_ctypes/callproc.c (original) +++ python/branches/py3k/Modules/_ctypes/callproc.c Wed Sep 29 12:37:16 2010 @@ -665,24 +665,15 @@ pa->keep = obj; return 0; #else - int size = PyUnicode_GET_SIZE(obj); pa->ffi_type = &ffi_type_pointer; - size += 1; /* terminating NUL */ - size *= sizeof(wchar_t); - pa->value.p = PyMem_Malloc(size); - if (!pa->value.p) { - PyErr_NoMemory(); + pa->value.p = PyUnicode_AsWideCharString((PyUnicodeObject *)obj, NULL); + if (pa->value.p == NULL) return -1; - } - memset(pa->value.p, 0, size); pa->keep = PyCapsule_New(pa->value.p, CTYPES_CAPSULE_NAME_PYMEM, pymem_destructor); if (!pa->keep) { PyMem_Free(pa->value.p); return -1; } - if (-1 == PyUnicode_AsWideChar((PyUnicodeObject *)obj, - pa->value.p, PyUnicode_GET_SIZE(obj))) - return -1; return 0; #endif } @@ -1147,7 +1138,7 @@ } for (i = 0; i < argcount; ++i) { atypes[i] = args[i].ffi_type; - if (atypes[i]->type == FFI_TYPE_STRUCT + if (atypes[i]->type == FFI_TYPE_STRUCT #ifdef _WIN64 && atypes[i]->size <= sizeof(void *) #endif Modified: python/branches/py3k/Modules/_ctypes/cfield.c ============================================================================== --- python/branches/py3k/Modules/_ctypes/cfield.c (original) +++ python/branches/py3k/Modules/_ctypes/cfield.c Wed Sep 29 12:37:16 2010 @@ -1433,15 +1433,11 @@ PyObject *keep; wchar_t *buffer; - int size = PyUnicode_GET_SIZE(value); - size += 1; /* terminating NUL */ - size *= sizeof(wchar_t); - buffer = (wchar_t *)PyMem_Malloc(size); + buffer = PyUnicode_AsWideCharString((PyUnicodeObject *)value, NULL); if (!buffer) { Py_DECREF(value); - return PyErr_NoMemory(); + return NULL; } - memset(buffer, 0, size); keep = PyCapsule_New(buffer, CTYPES_CFIELD_CAPSULE_NAME_PYMEM, pymem_destructor); if (!keep) { Py_DECREF(value); @@ -1449,12 +1445,6 @@ return NULL; } *(wchar_t **)ptr = (wchar_t *)buffer; - if (-1 == PyUnicode_AsWideChar((PyUnicodeObject *)value, - buffer, PyUnicode_GET_SIZE(value))) { - Py_DECREF(value); - Py_DECREF(keep); - return NULL; - } Py_DECREF(value); return keep; } From python-checkins at python.org Wed Sep 29 12:40:34 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Wed, 29 Sep 2010 12:40:34 +0200 (CEST) Subject: [Python-checkins] r85098 - python/branches/py3k-issue9978 Message-ID: <20100929104034.A8D13FAF8@mail.python.org> Author: hirokazu.yamamoto Date: Wed Sep 29 12:40:34 2010 New Revision: 85098 Log: Temporary branch for Issue #9978. Added: python/branches/py3k-issue9978/ - copied from r85097, /python/branches/py3k/ From python-checkins at python.org Wed Sep 29 12:43:41 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Wed, 29 Sep 2010 12:43:41 +0200 (CEST) Subject: [Python-checkins] r85099 - python/branches/py3k-issue9978/Lib/ctypes/wintypes.py Message-ID: <20100929104341.C06B2EE9CB@mail.python.org> Author: hirokazu.yamamoto Date: Wed Sep 29 12:43:41 2010 New Revision: 85099 Log: Remove *ctypes* Modified: python/branches/py3k-issue9978/Lib/ctypes/wintypes.py Modified: python/branches/py3k-issue9978/Lib/ctypes/wintypes.py ============================================================================== --- python/branches/py3k-issue9978/Lib/ctypes/wintypes.py (original) +++ python/branches/py3k-issue9978/Lib/ctypes/wintypes.py Wed Sep 29 12:43:41 2010 @@ -200,3 +200,5 @@ LPWIN32_FIND_DATAA = PWIN32_FIND_DATAA = ctypes.POINTER(WIN32_FIND_DATAA) LPWIN32_FIND_DATAW = PWIN32_FIND_DATAW = ctypes.POINTER(WIN32_FIND_DATAW) LPWORD = PWORD = ctypes.POINTER(WORD) + +del ctypes From python-checkins at python.org Wed Sep 29 12:49:46 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 12:49:46 +0200 (CEST) Subject: [Python-checkins] r85100 - in python/branches/py3k: Doc/library/gzip.rst Lib/gzip.py Lib/test/test_gzip.py Misc/NEWS Message-ID: <20100929104946.8EE22F7BB@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 12:49:46 2010 New Revision: 85100 Log: Issue #9962: GzipFile now has the peek() method. Modified: python/branches/py3k/Doc/library/gzip.rst python/branches/py3k/Lib/gzip.py python/branches/py3k/Lib/test/test_gzip.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/gzip.rst ============================================================================== --- python/branches/py3k/Doc/library/gzip.rst (original) +++ python/branches/py3k/Doc/library/gzip.rst Wed Sep 29 12:49:46 2010 @@ -25,10 +25,10 @@ .. class:: GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None) - Constructor for the :class:`GzipFile` class, which simulates most of the methods - of a :term:`file object`, with the exception of the :meth:`readinto` and - :meth:`truncate` methods. At least one of *fileobj* and *filename* must be - given a non-trivial value. + Constructor for the :class:`GzipFile` class, which simulates most of the + methods of a :term:`file object`, with the exception of the :meth:`truncate` + method. At least one of *fileobj* and *filename* must be given a non-trivial + value. The new class instance is based on *fileobj*, which can be a regular file, a :class:`StringIO` object, or any other object which simulates a file. It @@ -66,8 +66,9 @@ writing as *fileobj*, and retrieve the resulting memory buffer using the :class:`io.BytesIO` object's :meth:`~io.BytesIO.getvalue` method. - :class:`GzipFile` supports the whole :class:`io.BufferedIOBase` interface, - including iteration and the :keyword:`with` statement. + :class:`GzipFile` supports the :class:`io.BufferedIOBase` interface, + including iteration and the :keyword:`with` statement. Only the + :meth:`truncate` method isn't implemented. .. versionchanged:: 3.1 Support for the :keyword:`with` statement was added. @@ -78,6 +79,9 @@ .. versionchanged:: 3.2 Support for unseekable files was added. + .. versionchanged:: 3.2 + The :meth:`peek` method was implemented. + .. function:: open(filename, mode='rb', compresslevel=9) Modified: python/branches/py3k/Lib/gzip.py ============================================================================== --- python/branches/py3k/Lib/gzip.py (original) +++ python/branches/py3k/Lib/gzip.py Wed Sep 29 12:49:46 2010 @@ -204,7 +204,10 @@ return self.name def __repr__(self): - s = repr(self.fileobj) + fileobj = self.fileobj + if isinstance(fileobj, _PaddedFile): + fileobj = fileobj.file + s = repr(fileobj) return '' def _init_write(self, filename): @@ -336,6 +339,26 @@ self.offset += size return chunk + def peek(self, n): + if self.mode != READ: + import errno + raise IOError(errno.EBADF, "read() on write-only GzipFile object") + + # Do not return ridiculously small buffers + if n < 100: + n = 100 + if self.extrasize == 0: + if self.fileobj is None: + return b'' + try: + self._read(max(self.max_read_chunk, n)) + except EOFError: + pass + offset = self.offset - self.extrastart + remaining = self.extrasize + assert remaining == len(self.extrabuf) - offset + return self.extrabuf[offset:offset + n] + def _unread(self, buf): self.extrasize = len(buf) + self.extrasize self.offset -= len(buf) Modified: python/branches/py3k/Lib/test/test_gzip.py ============================================================================== --- python/branches/py3k/Lib/test/test_gzip.py (original) +++ python/branches/py3k/Lib/test/test_gzip.py Wed Sep 29 12:49:46 2010 @@ -286,6 +286,28 @@ with gzip.GzipFile(fileobj=buf, mode="rb") as f: self.assertEqual(f.read(), uncompressed) + def test_peek(self): + uncompressed = data1 * 200 + with gzip.GzipFile(self.filename, "wb") as f: + f.write(uncompressed) + + def sizes(): + while True: + for n in range(5, 50, 10): + yield n + + with gzip.GzipFile(self.filename, "rb") as f: + f.max_read_chunk = 33 + nread = 0 + for n in sizes(): + s = f.peek(n) + if s == b'': + break + self.assertEqual(f.read(len(s)), s) + nread += len(s) + self.assertEqual(f.read(100), b'') + self.assertEqual(nread, len(uncompressed)) + # Testing compress/decompress shortcut functions def test_compress(self): Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 29 12:49:46 2010 @@ -76,6 +76,8 @@ Library ------- +- Issue #9962: GzipFile now has the peek() method. + - Issue #9090: When a socket with a timeout fails with EWOULDBLOCK or EAGAIN, retry the select() loop instead of bailing out. This is because select() can incorrectly report a socket as ready for reading (for example, if it From python-checkins at python.org Wed Sep 29 12:59:32 2010 From: python-checkins at python.org (tarek.ziade) Date: Wed, 29 Sep 2010 12:59:32 +0200 Subject: [Python-checkins] distutils2: Make the upload_docs command fallback on docs/ if doc/ is not found Message-ID: tarek.ziade pushed 37f948f6be6c to distutils2: http://hg.python.org/distutils2/rev/37f948f6be6c changeset: 690:37f948f6be6c tag: tip user: anatoly techtonik date: Wed Sep 29 11:38:40 2010 +0300 summary: Make the upload_docs command fallback on docs/ if doc/ is not found files: distutils2/command/upload_docs.py, distutils2/tests/test_upload_docs.py, docs/source/distutils/newcommands.rst diff --git a/distutils2/command/upload_docs.py b/distutils2/command/upload_docs.py --- a/distutils2/command/upload_docs.py +++ b/distutils2/command/upload_docs.py @@ -84,6 +84,8 @@ if self.upload_dir is None: build = self.get_finalized_command('build') self.upload_dir = os.path.join(build.build_base, "docs") + if not os.path.isdir(self.upload_dir): + self.upload_dir = os.path.join(build.build_base, "doc") self.announce('Using upload directory %s' % self.upload_dir) self.verify_upload_dir(self.upload_dir) config = read_pypirc(self.repository, self.realm) diff --git a/distutils2/tests/test_upload_docs.py b/distutils2/tests/test_upload_docs.py --- a/distutils2/tests/test_upload_docs.py +++ b/distutils2/tests/test_upload_docs.py @@ -76,6 +76,19 @@ finally: os.chdir(previous) + def test_default_uploaddir_looks_for_doc_also(self): + sandbox = self.mkdtemp() + previous = os.getcwd() + os.chdir(sandbox) + try: + os.mkdir("build") + self.prepare_sample_dir("build") + os.rename(os.path.join("build", "docs"), os.path.join("build", "doc")) + self.cmd.ensure_finalized() + self.assertEqual(self.cmd.upload_dir, os.path.join("build", "doc")) + finally: + os.chdir(previous) + def prepare_sample_dir(self, sample_dir=None): if sample_dir is None: sample_dir = self.mkdtemp() diff --git a/docs/source/distutils/newcommands.rst b/docs/source/distutils/newcommands.rst --- a/docs/source/distutils/newcommands.rst +++ b/docs/source/distutils/newcommands.rst @@ -130,8 +130,8 @@ The ``upload_docs`` command has the following options: ``--upload-dir`` - The directory to be uploaded to the repository. The default value is - ``docs`` in project root. + The directory to be uploaded to the repository. By default documentation + is searched for in ``docs`` (or ``doc``) directory in project root. ``--show-response`` Display the full response text from server; this is useful for debugging -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Wed Sep 29 13:24:23 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 13:24:23 +0200 (CEST) Subject: [Python-checkins] r85101 - in python/branches/py3k/Doc/library: http.client.rst urllib.request.rst Message-ID: <20100929112423.4EFA7EE9AB@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 13:24:21 2010 New Revision: 85101 Log: Issue #9983: warn that urllib and httplib don't perform SSL certificate validation. Modified: python/branches/py3k/Doc/library/http.client.rst python/branches/py3k/Doc/library/urllib.request.rst Modified: python/branches/py3k/Doc/library/http.client.rst ============================================================================== --- python/branches/py3k/Doc/library/http.client.rst (original) +++ python/branches/py3k/Doc/library/http.client.rst Wed Sep 29 13:24:21 2010 @@ -53,13 +53,13 @@ .. class:: HTTPSConnection(host, port=None, key_file=None, cert_file=None, strict=None[, timeout[, source_address]]) A subclass of :class:`HTTPConnection` that uses SSL for communication with - secure servers. Default port is ``443``. *key_file* is the name of a PEM - formatted file that contains your private key. *cert_file* is a PEM formatted - certificate chain file. + secure servers. Default port is ``443``. *key_file* is the name of a PEM + formatted file that contains your private key, and *cert_file* is a PEM + formatted certificate chain file; both can be used for authenticating + yourself against the server. - .. note:: - - This does not do any certificate verification. + .. warning:: + This does not do any verification of the server's certificate. .. versionchanged:: 3.2 *source_address* was added. Modified: python/branches/py3k/Doc/library/urllib.request.rst ============================================================================== --- python/branches/py3k/Doc/library/urllib.request.rst (original) +++ python/branches/py3k/Doc/library/urllib.request.rst Wed Sep 29 13:24:21 2010 @@ -11,6 +11,10 @@ opening URLs (mostly HTTP) in a complex world --- basic and digest authentication, redirections, cookies and more. +.. warning:: When opening HTTPS (or FTPS) URLs, it is not attempted to + validate the server certificate. Use at your own risk! + + The :mod:`urllib.request` module defines the following functions: From python-checkins at python.org Wed Sep 29 13:25:47 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 13:25:47 +0200 (CEST) Subject: [Python-checkins] r85102 - in python/branches/release31-maint: Doc/library/http.client.rst Doc/library/urllib.request.rst Message-ID: <20100929112547.4F300EE9DA@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 13:25:47 2010 New Revision: 85102 Log: Merged revisions 85101 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85101 | antoine.pitrou | 2010-09-29 13:24:21 +0200 (mer., 29 sept. 2010) | 3 lines Issue #9983: warn that urllib and httplib don't perform SSL certificate validation. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/http.client.rst python/branches/release31-maint/Doc/library/urllib.request.rst Modified: python/branches/release31-maint/Doc/library/http.client.rst ============================================================================== --- python/branches/release31-maint/Doc/library/http.client.rst (original) +++ python/branches/release31-maint/Doc/library/http.client.rst Wed Sep 29 13:25:47 2010 @@ -48,13 +48,13 @@ .. class:: HTTPSConnection(host, port=None, key_file=None, cert_file=None, strict=None[, timeout]) A subclass of :class:`HTTPConnection` that uses SSL for communication with - secure servers. Default port is ``443``. *key_file* is the name of a PEM - formatted file that contains your private key. *cert_file* is a PEM formatted - certificate chain file. + secure servers. Default port is ``443``. *key_file* is the name of a PEM + formatted file that contains your private key, and *cert_file* is a PEM + formatted certificate chain file; both can be used for authenticating + yourself against the server. - .. note:: - - This does not do any certificate verification. + .. warning:: + This does not do any verification of the server's certificate. .. class:: HTTPResponse(sock, debuglevel=0, strict=0, method=None, url=None) Modified: python/branches/release31-maint/Doc/library/urllib.request.rst ============================================================================== --- python/branches/release31-maint/Doc/library/urllib.request.rst (original) +++ python/branches/release31-maint/Doc/library/urllib.request.rst Wed Sep 29 13:25:47 2010 @@ -11,6 +11,10 @@ opening URLs (mostly HTTP) in a complex world --- basic and digest authentication, redirections, cookies and more. +.. warning:: When opening HTTPS (or FTPS) URLs, it is not attempted to + validate the server certificate. Use at your own risk! + + The :mod:`urllib.request` module defines the following functions: From python-checkins at python.org Wed Sep 29 13:30:52 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 13:30:52 +0200 (CEST) Subject: [Python-checkins] r85103 - in python/branches/release27-maint: Doc/library/httplib.rst Doc/library/urllib.rst Doc/library/urllib2.rst Message-ID: <20100929113052.5996DFD17@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 13:30:52 2010 New Revision: 85103 Log: Merged revisions 85101 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85101 | antoine.pitrou | 2010-09-29 13:24:21 +0200 (mer., 29 sept. 2010) | 3 lines Issue #9983: warn that urllib and httplib don't perform SSL certificate validation. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/httplib.rst python/branches/release27-maint/Doc/library/urllib.rst python/branches/release27-maint/Doc/library/urllib2.rst Modified: python/branches/release27-maint/Doc/library/httplib.rst ============================================================================== --- python/branches/release27-maint/Doc/library/httplib.rst (original) +++ python/branches/release27-maint/Doc/library/httplib.rst Wed Sep 29 13:30:52 2010 @@ -73,9 +73,8 @@ formatted file that contains your private key. *cert_file* is a PEM formatted certificate chain file. - .. note:: - - This does not do any certificate verification. + .. warning:: + This does not do any verification of the server's certificate. .. versionadded:: 2.0 Modified: python/branches/release27-maint/Doc/library/urllib.rst ============================================================================== --- python/branches/release27-maint/Doc/library/urllib.rst (original) +++ python/branches/release27-maint/Doc/library/urllib.rst Wed Sep 29 13:30:52 2010 @@ -23,6 +23,10 @@ instead of filenames. Some restrictions apply --- it can only open URLs for reading, and no seek operations are available. +.. warning:: When opening HTTPS URLs, it is not attempted to validate the + server certificate. Use at your own risk! + + High-level interface -------------------- Modified: python/branches/release27-maint/Doc/library/urllib2.rst ============================================================================== --- python/branches/release27-maint/Doc/library/urllib2.rst (original) +++ python/branches/release27-maint/Doc/library/urllib2.rst Wed Sep 29 13:30:52 2010 @@ -18,6 +18,9 @@ URLs (mostly HTTP) in a complex world --- basic and digest authentication, redirections, cookies and more. +.. warning:: When opening HTTPS (or FTPS) URLs, it is not attempted to + validate the server certificate. Use at your own risk! + The :mod:`urllib2` module defines the following functions: From python-checkins at python.org Wed Sep 29 14:36:15 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Wed, 29 Sep 2010 14:36:15 +0200 (CEST) Subject: [Python-checkins] r85104 - in python/branches/py3k-issue9978/Lib/test: test_os.py win_console_handler.py Message-ID: <20100929123615.1A8B2EE9D8@mail.python.org> Author: hirokazu.yamamoto Date: Wed Sep 29 14:36:14 2010 New Revision: 85104 Log: Wait for win_console_handler reaches before dead loop. (Otherwise, test_os.py didn't fail on slow machine because process was killed before wintypes.WINFUNCTYPE was reached) # I suppose Win32 CreateEvent is better. Modified: python/branches/py3k-issue9978/Lib/test/test_os.py python/branches/py3k-issue9978/Lib/test/win_console_handler.py Modified: python/branches/py3k-issue9978/Lib/test/test_os.py ============================================================================== --- python/branches/py3k-issue9978/Lib/test/test_os.py (original) +++ python/branches/py3k-issue9978/Lib/test/test_os.py Wed Sep 29 14:36:14 2010 @@ -13,6 +13,7 @@ import shutil from test import support import contextlib +from win_console_handler import create_mmap # Detect whether we're on a Linux system that uses the (now outdated # and unmaintained) linuxthreads threading library. There's an issue @@ -1029,13 +1030,16 @@ self._kill(100) def _kill_with_event(self, event, name): + m = create_mmap() + m[0] = 0 # Run a script which has console control handling enabled. proc = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), "win_console_handler.py")], creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) # Let the interpreter startup before we send signals. See #3137. - time.sleep(0.5) + while m[0] == 0: + time.sleep(0.5) os.kill(proc.pid, event) # proc.send_signal(event) could also be done here. # Allow time for the signal to be passed and the process to exit. Modified: python/branches/py3k-issue9978/Lib/test/win_console_handler.py ============================================================================== --- python/branches/py3k-issue9978/Lib/test/win_console_handler.py (original) +++ python/branches/py3k-issue9978/Lib/test/win_console_handler.py Wed Sep 29 14:36:14 2010 @@ -8,36 +8,46 @@ similar example in C. """ -from ctypes import wintypes -import signal -import ctypes - -# Function prototype for the handler function. Returns BOOL, takes a DWORD. -HandlerRoutine = wintypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) - -def _ctrl_handler(sig): - """Handle a sig event and return 0 to terminate the process""" - if sig == signal.CTRL_C_EVENT: - pass - elif sig == signal.CTRL_BREAK_EVENT: - pass - else: - print("UNKNOWN EVENT") - return 0 +import mmap -ctrl_handler = HandlerRoutine(_ctrl_handler) +def create_mmap(): + return mmap.mmap(-1, 1, "Python/Lib/test/win_console_handler.py") +if __name__ == "__main__": + from ctypes import wintypes + import signal + import ctypes + + # Function prototype for the handler function. Returns BOOL, takes a DWORD. + HandlerRoutine = wintypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) + + def _ctrl_handler(sig): + """Handle a sig event and return 0 to terminate the process""" + if sig == signal.CTRL_C_EVENT: + pass + elif sig == signal.CTRL_BREAK_EVENT: + pass + else: + print("UNKNOWN EVENT") + return 0 + + ctrl_handler = HandlerRoutine(_ctrl_handler) + + + SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler + SetConsoleCtrlHandler.argtypes = (HandlerRoutine, wintypes.BOOL) + SetConsoleCtrlHandler.restype = wintypes.BOOL -SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler -SetConsoleCtrlHandler.argtypes = (HandlerRoutine, wintypes.BOOL) -SetConsoleCtrlHandler.restype = wintypes.BOOL -if __name__ == "__main__": # Add our console control handling function with value 1 if not SetConsoleCtrlHandler(ctrl_handler, 1): print("Unable to add SetConsoleCtrlHandler") exit(-1) + # Awake waiting main process + m = create_mmap() + m[0] = 1 + # Do nothing but wait for the signal while True: pass From python-checkins at python.org Wed Sep 29 14:39:06 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Wed, 29 Sep 2010 14:39:06 +0200 (CEST) Subject: [Python-checkins] r85105 - python/branches/py3k-issue9978/Lib/test/win_console_handler.py Message-ID: <20100929123906.EB3D8EE9E6@mail.python.org> Author: hirokazu.yamamoto Date: Wed Sep 29 14:39:06 2010 New Revision: 85105 Log: WINFUNCTYPE is not in ctypes.wintypes but in ctypes. Modified: python/branches/py3k-issue9978/Lib/test/win_console_handler.py Modified: python/branches/py3k-issue9978/Lib/test/win_console_handler.py ============================================================================== --- python/branches/py3k-issue9978/Lib/test/win_console_handler.py (original) +++ python/branches/py3k-issue9978/Lib/test/win_console_handler.py Wed Sep 29 14:39:06 2010 @@ -19,7 +19,7 @@ import ctypes # Function prototype for the handler function. Returns BOOL, takes a DWORD. - HandlerRoutine = wintypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) + HandlerRoutine = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) def _ctrl_handler(sig): """Handle a sig event and return 0 to terminate the process""" From python-checkins at python.org Wed Sep 29 14:39:51 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Wed, 29 Sep 2010 14:39:51 +0200 (CEST) Subject: [Python-checkins] r85106 - python/branches/py3k-issue9978/Lib/ctypes/wintypes.py Message-ID: <20100929123951.20522EE991@mail.python.org> Author: hirokazu.yamamoto Date: Wed Sep 29 14:39:45 2010 New Revision: 85106 Log: Revert previous commit for this file. Modified: python/branches/py3k-issue9978/Lib/ctypes/wintypes.py Modified: python/branches/py3k-issue9978/Lib/ctypes/wintypes.py ============================================================================== --- python/branches/py3k-issue9978/Lib/ctypes/wintypes.py (original) +++ python/branches/py3k-issue9978/Lib/ctypes/wintypes.py Wed Sep 29 14:39:45 2010 @@ -200,5 +200,3 @@ LPWIN32_FIND_DATAA = PWIN32_FIND_DATAA = ctypes.POINTER(WIN32_FIND_DATAA) LPWIN32_FIND_DATAW = PWIN32_FIND_DATAW = ctypes.POINTER(WIN32_FIND_DATAW) LPWORD = PWORD = ctypes.POINTER(WORD) - -del ctypes From python-checkins at python.org Wed Sep 29 16:38:02 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Wed, 29 Sep 2010 16:38:02 +0200 (CEST) Subject: [Python-checkins] r85107 - in python/branches/py3k-issue9978/Lib/test: test_os.py win_console_handler.py Message-ID: <20100929143802.3561FEE9F8@mail.python.org> Author: hirokazu.yamamoto Date: Wed Sep 29 16:38:02 2010 New Revision: 85107 Log: Better implementation for process waiting. Sometimes test hanged. Modified: python/branches/py3k-issue9978/Lib/test/test_os.py python/branches/py3k-issue9978/Lib/test/win_console_handler.py Modified: python/branches/py3k-issue9978/Lib/test/test_os.py ============================================================================== --- python/branches/py3k-issue9978/Lib/test/test_os.py (original) +++ python/branches/py3k-issue9978/Lib/test/test_os.py Wed Sep 29 16:38:02 2010 @@ -13,7 +13,7 @@ import shutil from test import support import contextlib -from win_console_handler import create_mmap +import mmap # Detect whether we're on a Linux system that uses the (now outdated # and unmaintained) linuxthreads threading library. There's an issue @@ -1030,15 +1030,17 @@ self._kill(100) def _kill_with_event(self, event, name): - m = create_mmap() + tagname = "python-test#test_os#%d" % os.getpid() + m = mmap.mmap(-1, 1, tagname) m[0] = 0 # Run a script which has console control handling enabled. proc = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), - "win_console_handler.py")], + "win_console_handler.py"), tagname], creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) # Let the interpreter startup before we send signals. See #3137. while m[0] == 0: + self.assertIsNone(proc.poll(), "subprocess died unexpectedly") time.sleep(0.5) os.kill(proc.pid, event) # proc.send_signal(event) could also be done here. Modified: python/branches/py3k-issue9978/Lib/test/win_console_handler.py ============================================================================== --- python/branches/py3k-issue9978/Lib/test/win_console_handler.py (original) +++ python/branches/py3k-issue9978/Lib/test/win_console_handler.py Wed Sep 29 16:38:02 2010 @@ -8,44 +8,40 @@ similar example in C. """ +from ctypes import wintypes +import signal +import ctypes import mmap +import sys -def create_mmap(): - return mmap.mmap(-1, 1, "Python/Lib/test/win_console_handler.py") +# Function prototype for the handler function. Returns BOOL, takes a DWORD. +HandlerRoutine = wintypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) -if __name__ == "__main__": - from ctypes import wintypes - import signal - import ctypes - - # Function prototype for the handler function. Returns BOOL, takes a DWORD. - HandlerRoutine = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) - - def _ctrl_handler(sig): - """Handle a sig event and return 0 to terminate the process""" - if sig == signal.CTRL_C_EVENT: - pass - elif sig == signal.CTRL_BREAK_EVENT: - pass - else: - print("UNKNOWN EVENT") - return 0 - - ctrl_handler = HandlerRoutine(_ctrl_handler) - - - SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler - SetConsoleCtrlHandler.argtypes = (HandlerRoutine, wintypes.BOOL) - SetConsoleCtrlHandler.restype = wintypes.BOOL +def _ctrl_handler(sig): + """Handle a sig event and return 0 to terminate the process""" + if sig == signal.CTRL_C_EVENT: + pass + elif sig == signal.CTRL_BREAK_EVENT: + pass + else: + print("UNKNOWN EVENT") + return 0 +ctrl_handler = HandlerRoutine(_ctrl_handler) + +SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler +SetConsoleCtrlHandler.argtypes = (HandlerRoutine, wintypes.BOOL) +SetConsoleCtrlHandler.restype = wintypes.BOOL + +if __name__ == "__main__": # Add our console control handling function with value 1 if not SetConsoleCtrlHandler(ctrl_handler, 1): print("Unable to add SetConsoleCtrlHandler") exit(-1) - # Awake waiting main process - m = create_mmap() + # Awake main process + m = mmap.mmap(-1, 1, sys.argv[1]) m[0] = 1 # Do nothing but wait for the signal From python-checkins at python.org Wed Sep 29 16:51:00 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Wed, 29 Sep 2010 16:51:00 +0200 (CEST) Subject: [Python-checkins] r85108 - python/branches/py3k-issue9978/Lib/test/win_console_handler.py Message-ID: <20100929145100.EB86AEE9D5@mail.python.org> Author: hirokazu.yamamoto Date: Wed Sep 29 16:51:00 2010 New Revision: 85108 Log: Wrongly reverted actual fix. Modified: python/branches/py3k-issue9978/Lib/test/win_console_handler.py Modified: python/branches/py3k-issue9978/Lib/test/win_console_handler.py ============================================================================== --- python/branches/py3k-issue9978/Lib/test/win_console_handler.py (original) +++ python/branches/py3k-issue9978/Lib/test/win_console_handler.py Wed Sep 29 16:51:00 2010 @@ -15,7 +15,7 @@ import sys # Function prototype for the handler function. Returns BOOL, takes a DWORD. -HandlerRoutine = wintypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) +HandlerRoutine = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) def _ctrl_handler(sig): """Handle a sig event and return 0 to terminate the process""" From python-checkins at python.org Wed Sep 29 16:51:42 2010 From: python-checkins at python.org (brian.curtin) Date: Wed, 29 Sep 2010 16:51:42 +0200 (CEST) Subject: [Python-checkins] r85109 - python/branches/py3k/Lib/test/win_console_handler.py Message-ID: <20100929145142.C82FAEEA09@mail.python.org> Author: brian.curtin Date: Wed Sep 29 16:51:42 2010 New Revision: 85109 Log: Fix #9978. WINFUNCTYPE is from ctypes, not from ctypes.wintypes. r85073 changed the importing in wintypes to not use *, so the previous usage here became even more incorrect. Modified: python/branches/py3k/Lib/test/win_console_handler.py Modified: python/branches/py3k/Lib/test/win_console_handler.py ============================================================================== --- python/branches/py3k/Lib/test/win_console_handler.py (original) +++ python/branches/py3k/Lib/test/win_console_handler.py Wed Sep 29 16:51:42 2010 @@ -8,12 +8,12 @@ similar example in C. """ -from ctypes import wintypes +from ctypes import wintypes, WINFUNCTYPE import signal import ctypes # Function prototype for the handler function. Returns BOOL, takes a DWORD. -HandlerRoutine = wintypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) +HandlerRoutine = WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) def _ctrl_handler(sig): """Handle a sig event and return 0 to terminate the process""" From python-checkins at python.org Wed Sep 29 16:56:32 2010 From: python-checkins at python.org (brian.curtin) Date: Wed, 29 Sep 2010 16:56:32 +0200 (CEST) Subject: [Python-checkins] r85110 - in python/branches/release27-maint: Lib/test/win_console_handler.py Message-ID: <20100929145632.A1ACBEEA01@mail.python.org> Author: brian.curtin Date: Wed Sep 29 16:56:32 2010 New Revision: 85110 Log: Merged revisions 85109 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r85109 | brian.curtin | 2010-09-29 09:51:42 -0500 (Wed, 29 Sep 2010) | 4 lines Fix #9978. WINFUNCTYPE is from ctypes, not from ctypes.wintypes. r85073 changed the importing in wintypes to not use *, so the previous usage here became even more incorrect. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/win_console_handler.py Modified: python/branches/release27-maint/Lib/test/win_console_handler.py ============================================================================== --- python/branches/release27-maint/Lib/test/win_console_handler.py (original) +++ python/branches/release27-maint/Lib/test/win_console_handler.py Wed Sep 29 16:56:32 2010 @@ -8,12 +8,12 @@ similar example in C. """ -from ctypes import wintypes +from ctypes import wintypes, WINFUNCTYPE import signal import ctypes # Function prototype for the handler function. Returns BOOL, takes a DWORD. -HandlerRoutine = wintypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) +HandlerRoutine = WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) def _ctrl_handler(sig): """Handle a sig event and return 0 to terminate the process""" From python-checkins at python.org Wed Sep 29 17:03:40 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 17:03:40 +0200 (CEST) Subject: [Python-checkins] r85111 - in python/branches/py3k: Doc/library/nntplib.rst Lib/nntplib.py Lib/test/test_nntplib.py Misc/NEWS Message-ID: <20100929150340.84737EE989@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 17:03:40 2010 New Revision: 85111 Log: Issue #9360: Cleanup and improvements to the nntplib module. The API now conforms to the philosophy of bytes and unicode separation in Python 3. A test suite has also been added. Added: python/branches/py3k/Lib/test/test_nntplib.py Modified: python/branches/py3k/Doc/library/nntplib.rst python/branches/py3k/Lib/nntplib.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/nntplib.rst ============================================================================== --- python/branches/py3k/Doc/library/nntplib.rst (original) +++ python/branches/py3k/Doc/library/nntplib.rst Wed Sep 29 17:03:40 2010 @@ -11,100 +11,99 @@ single: Network News Transfer Protocol This module defines the class :class:`NNTP` which implements the client side of -the NNTP protocol. It can be used to implement a news reader or poster, or -automated news processors. For more information on NNTP (Network News Transfer -Protocol), see Internet :rfc:`977`. +the Network News Transfer Protocol. It can be used to implement a news reader +or poster, or automated news processors. It is compatible with :rfc:`3977` +as well as the older :rfc:`977` and :rfc:`2980`. Here are two small examples of how it can be used. To list some statistics about a newsgroup and print the subjects of the last 10 articles:: - >>> s = NNTP('news.gmane.org') + >>> s = nntplib.NNTP('news.gmane.org') >>> resp, count, first, last, name = s.group('gmane.comp.python.committers') >>> print('Group', name, 'has', count, 'articles, range', first, 'to', last) - Group gmane.comp.python.committers has 1071 articles, range 1 to 1071 - >>> resp, subs = s.xhdr('subject', first + '-' + last) - >>> for id, sub in subs[-10:]: print(id, sub) + Group gmane.comp.python.committers has 1096 articles, range 1 to 1096 + >>> resp, overviews = s.over((last - 9, last)) + >>> for id, over in overviews: + ... print(id, nntplib.decode_header(over['subject'])) ... - 1062 Re: Mercurial Status? - 1063 Re: [python-committers] (Windows) buildbots on 3.x - 1064 Re: Mercurial Status? - 1065 Re: Mercurial Status? - 1066 Python 2.6.6 status - 1067 Commit Privileges for Ask Solem - 1068 Re: Commit Privileges for Ask Solem - 1069 Re: Commit Privileges for Ask Solem - 1070 Re: Commit Privileges for Ask Solem - 1071 2.6.6 rc 2 + 1087 Re: Commit privileges for ?ukasz Langa + 1088 Re: 3.2 alpha 2 freeze + 1089 Re: 3.2 alpha 2 freeze + 1090 Re: Commit privileges for ?ukasz Langa + 1091 Re: Commit privileges for ?ukasz Langa + 1092 Updated ssh key + 1093 Re: Updated ssh key + 1094 Re: Updated ssh key + 1095 Hello fellow committers! + 1096 Re: Hello fellow committers! >>> s.quit() '205 Bye!' -To post an article from a file (this assumes that the article has valid +To post an article from a binary file (this assumes that the article has valid headers, and that you have right to post on the particular newsgroup):: - >>> s = NNTP('news.gmane.org') - >>> f = open('/tmp/article') + >>> s = nntplib.NNTP('news.gmane.org') + >>> f = open('/tmp/article.txt', 'rb') >>> s.post(f) '240 Article posted successfully.' >>> s.quit() '205 Bye!' -The module itself defines the following items: +The module itself defines the following classes: -.. class:: NNTP(host[, port [, user[, password [, readermode][, usenetrc]]]]) +.. class:: NNTP(host, port=119, user=None, password=None, readermode=None, usenetrc=True, [timeout]) Return a new instance of the :class:`NNTP` class, representing a connection - to the NNTP server running on host *host*, listening at port *port*. The - default *port* is 119. If the optional *user* and *password* are provided, - or if suitable credentials are present in :file:`/.netrc` and the optional - flag *usenetrc* is true (the default), the ``AUTHINFO USER`` and ``AUTHINFO - PASS`` commands are used to identify and authenticate the user to the server. - If the optional flag *readermode* is true, then a ``mode reader`` command is - sent before authentication is performed. Reader mode is sometimes necessary - if you are connecting to an NNTP server on the local machine and intend to - call reader-specific commands, such as ``group``. If you get unexpected + to the NNTP server running on host *host*, listening at port *port*. + An optional *timeout* can be specified for the socket connection. + If the optional *user* and *password* are provided, or if suitable + credentials are present in :file:`/.netrc` and the optional flag *usenetrc* + is true (the default), the ``AUTHINFO USER`` and ``AUTHINFO PASS`` commands + are used to identify and authenticate the user to the server. If the optional + flag *readermode* is true, then a ``mode reader`` command is sent before + authentication is performed. Reader mode is sometimes necessary if you are + connecting to an NNTP server on the local machine and intend to call + reader-specific commands, such as ``group``. If you get unexpected :exc:`NNTPPermanentError`\ s, you might need to set *readermode*. *readermode* defaults to ``None``. *usenetrc* defaults to ``True``. .. exception:: NNTPError - Derived from the standard exception :exc:`Exception`, this is the base class for - all exceptions raised by the :mod:`nntplib` module. + Derived from the standard exception :exc:`Exception`, this is the base + class for all exceptions raised by the :mod:`nntplib` module. Instances + of this class have the following attribute: + + .. attribute:: response + + The response of the server if available, as a :class:`str` object. .. exception:: NNTPReplyError - Exception raised when an unexpected reply is received from the server. For - backwards compatibility, the exception ``error_reply`` is equivalent to this - class. + Exception raised when an unexpected reply is received from the server. .. exception:: NNTPTemporaryError - Exception raised when an error code in the range 400--499 is received. For - backwards compatibility, the exception ``error_temp`` is equivalent to this - class. + Exception raised when a response code in the range 400--499 is received. .. exception:: NNTPPermanentError - Exception raised when an error code in the range 500--599 is received. For - backwards compatibility, the exception ``error_perm`` is equivalent to this - class. + Exception raised when a response code in the range 500--599 is received. .. exception:: NNTPProtocolError Exception raised when a reply is received from the server that does not begin - with a digit in the range 1--5. For backwards compatibility, the exception - ``error_proto`` is equivalent to this class. + with a digit in the range 1--5. .. exception:: NNTPDataError - Exception raised when there is some error in the response data. For backwards - compatibility, the exception ``error_data`` is equivalent to this class. + Exception raised when there is some error in the response data. .. _nntp-objects: @@ -112,10 +111,29 @@ NNTP Objects ------------ -NNTP instances have the following methods. The *response* that is returned as -the first item in the return tuple of almost all methods is the server's -response: a string beginning with a three-digit code. If the server's response -indicates an error, the method raises one of the above exceptions. +:class:`NNTP` instances have the following methods. The *response* that is +returned as the first item in the return tuple of almost all methods is the +server's response: a string beginning with a three-digit code. If the server's +response indicates an error, the method raises one of the above exceptions. + +.. note:: + Many of the following methods take an optional keyword-only argument *file*. + When the *file* argument is supplied, it must be either a :term:`file object` + opened for binary writing, or the name of an on-disk file to be written to. + The method will then write any data returned by the server (except for the + response line and the terminating dot) to the file; any list of lines, + tuples or objects that the method normally returns will be empty. + + +.. versionchanged:: 3.2 + Many of the following methods have been reworked and fixed, which makes + them incompatible with their 3.1 counterparts. + + +.. method:: NNTP.quit() + + Send a ``QUIT`` command and close the connection. Once this method has been + called, no other methods of the NNTP object should be called. .. method:: NNTP.getwelcome() @@ -125,62 +143,70 @@ that may be relevant to the user.) -.. method:: NNTP.set_debuglevel(level) +.. method:: NNTP.getcapabilities() - Set the instance's debugging level. This controls the amount of debugging - output printed. The default, ``0``, produces no debugging output. A value of - ``1`` produces a moderate amount of debugging output, generally a single line - per request or response. A value of ``2`` or higher produces the maximum amount - of debugging output, logging each line sent and received on the connection - (including message text). + Return the :rfc:`3977` capabilities advertised by the server, as a + :class:`dict` instance mapping capability names to (possibly empty) lists + of values. On legacy servers which don't understand the ``CAPABILITIES`` + command, an empty dictionary is returned instead. + >>> s = NNTP('news.gmane.org') + >>> 'POST' in s.getcapabilities() + True -.. method:: NNTP.newgroups(date, time, [file]) + .. versionadded:: 3.2 - Send a ``NEWGROUPS`` command. The *date* argument should be a string of the - form ``'yymmdd'`` indicating the date, and *time* should be a string of the form - ``'hhmmss'`` indicating the time. Return a pair ``(response, groups)`` where - *groups* is a list of group names that are new since the given date and time. If - the *file* parameter is supplied, then the output of the ``NEWGROUPS`` command - is stored in a file. If *file* is a string, then the method will open a file - object with that name, write to it then close it. If *file* is a :term:`file - object`, then it will start calling :meth:`write` on it to store the lines of - the command output. If *file* is supplied, then the returned *list* is an empty list. +.. method:: NNTP.newgroups(date, *, file=None) -.. method:: NNTP.newnews(group, date, time, [file]) + Send a ``NEWGROUPS`` command. The *date* argument should be a + :class:`datetime.date` or :class:`datetime.datetime` object. + Return a pair ``(response, groups)`` where *groups* is a list representing + the groups that are new since the given *date*. If *file* is supplied, + though, then *groups* will be empty. + + >>> from datetime import date, timedelta + >>> resp, groups = s.newgroups(date.today() - timedelta(days=3)) + >>> len(groups) + 85 + >>> groups[0] + GroupInfo(group='gmane.network.tor.devel', last='4', first='1', flag='m') + + +.. method:: NNTP.newnews(group, date, *, file=None) Send a ``NEWNEWS`` command. Here, *group* is a group name or ``'*'``, and - *date* and *time* have the same meaning as for :meth:`newgroups`. Return a pair - ``(response, articles)`` where *articles* is a list of message ids. If the - *file* parameter is supplied, then the output of the ``NEWNEWS`` command is - stored in a file. If *file* is a string, then the method will open a file - object with that name, write to it then close it. If *file* is a :term:`file - object`, then it will start calling :meth:`write` on it to store the lines of the - command output. If *file* is supplied, then the returned *list* is an empty list. + *date* has the same meaning as for :meth:`newgroups`. Return a pair + ``(response, articles)`` where *articles* is a list of message ids. + + This command is frequently disabled by NNTP server administrators. -.. method:: NNTP.list([file]) +.. method:: NNTP.list(*, file=None) Send a ``LIST`` command. Return a pair ``(response, list)`` where *list* is a - list of tuples. Each tuple has the form ``(group, last, first, flag)``, where + list of tuples representing all the groups available from this NNTP server. + Each tuple has the form ``(group, last, first, flag)``, where *group* is a group name, *last* and *first* are the last and first article - numbers (as strings), and *flag* is ``'y'`` if posting is allowed, ``'n'`` if - not, and ``'m'`` if the newsgroup is moderated. (Note the ordering: *last*, - *first*.) If the *file* parameter is supplied, then the output of the ``LIST`` - command is stored in a file. If *file* is a string, then the method will open - a file with that name, write to it then close it. If *file* is a :term:`file - object`, then it will start calling :meth:`write` on it to store the lines of - the command output. If *file* is supplied, then the returned *list* is an empty - list. + numbers, and *flag* is ``'y'`` if posting is allowed, ``'n'`` if not, + and ``'m'`` if the newsgroup is moderated. (Note the ordering: *last*, *first*.) + + This command will often return very large results. It is best to cache the + results offline unless you really need to refresh them. .. method:: NNTP.descriptions(grouppattern) Send a ``LIST NEWSGROUPS`` command, where *grouppattern* is a wildmat string as - specified in RFC2980 (it's essentially the same as DOS or UNIX shell wildcard - strings). Return a pair ``(response, list)``, where *list* is a list of tuples - containing ``(name, title)``. + specified in :rfc:`3977` (it's essentially the same as DOS or UNIX shell wildcard + strings). Return a pair ``(response, descriptions)``, where *descriptions* + is a dictionary mapping group names to textual descriptions. + + >>> resp, descs = s.descriptions('gmane.comp.python.*') + >>> len(descs) + 295 + >>> descs.popitem() + ('gmane.comp.python.bio.general', 'BioPython discussion list (Moderated)') .. method:: NNTP.description(group) @@ -195,30 +221,73 @@ .. method:: NNTP.group(name) - Send a ``GROUP`` command, where *name* is the group name. Return a tuple - ``(response, count, first, last, name)`` where *count* is the (estimated) number - of articles in the group, *first* is the first article number in the group, - *last* is the last article number in the group, and *name* is the group name. - The numbers are returned as strings. + Send a ``GROUP`` command, where *name* is the group name. The group is + selected as the current group, if it exists. Return a tuple + ``(response, count, first, last, name)`` where *count* is the (estimated) + number of articles in the group, *first* is the first article number in + the group, *last* is the last article number in the group, and *name* + is the group name. + + +.. method:: NNTP.over(message_spec, *, file=None) + + Send a ``OVER`` command, or a ``XOVER`` command on legacy servers. + *message_spec* can be either a string representing a message id, or + a ``(first, last)`` tuple of numbers indicating a range of articles in + the current group, or a ``(first, None)`` tuple indicating a range of + articles starting from *first* to the last article in the current group, + or :const:`None` to select the current article in the current group. + + Return a pair ``(response, overviews)``. *overviews* is a list of + ``(article_number, overview)`` tuples, one for each article selected + by *message_spec*. Each *overview* is a dictionary with the same number + of items, but this number depends on the server. These items are either + message headers (the key is then the lower-cased header name) or metadata + items (the key is then the metadata name prepended with ``":"``). The + following items are guaranteed to be present by the NNTP specification: + + * the ``subject``, ``from``, ``date``, ``message-id`` and ``references`` + headers + * the ``:bytes`` metadata: the number of bytes in the entire raw article + (including headers and body) + * the ``:lines`` metadata: the number of lines in the article body + + It is advisable to use the :func:`decode_header` function on header + values when they may contain non-ASCII characters:: + + >>> _, _, first, last, _ = s.group('gmane.comp.python.devel') + >>> resp, overviews = s.over((last, last)) + >>> art_num, over = overviews[0] + >>> art_num + 117216 + >>> list(over.keys()) + ['xref', 'from', ':lines', ':bytes', 'references', 'date', 'message-id', 'subject'] + >>> over['from'] + '=?UTF-8?B?Ik1hcnRpbiB2LiBMw7Z3aXMi?= ' + >>> nntplib.decode_header(over['from']) + '"Martin v. L?wis" ' + + .. versionadded:: 3.2 -.. method:: NNTP.help([file]) +.. method:: NNTP.help(*, file=None) Send a ``HELP`` command. Return a pair ``(response, list)`` where *list* is a - list of help strings. If the *file* parameter is supplied, then the output of - the ``HELP`` command is stored in a file. If *file* is a string, then the - method will open a file with that name, write to it then close it. If *file* - is a :term:`file object`, then it will start calling :meth:`write` on it to store - the lines of the command output. If *file* is supplied, then the returned *list* - is an empty list. + list of help strings. + +.. method:: NNTP.stat(message_spec=None) -.. method:: NNTP.stat(id) - - Send a ``STAT`` command, where *id* is the message id (enclosed in ``'<'`` and - ``'>'``) or an article number (as a string). Return a triple ``(response, - number, id)`` where *number* is the article number (as a string) and *id* is the - message id (enclosed in ``'<'`` and ``'>'``). + Send a ``STAT`` command, where *message_spec* is either a message id + (enclosed in ``'<'`` and ``'>'``) or an article number in the current group. + If *message_spec* is omitted or :const:`None`, the current article in the + current group is considered. Return a triple ``(response, number, id)`` + where *number* is the article number and *id* is the message id. + + >>> _, _, first, last, _ = s.group('gmane.comp.python.devel') + >>> resp, number, message_id = s.stat(first) + >>> number, message_id + (9099, '<20030112190404.GE29873 at epoch.metaslash.com>') .. method:: NNTP.next() @@ -231,28 +300,69 @@ Send a ``LAST`` command. Return as for :meth:`stat`. -.. method:: NNTP.head(id) +.. method:: NNTP.article(message_spec=None, *, file=None) + + Send an ``ARTICLE`` command, where *message_spec* has the same meaning as + for :meth:`stat`. Return a tuple ``(response, info)`` where *info* + is a :class:`~collections.namedtuple` with three members *number*, + *message_id* and *lines* (in that order). *number* is the article number + in the group (or 0 if the information is not available), *message_id* the + message id as a string, and *lines* a list of lines (without terminating + newlines) comprising the raw message including headers and body. + + >>> resp, info = s.article('<20030112190404.GE29873 at epoch.metaslash.com>') + >>> info.number + 0 + >>> info.message_id + '<20030112190404.GE29873 at epoch.metaslash.com>' + >>> len(info.lines) + 65 + >>> info.lines[0] + b'Path: main.gmane.org!not-for-mail' + >>> info.lines[1] + b'From: Neal Norwitz ' + >>> info.lines[-3:] + [b'There is a patch for 2.3 as well as 2.2.', b'', b'Neal'] + + +.. method:: NNTP.head(message_spec=None, *, file=None) + + Same as :meth:`article()`, but sends a ``HEAD`` command. The *lines* + returned (or written to *file*) will only contain the message headers, not + the body. + - Send a ``HEAD`` command, where *id* has the same meaning as for :meth:`stat`. - Return a tuple ``(response, number, id, list)`` where the first three are the - same as for :meth:`stat`, and *list* is a list of the article's headers (an - uninterpreted list of lines, without trailing newlines). +.. method:: NNTP.body(message_spec=None, *, file=None) + Same as :meth:`article()`, but sends a ``BODY`` command. The *lines* + returned (or written to *file*) will only contain the message body, not the + headers. -.. method:: NNTP.body(id,[file]) - Send a ``BODY`` command, where *id* has the same meaning as for :meth:`stat`. - If the *file* parameter is supplied, then the body is stored in a file. If - *file* is a string, then the method will open a file with that name, write - to it then close it. If *file* is a :term:`file object`, then it will start - calling :meth:`write` on it to store the lines of the body. Return as for - :meth:`head`. If *file* is supplied, then the returned *list* is an empty list. +.. method:: NNTP.post(data) + Post an article using the ``POST`` command. The *data* argument is either + a :term:`file object` opened for binary reading, or any iterable of bytes + objects (representing raw lines of the article to be posted). It should + represent a well-formed news article, including the required headers. The + :meth:`post` method automatically escapes lines beginning with ``.`` and + appends the termination line. -.. method:: NNTP.article(id) + If the method succeeds, the server's response is returned. If the server + refuses posting, a :class:`NNTPReplyError` is raised. - Send an ``ARTICLE`` command, where *id* has the same meaning as for - :meth:`stat`. Return as for :meth:`head`. + +.. method:: NNTP.ihave(message_id, data) + + Send an ``IHAVE`` command. *message_id* is the id of the message to send + to the server (enclosed in ``'<'`` and ``'>'``). The *data* parameter + and the return value are the same as for :meth:`post()`. + + +.. method:: NNTP.date() + + Return a pair ``(response, date)``. *date* is a :class:`~datetime.datetime` + object containing the current date and time of the server. .. method:: NNTP.slave() @@ -260,10 +370,23 @@ Send a ``SLAVE`` command. Return the server's *response*. -.. method:: NNTP.xhdr(header, string, [file]) +.. method:: NNTP.set_debuglevel(level) + + Set the instance's debugging level. This controls the amount of debugging + output printed. The default, ``0``, produces no debugging output. A value of + ``1`` produces a moderate amount of debugging output, generally a single line + per request or response. A value of ``2`` or higher produces the maximum amount + of debugging output, logging each line sent and received on the connection + (including message text). + + +The following are optional NNTP extensions defined in :rfc:`2980`. Some of +them have been superseded by newer commands in :rfc:`3977`. + + +.. method:: NNTP.xhdr(header, string, *, file=None) - Send an ``XHDR`` command. This command is not defined in the RFC but is a - common extension. The *header* argument is a header keyword, e.g. + Send an ``XHDR`` command. The *header* argument is a header keyword, e.g. ``'subject'``. The *string* argument should have the form ``'first-last'`` where *first* and *last* are the first and last article numbers to search. Return a pair ``(response, list)``, where *list* is a list of pairs ``(id, @@ -276,66 +399,55 @@ returned *list* is an empty list. -.. method:: NNTP.post(file) +.. method:: NNTP.xover(start, end, *, file=None) - Post an article using the ``POST`` command. The *file* argument is an open file - object which is read until EOF using its :meth:`readline` method. It should be - a well-formed news article, including the required headers. The :meth:`post` - method automatically escapes lines beginning with ``.``. + Send an ``XOVER`` command. *start* and *end* are article numbers + delimiting the range of articles to select. The return value is the + same of for :meth:`over()`. It is recommended to use :meth:`over()` + instead, since it will automatically use the newer ``OVER`` command + if available. -.. method:: NNTP.ihave(id, file) +.. method:: NNTP.xpath(id) - Send an ``IHAVE`` command. *id* is a message id (enclosed in ``'<'`` and - ``'>'``). If the response is not an error, treat *file* exactly as for the - :meth:`post` method. + Return a pair ``(resp, path)``, where *path* is the directory path to the + article with message ID *id*. Most of the time, this extension is not + enabled by NNTP server administrators. -.. method:: NNTP.date() +.. XXX deprecated: - Return a triple ``(response, date, time)``, containing the current date and time - in a form suitable for the :meth:`newnews` and :meth:`newgroups` methods. This - is an optional NNTP extension, and may not be supported by all servers. - - -.. method:: NNTP.xgtitle(name, [file]) - - Process an ``XGTITLE`` command, returning a pair ``(response, list)``, where - *list* is a list of tuples containing ``(name, title)``. If the *file* parameter - is supplied, then the output of the ``XGTITLE`` command is stored in a file. - If *file* is a string, then the method will open a file with that name, write - to it then close it. If *file* is a :term:`file object`, then it will start - calling :meth:`write` on it to store the lines of the command output. If *file* - is supplied, then the returned *list* is an empty list. This is an optional NNTP - extension, and may not be supported by all servers. - - RFC2980 says "It is suggested that this extension be deprecated". Use - :meth:`descriptions` or :meth:`description` instead. - - -.. method:: NNTP.xover(start, end, [file]) - - Return a pair ``(resp, list)``. *list* is a list of tuples, one for each - article in the range delimited by the *start* and *end* article numbers. Each - tuple is of the form ``(article number, subject, poster, date, id, references, - size, lines)``. If the *file* parameter is supplied, then the output of the - ``XOVER`` command is stored in a file. If *file* is a string, then the method - will open a file with that name, write to it then close it. If *file* is a - :term:`file object`, then it will start calling :meth:`write` on it to store the - lines of the command output. If *file* is supplied, then the returned *list* is - an empty list. This is an optional NNTP extension, and may not be supported by - all servers. + .. method:: NNTP.xgtitle(name, *, file=None) + Process an ``XGTITLE`` command, returning a pair ``(response, list)``, where + *list* is a list of tuples containing ``(name, title)``. If the *file* parameter + is supplied, then the output of the ``XGTITLE`` command is stored in a file. + If *file* is a string, then the method will open a file with that name, write + to it then close it. If *file* is a :term:`file object`, then it will start + calling :meth:`write` on it to store the lines of the command output. If *file* + is supplied, then the returned *list* is an empty list. This is an optional NNTP + extension, and may not be supported by all servers. -.. method:: NNTP.xpath(id) + RFC2980 says "It is suggested that this extension be deprecated". Use + :meth:`descriptions` or :meth:`description` instead. - Return a pair ``(resp, path)``, where *path* is the directory path to the - article with message ID *id*. This is an optional NNTP extension, and may not - be supported by all servers. +Utility functions +----------------- -.. method:: NNTP.quit() +The module also defines the following utility function: - Send a ``QUIT`` command and close the connection. Once this method has been - called, no other methods of the NNTP object should be called. +.. function:: decode_header(header_str) + + Decode a header value, un-escaping any escaped non-ASCII characters. + *header_str* must be a :class:`str` object. The unescaped value is + returned. Using this function is recommended to display some headers + in a human readable form:: + + >>> decode_header("Some subject") + 'Some subject' + >>> decode_header("=?ISO-8859-15?Q?D=E9buter_en_Python?=") + 'D?buter en Python' + >>> decode_header("Re: =?UTF-8?B?cHJvYmzDqG1lIGRlIG1hdHJpY2U=?=") + 'Re: probl?me de matrice' Modified: python/branches/py3k/Lib/nntplib.py ============================================================================== --- python/branches/py3k/Lib/nntplib.py (original) +++ python/branches/py3k/Lib/nntplib.py Wed Sep 29 17:03:40 2010 @@ -1,4 +1,7 @@ -"""An NNTP client class based on RFC 977: Network News Transfer Protocol. +"""An NNTP client class based on: +- RFC 977: Network News Transfer Protocol +- RFC 2980: Common NNTP Extensions +- RFC 3977: Network News Transfer Protocol (version 2) Example: @@ -27,15 +30,53 @@ # RFC 977 by Brian Kantor and Phil Lapsley. # xover, xgtitle, xpath, date methods by Kevan Heydon +# Incompatible changes from the 2.x nntplib: +# - all commands are encoded as UTF-8 data (using the "surrogateescape" +# error handler), except for raw message data (POST, IHAVE) +# - all responses are decoded as UTF-8 data (using the "surrogateescape" +# error handler), except for raw message data (ARTICLE, HEAD, BODY) +# - the `file` argument to various methods is keyword-only +# +# - NNTP.date() returns a datetime object +# - NNTP.newgroups() and NNTP.newnews() take a datetime (or date) object, +# rather than a pair of (date, time) strings. +# - NNTP.newgroups() and NNTP.list() return a list of GroupInfo named tuples +# - NNTP.descriptions() returns a dict mapping group names to descriptions +# - NNTP.xover() returns a list of dicts mapping field names (header or metadata) +# to field values; each dict representing a message overview. +# - NNTP.article(), NNTP.head() and NNTP.body() return a (response, ArticleInfo) +# tuple. +# - the "internal" methods have been marked private (they now start with +# an underscore) + +# Other changes from the 2.x/3.1 nntplib: +# - automatic querying of capabilities at connect +# - New method NNTP.getcapabilities() +# - New method NNTP.over() +# - New helper function decode_header() +# - NNTP.post() and NNTP.ihave() accept file objects, bytes-like objects and +# arbitrary iterables yielding lines. +# - An extensive test suite :-) + +# TODO: +# - return structured data (GroupInfo etc.) everywhere +# - support HDR # Imports import re import socket - -__all__ = ["NNTP","NNTPReplyError","NNTPTemporaryError", - "NNTPPermanentError","NNTPProtocolError","NNTPDataError", - "error_reply","error_temp","error_perm","error_proto", - "error_data",] +import collections +import datetime +import warnings + +from email.header import decode_header as _email_decode_header +from socket import _GLOBAL_DEFAULT_TIMEOUT + +__all__ = ["NNTP", + "NNTPReplyError", "NNTPTemporaryError", "NNTPPermanentError", + "NNTPProtocolError", "NNTPDataError", + "decode_header", + ] # Exceptions raised when an error or invalid response is received class NNTPError(Exception): @@ -67,39 +108,189 @@ """Error in response data""" pass -# for backwards compatibility -error_reply = NNTPReplyError -error_temp = NNTPTemporaryError -error_perm = NNTPPermanentError -error_proto = NNTPProtocolError -error_data = NNTPDataError - - # Standard port used by NNTP servers NNTP_PORT = 119 # Response numbers that are followed by additional text (e.g. article) -LONGRESP = [b'100', b'215', b'220', b'221', b'222', b'224', b'230', b'231', b'282'] - +_LONGRESP = { + '100', # HELP + '101', # CAPABILITIES + '211', # LISTGROUP (also not multi-line with GROUP) + '215', # LIST + '220', # ARTICLE + '221', # HEAD, XHDR + '222', # BODY + '224', # OVER, XOVER + '225', # HDR + '230', # NEWNEWS + '231', # NEWGROUPS + '282', # XGTITLE +} + +# Default decoded value for LIST OVERVIEW.FMT if not supported +_DEFAULT_OVERVIEW_FMT = [ + "subject", "from", "date", "message-id", "references", ":bytes", ":lines"] + +# Alternative names allowed in LIST OVERVIEW.FMT response +_OVERVIEW_FMT_ALTERNATIVES = { + 'bytes': ':bytes', + 'lines': ':lines', +} # Line terminators (we always output CRLF, but accept any of CRLF, CR, LF) -CRLF = b'\r\n' +_CRLF = b'\r\n' +GroupInfo = collections.namedtuple('GroupInfo', + ['group', 'last', 'first', 'flag']) +ArticleInfo = collections.namedtuple('ArticleInfo', + ['number', 'message_id', 'lines']) -# The class itself -class NNTP: - def __init__(self, host, port=NNTP_PORT, user=None, password=None, - readermode=None, usenetrc=True): + +# Helper function(s) +def decode_header(header_str): + """Takes an unicode string representing a munged header value + and decodes it as a (possibly non-ASCII) readable value.""" + parts = [] + for v, enc in _email_decode_header(header_str): + if isinstance(v, bytes): + parts.append(v.decode(enc or 'ascii')) + else: + parts.append(v) + return ' '.join(parts) + +def _parse_overview_fmt(lines): + """Parse a list of string representing the response to LIST OVERVIEW.FMT + and return a list of header/metadata names. + Raises NNTPDataError if the response is not compliant + (cf. RFC 3977, section 8.4).""" + fmt = [] + for line in lines: + if line[0] == ':': + # Metadata name (e.g. ":bytes") + name, _, suffix = line[1:].partition(':') + name = ':' + name + else: + # Header name (e.g. "Subject:" or "Xref:full") + name, _, suffix = line.partition(':') + name = name.lower() + name = _OVERVIEW_FMT_ALTERNATIVES.get(name, name) + # Should we do something with the suffix? + fmt.append(name) + defaults = _DEFAULT_OVERVIEW_FMT + if len(fmt) < len(defaults): + raise NNTPDataError("LIST OVERVIEW.FMT response too short") + if fmt[:len(defaults)] != defaults: + raise NNTPDataError("LIST OVERVIEW.FMT redefines default fields") + return fmt + +def _parse_overview(lines, fmt, data_process_func=None): + """Parse the response to a OVER or XOVER command according to the + overview format `fmt`.""" + n_defaults = len(_DEFAULT_OVERVIEW_FMT) + overview = [] + for line in lines: + fields = {} + article_number, *tokens = line.split('\t') + article_number = int(article_number) + for i, token in enumerate(tokens): + if i >= len(fmt): + # XXX should we raise an error? Some servers might not + # support LIST OVERVIEW.FMT and still return additional + # headers. + continue + field_name = fmt[i] + is_metadata = field_name.startswith(':') + if i >= n_defaults and not is_metadata: + # Non-default header names are included in full in the response + h = field_name + ":" + if token[:len(h)].lower() != h: + raise NNTPDataError("OVER/XOVER response doesn't include " + "names of additional headers") + token = token[len(h):].lstrip(" ") + fields[fmt[i]] = token + overview.append((article_number, fields)) + return overview + +def _parse_datetime(date_str, time_str=None): + """Parse a pair of (date, time) strings, and return a datetime object. + If only the date is given, it is assumed to be date and time + concatenated together (e.g. response to the DATE command). + """ + if time_str is None: + time_str = date_str[-6:] + date_str = date_str[:-6] + hours = int(time_str[:2]) + minutes = int(time_str[2:4]) + seconds = int(time_str[4:]) + year = int(date_str[:-4]) + month = int(date_str[-4:-2]) + day = int(date_str[-2:]) + # RFC 3977 doesn't say how to interpret 2-char years. Assume that + # there are no dates before 1970 on Usenet. + if year < 70: + year += 2000 + elif year < 100: + year += 1900 + return datetime.datetime(year, month, day, hours, minutes, seconds) + +def _unparse_datetime(dt, legacy=False): + """Format a date or datetime object as a pair of (date, time) strings + in the format required by the NEWNEWS and NEWGROUPS commands. If a + date object is passed, the time is assumed to be midnight (00h00). + + The returned representation depends on the legacy flag: + * if legacy is False (the default): + date has the YYYYMMDD format and time the HHMMSS format + * if legacy is True: + date has the YYMMDD format and time the HHMMSS format. + RFC 3977 compliant servers should understand both formats; therefore, + legacy is only needed when talking to old servers. + """ + if not isinstance(dt, datetime.datetime): + time_str = "000000" + else: + time_str = "{0.hour:02d}{0.minute:02d}{0.second:02d}".format(dt) + y = dt.year + if legacy: + y = y % 100 + date_str = "{0:02d}{1.month:02d}{1.day:02d}".format(y, dt) + else: + date_str = "{0:04d}{1.month:02d}{1.day:02d}".format(y, dt) + return date_str, time_str + + +# The classes themselves +class _NNTPBase: + # UTF-8 is the character set for all NNTP commands and responses: they + # are automatically encoded (when sending) and decoded (and receiving) + # by this class. + # However, some multi-line data blocks can contain arbitrary bytes (for + # example, latin-1 or utf-16 data in the body of a message). Commands + # taking (POST, IHAVE) or returning (HEAD, BODY, ARTICLE) raw message + # data will therefore only accept and produce bytes objects. + # Furthermore, since there could be non-compliant servers out there, + # we use 'surrogateescape' as the error handler for fault tolerance + # and easy round-tripping. This could be useful for some applications + # (e.g. NNTP gateways). + + encoding = 'utf-8' + errors = 'surrogateescape' + + def __init__(self, file, user=None, password=None, + readermode=None, usenetrc=True, + timeout=_GLOBAL_DEFAULT_TIMEOUT): """Initialize an instance. Arguments: - - host: hostname to connect to - - port: port to connect to (default the standard NNTP port) + - file: file-like object (open for read/write in binary mode) - user: username to authenticate with - password: password to use with username - readermode: if true, send 'mode reader' command after connecting. + - usenetrc: allow loading username and password from ~/.netrc file + if not specified explicitly + - timeout: timeout (in seconds) used for socket connections readermode is sometimes necessary if you are connecting to an NNTP server on the local machine and intend to call @@ -107,12 +298,9 @@ unexpected NNTPPermanentErrors, you might need to set readermode. """ - self.host = host - self.port = port - self.sock = socket.create_connection((host, port)) - self.file = self.sock.makefile('rb') + self.file = file self.debugging = 0 - self.welcome = self.getresp() + self.welcome = self._getresp() # 'mode reader' is sometimes necessary to enable 'reader' mode. # However, the order in which 'mode reader' and 'authinfo' need to @@ -122,12 +310,12 @@ readermode_afterauth = 0 if readermode: try: - self.welcome = self.shortcmd('mode reader') + self.welcome = self._shortcmd('mode reader') except NNTPPermanentError: # error 500, probably 'not implemented' pass except NNTPTemporaryError as e: - if user and e.response.startswith(b'480'): + if user and e.response.startswith('480'): # Need authorization before 'mode reader' readermode_afterauth = 1 else: @@ -144,29 +332,35 @@ password = auth[2] except IOError: pass - # Perform NNRP authentication if needed. + # Perform NNTP authentication if needed. if user: - resp = self.shortcmd('authinfo user '+user) - if resp.startswith(b'381'): + resp = self._shortcmd('authinfo user '+user) + if resp.startswith('381'): if not password: raise NNTPReplyError(resp) else: - resp = self.shortcmd( + resp = self._shortcmd( 'authinfo pass '+password) - if not resp.startswith(b'281'): + if not resp.startswith('281'): raise NNTPPermanentError(resp) if readermode_afterauth: try: - self.welcome = self.shortcmd('mode reader') + self.welcome = self._shortcmd('mode reader') except NNTPPermanentError: # error 500, probably 'not implemented' pass - - # Get the welcome message from the server - # (this is read and squirreled away by __init__()). - # If the response code is 200, posting is allowed; - # if it 201, posting is not allowed + # Inquire about capabilities (RFC 3977) + self.nntp_version = 1 + try: + resp, caps = self.capabilities() + except NNTPPermanentError: + # Server doesn't support capabilities + self._caps = {} + else: + self._caps = caps + if 'VERSION' in caps: + self.nntp_version = int(caps['VERSION'][0]) def getwelcome(self): """Get the welcome message from the server @@ -177,6 +371,12 @@ if self.debugging: print('*welcome*', repr(self.welcome)) return self.welcome + def getcapabilities(self): + """Get the server capabilities, as read by __init__(). + If the CAPABILITIES command is not supported, an empty dict is + returned.""" + return self._caps + def set_debuglevel(self, level): """Set the debugging level. Argument 'level' means: 0: no debugging output (default) @@ -186,121 +386,221 @@ self.debugging = level debug = set_debuglevel - def putline(self, line): - """Internal: send one line to the server, appending CRLF.""" - line = line + CRLF + def _putline(self, line): + """Internal: send one line to the server, appending CRLF. + The `line` must be a bytes-like object.""" + line = line + _CRLF if self.debugging > 1: print('*put*', repr(line)) - self.sock.sendall(line) + self.file.write(line) + self.file.flush() - def putcmd(self, line): - """Internal: send one command to the server (through putline()).""" + def _putcmd(self, line): + """Internal: send one command to the server (through _putline()). + The `line` must be an unicode string.""" if self.debugging: print('*cmd*', repr(line)) - line = bytes(line, "ASCII") - self.putline(line) + line = line.encode(self.encoding, self.errors) + self._putline(line) - def getline(self): - """Internal: return one line from the server, stripping CRLF. - Raise EOFError if the connection is closed.""" + def _getline(self, strip_crlf=True): + """Internal: return one line from the server, stripping _CRLF. + Raise EOFError if the connection is closed. + Returns a bytes object.""" line = self.file.readline() if self.debugging > 1: print('*get*', repr(line)) if not line: raise EOFError - if line[-2:] == CRLF: - line = line[:-2] - elif line[-1:] in CRLF: - line = line[:-1] + if strip_crlf: + if line[-2:] == _CRLF: + line = line[:-2] + elif line[-1:] in _CRLF: + line = line[:-1] return line - def getresp(self): + def _getresp(self): """Internal: get a response from the server. - Raise various errors if the response indicates an error.""" - resp = self.getline() + Raise various errors if the response indicates an error. + Returns an unicode string.""" + resp = self._getline() if self.debugging: print('*resp*', repr(resp)) + resp = resp.decode(self.encoding, self.errors) c = resp[:1] - if c == b'4': + if c == '4': raise NNTPTemporaryError(resp) - if c == b'5': + if c == '5': raise NNTPPermanentError(resp) - if c not in b'123': + if c not in '123': raise NNTPProtocolError(resp) return resp - def getlongresp(self, file=None): + def _getlongresp(self, file=None): """Internal: get a response plus following text from the server. - Raise various errors if the response indicates an error.""" + Raise various errors if the response indicates an error. + + Returns a (response, lines) tuple where `response` is an unicode + string and `lines` is a list of bytes objects. + If `file` is a file-like object, it must be open in binary mode. + """ openedFile = None try: # If a string was passed then open a file with that name - if isinstance(file, str): - openedFile = file = open(file, "w") + if isinstance(file, (str, bytes)): + openedFile = file = open(file, "wb") - resp = self.getresp() - if resp[:3] not in LONGRESP: + resp = self._getresp() + if resp[:3] not in _LONGRESP: raise NNTPReplyError(resp) - list = [] - while 1: - line = self.getline() - if line == b'.': - break - if line.startswith(b'..'): - line = line[1:] - if file: - file.write(line + b'\n') - else: - list.append(line) + + lines = [] + if file is not None: + # XXX lines = None instead? + terminators = (b'.' + _CRLF, b'.\n') + while 1: + line = self._getline(False) + if line in terminators: + break + if line.startswith(b'..'): + line = line[1:] + file.write(line) + else: + terminator = b'.' + while 1: + line = self._getline() + if line == terminator: + break + if line.startswith(b'..'): + line = line[1:] + lines.append(line) finally: # If this method created the file, then it must close it if openedFile: openedFile.close() - return resp, list + return resp, lines - def shortcmd(self, line): - """Internal: send a command and get the response.""" - self.putcmd(line) - return self.getresp() - - def longcmd(self, line, file=None): - """Internal: send a command and get the response plus following text.""" - self.putcmd(line) - return self.getlongresp(file) + def _shortcmd(self, line): + """Internal: send a command and get the response. + Same return value as _getresp().""" + self._putcmd(line) + return self._getresp() + + def _longcmd(self, line, file=None): + """Internal: send a command and get the response plus following text. + Same return value as _getlongresp().""" + self._putcmd(line) + return self._getlongresp(file) + + def _longcmdstring(self, line, file=None): + """Internal: send a command and get the response plus following text. + Same as _longcmd() and _getlongresp(), except that the returned `lines` + are unicode strings rather than bytes objects. + """ + self._putcmd(line) + resp, list = self._getlongresp(file) + return resp, [line.decode(self.encoding, self.errors) + for line in list] + + def _getoverviewfmt(self): + """Internal: get the overview format. Queries the server if not + already done, else returns the cached value.""" + try: + return self._cachedoverviewfmt + except AttributeError: + pass + try: + resp, lines = self._longcmdstring("LIST OVERVIEW.FMT") + except NNTPPermanentError: + # Not supported by server? + fmt = _DEFAULT_OVERVIEW_FMT[:] + else: + fmt = _parse_overview_fmt(lines) + self._cachedoverviewfmt = fmt + return fmt + + def _grouplist(self, lines): + # Parse lines into "group last first flag" + return [GroupInfo(*line.split()) for line in lines] - def newgroups(self, date, time, file=None): - """Process a NEWGROUPS command. Arguments: - - date: string 'yymmdd' indicating the date - - time: string 'hhmmss' indicating the time + def capabilities(self): + """Process a CAPABILITIES command. Not supported by all servers. Return: - resp: server response if successful - - list: list of newsgroup names""" + - caps: a dictionary mapping capability names to lists of tokens + (for example {'VERSION': ['2'], 'OVER': [], LIST: ['ACTIVE', 'HEADERS'] }) + """ + caps = {} + resp, lines = self._longcmdstring("CAPABILITIES") + for line in lines: + name, *tokens = line.split() + caps[name] = tokens + return resp, caps - return self.longcmd('NEWGROUPS ' + date + ' ' + time, file) + def newgroups(self, date, *, file=None): + """Process a NEWGROUPS command. Arguments: + - date: a date or datetime object + Return: + - resp: server response if successful + - list: list of newsgroup names + """ + if not isinstance(date, (datetime.date, datetime.date)): + raise TypeError( + "the date parameter must be a date or datetime object, " + "not '{:40}'".format(date.__class__.__name__)) + date_str, time_str = _unparse_datetime(date, self.nntp_version < 2) + cmd = 'NEWGROUPS {0} {1}'.format(date_str, time_str) + resp, lines = self._longcmdstring(cmd, file) + return resp, self._grouplist(lines) - def newnews(self, group, date, time, file=None): + def newnews(self, group, date, *, file=None): """Process a NEWNEWS command. Arguments: - group: group name or '*' - - date: string 'yymmdd' indicating the date - - time: string 'hhmmss' indicating the time + - date: a date or datetime object Return: - resp: server response if successful - - list: list of message ids""" - - cmd = 'NEWNEWS ' + group + ' ' + date + ' ' + time - return self.longcmd(cmd, file) - - def list(self, file=None): - """Process a LIST command. Return: + - list: list of message ids + """ + if not isinstance(date, (datetime.date, datetime.date)): + raise TypeError( + "the date parameter must be a date or datetime object, " + "not '{:40}'".format(date.__class__.__name__)) + date_str, time_str = _unparse_datetime(date, self.nntp_version < 2) + cmd = 'NEWNEWS {0} {1} {2}'.format(group, date_str, time_str) + return self._longcmdstring(cmd, file) + + def list(self, *, file=None): + """Process a LIST command. Argument: + - file: Filename string or file object to store the result in + Returns: - resp: server response if successful - - list: list of (group, last, first, flag) (strings)""" + - list: list of (group, last, first, flag) (strings) + """ + resp, lines = self._longcmdstring('LIST', file) + return resp, self._grouplist(lines) - resp, list = self.longcmd('LIST', file) - for i in range(len(list)): - # Parse lines into "group last first flag" - list[i] = tuple(list[i].split()) - return resp, list + def _getdescriptions(self, group_pattern, return_all): + line_pat = re.compile('^(?P[^ \t]+)[ \t]+(.*)$') + # Try the more std (acc. to RFC2980) LIST NEWSGROUPS first + resp, lines = self._longcmdstring('LIST NEWSGROUPS ' + group_pattern) + if not resp.startswith('215'): + # Now the deprecated XGTITLE. This either raises an error + # or succeeds with the same output structure as LIST + # NEWSGROUPS. + resp, lines = self._longcmdstring('XGTITLE ' + group_pattern) + groups = {} + for raw_line in lines: + match = line_pat.search(raw_line.strip()) + if match: + name, desc = match.group(1, 2) + if not return_all: + return desc + groups[name] = desc + if return_all: + return resp, groups + else: + # Nothing found + return '' def description(self, group): - """Get a description for a single group. If more than one group matches ('group' is a pattern), return the first. If no group matches, return an empty string. @@ -311,42 +611,24 @@ NOTE: This neither checks for a wildcard in 'group' nor does it check whether the group actually exists.""" - - resp, lines = self.descriptions(group) - if len(lines) == 0: - return b'' - else: - return lines[0][1] + return self._getdescriptions(group, False) def descriptions(self, group_pattern): """Get descriptions for a range of groups.""" - line_pat = re.compile(b'^(?P[^ \t]+)[ \t]+(.*)$') - # Try the more std (acc. to RFC2980) LIST NEWSGROUPS first - resp, raw_lines = self.longcmd('LIST NEWSGROUPS ' + group_pattern) - if not resp.startswith(b'215'): - # Now the deprecated XGTITLE. This either raises an error - # or succeeds with the same output structure as LIST - # NEWSGROUPS. - resp, raw_lines = self.longcmd('XGTITLE ' + group_pattern) - lines = [] - for raw_line in raw_lines: - match = line_pat.search(raw_line.strip()) - if match: - lines.append(match.group(1, 2)) - return resp, lines + return self._getdescriptions(group_pattern, True) def group(self, name): """Process a GROUP command. Argument: - group: the group name Returns: - resp: server response if successful - - count: number of articles (string) - - first: first article number (string) - - last: last article number (string) - - name: the group name""" - - resp = self.shortcmd('GROUP ' + name) - if not resp.startswith(b'211'): + - count: number of articles + - first: first article number + - last: last article number + - name: the group name + """ + resp = self._shortcmd('GROUP ' + name) + if not resp.startswith('211'): raise NNTPReplyError(resp) words = resp.split() count = first = last = 0 @@ -359,151 +641,177 @@ last = words[3] if n > 4: name = words[4].lower() - return resp, count, first, last, name + return resp, int(count), int(first), int(last), name - def help(self, file=None): - """Process a HELP command. Returns: + def help(self, *, file=None): + """Process a HELP command. Argument: + - file: Filename string or file object to store the result in + Returns: - resp: server response if successful - - list: list of strings""" - - return self.longcmd('HELP',file) + - list: list of strings returned by the server in response to the + HELP command + """ + return self._longcmdstring('HELP', file) - def statparse(self, resp): - """Internal: parse the response of a STAT, NEXT or LAST command.""" - if not resp.startswith(b'22'): + def _statparse(self, resp): + """Internal: parse the response line of a STAT, NEXT, LAST, + ARTICLE, HEAD or BODY command.""" + if not resp.startswith('22'): raise NNTPReplyError(resp) words = resp.split() - nr = 0 - id = b'' - n = len(words) - if n > 1: - nr = words[1] - if n > 2: - id = words[2] - return resp, nr, id + art_num = int(words[1]) + message_id = words[2] + return resp, art_num, message_id - def statcmd(self, line): + def _statcmd(self, line): """Internal: process a STAT, NEXT or LAST command.""" - resp = self.shortcmd(line) - return self.statparse(resp) + resp = self._shortcmd(line) + return self._statparse(resp) - def stat(self, id): + def stat(self, message_spec=None): """Process a STAT command. Argument: - - id: article number or message id + - message_spec: article number or message id (if not specified, + the current article is selected) Returns: - resp: server response if successful - - nr: the article number - - id: the message id""" - - return self.statcmd('STAT {0}'.format(id)) + - art_num: the article number + - message_id: the message id + """ + if message_spec: + return self._statcmd('STAT {0}'.format(message_spec)) + else: + return self._statcmd('STAT') def next(self): """Process a NEXT command. No arguments. Return as for STAT.""" - return self.statcmd('NEXT') + return self._statcmd('NEXT') def last(self): """Process a LAST command. No arguments. Return as for STAT.""" - return self.statcmd('LAST') + return self._statcmd('LAST') - def artcmd(self, line, file=None): + def _artcmd(self, line, file=None): """Internal: process a HEAD, BODY or ARTICLE command.""" - resp, list = self.longcmd(line, file) - resp, nr, id = self.statparse(resp) - return resp, nr, id, list + resp, lines = self._longcmd(line, file) + resp, art_num, message_id = self._statparse(resp) + return resp, ArticleInfo(art_num, message_id, lines) - def head(self, id): + def head(self, message_spec=None, *, file=None): """Process a HEAD command. Argument: - - id: article number or message id + - message_spec: article number or message id + - file: filename string or file object to store the headers in Returns: - resp: server response if successful - - nr: article number - - id: message id - - list: the lines of the article's header""" - - return self.artcmd('HEAD {0}'.format(id)) + - ArticleInfo: (article number, message id, list of header lines) + """ + if message_spec is not None: + cmd = 'HEAD {0}'.format(message_spec) + else: + cmd = 'HEAD' + return self._artcmd(cmd, file) - def body(self, id, file=None): + def body(self, message_spec=None, *, file=None): """Process a BODY command. Argument: - - id: article number or message id - - file: Filename string or file object to store the article in + - message_spec: article number or message id + - file: filename string or file object to store the body in Returns: - resp: server response if successful - - nr: article number - - id: message id - - list: the lines of the article's body or an empty list - if file was used""" - - return self.artcmd('BODY {0}'.format(id), file) + - ArticleInfo: (article number, message id, list of body lines) + """ + if message_spec is not None: + cmd = 'BODY {0}'.format(message_spec) + else: + cmd = 'BODY' + return self._artcmd(cmd, file) - def article(self, id): + def article(self, message_spec=None, *, file=None): """Process an ARTICLE command. Argument: - - id: article number or message id + - message_spec: article number or message id + - file: filename string or file object to store the article in Returns: - resp: server response if successful - - nr: article number - - id: message id - - list: the lines of the article""" - - return self.artcmd('ARTICLE {0}'.format(id)) + - ArticleInfo: (article number, message id, list of article lines) + """ + if message_spec is not None: + cmd = 'ARTICLE {0}'.format(message_spec) + else: + cmd = 'ARTICLE' + return self._artcmd(cmd, file) def slave(self): """Process a SLAVE command. Returns: - - resp: server response if successful""" - - return self.shortcmd('SLAVE') + - resp: server response if successful + """ + return self._shortcmd('SLAVE') - def xhdr(self, hdr, str, file=None): + def xhdr(self, hdr, str, *, file=None): """Process an XHDR command (optional server extension). Arguments: - hdr: the header type (e.g. 'subject') - str: an article nr, a message id, or a range nr1-nr2 + - file: Filename string or file object to store the result in Returns: - resp: server response if successful - - list: list of (nr, value) strings""" - - pat = re.compile(b'^([0-9]+) ?(.*)\n?') - resp, lines = self.longcmd('XHDR {0} {1}'.format(hdr, str), file) - for i in range(len(lines)): - line = lines[i] + - list: list of (nr, value) strings + """ + pat = re.compile('^([0-9]+) ?(.*)\n?') + resp, lines = self._longcmdstring('XHDR {0} {1}'.format(hdr, str), file) + def remove_number(line): m = pat.match(line) - if m: - lines[i] = m.group(1, 2) - return resp, lines + return m.group(1, 2) if m else line + return resp, [remove_number(line) for line in lines] - def xover(self, start, end, file=None): + def xover(self, start, end, *, file=None): """Process an XOVER command (optional server extension) Arguments: - start: start of range - end: end of range + - file: Filename string or file object to store the result in Returns: - resp: server response if successful - - list: list of (art-nr, subject, poster, date, - id, references, size, lines)""" + - list: list of dicts containing the response fields + """ + resp, lines = self._longcmdstring('XOVER {0}-{1}'.format(start, end), + file) + fmt = self._getoverviewfmt() + return resp, _parse_overview(lines, fmt) + + def over(self, message_spec, *, file=None): + """Process an OVER command. If the command isn't supported, fall + back to XOVER. Arguments: + - message_spec: + - either a message id, indicating the article to fetch + information about + - or a (start, end) tuple, indicating a range of article numbers; + if end is None, information up to the newest message will be + retrieved + - or None, indicating the current article number must be used + - file: Filename string or file object to store the result in + Returns: + - resp: server response if successful + - list: list of dicts containing the response fields - resp, lines = self.longcmd('XOVER {0}-{1}'.format(start, end), file) - xover_lines = [] - for line in lines: - elem = line.split(b'\t') - try: - xover_lines.append((elem[0], - elem[1], - elem[2], - elem[3], - elem[4], - elem[5].split(), - elem[6], - elem[7])) - except IndexError: - raise NNTPDataError(line) - return resp,xover_lines + NOTE: the "message id" form isn't supported by XOVER + """ + cmd = 'OVER' if 'OVER' in self._caps else 'XOVER' + if isinstance(message_spec, (tuple, list)): + start, end = message_spec + cmd += ' {0}-{1}'.format(start, end or '') + elif message_spec is not None: + cmd = cmd + ' ' + message_spec + resp, lines = self._longcmdstring(cmd, file) + fmt = self._getoverviewfmt() + return resp, _parse_overview(lines, fmt) - def xgtitle(self, group, file=None): + def xgtitle(self, group, *, file=None): """Process an XGTITLE command (optional server extension) Arguments: - group: group name wildcard (i.e. news.*) Returns: - resp: server response if successful - list: list of (name,title) strings""" - - line_pat = re.compile(b'^([^ \t]+)[ \t]+(.*)$') - resp, raw_lines = self.longcmd('XGTITLE ' + group, file) + warnings.warn("The XGTITLE extension is not actively used, " + "use descriptions() instead", + PendingDeprecationWarning, 2) + line_pat = re.compile('^([^ \t]+)[ \t]+(.*)$') + resp, raw_lines = self._longcmdstring('XGTITLE ' + group, file) lines = [] for raw_line in raw_lines: match = line_pat.search(raw_line.strip()) @@ -511,15 +819,18 @@ lines.append(match.group(1, 2)) return resp, lines - def xpath(self,id): + def xpath(self, id): """Process an XPATH command (optional server extension) Arguments: - id: Message id of article Returns: resp: server response if successful - path: directory path to article""" + path: directory path to article + """ + warnings.warn("The XPATH extension is not actively used", + PendingDeprecationWarning, 2) - resp = self.shortcmd('XPATH {0}'.format(id)) - if not resp.startswith(b'223'): + resp = self._shortcmd('XPATH {0}'.format(id)) + if not resp.startswith('223'): raise NNTPReplyError(resp) try: [resp_num, path] = resp.split() @@ -528,89 +839,144 @@ else: return resp, path - def date (self): - """Process the DATE command. Arguments: - None + def date(self): + """Process the DATE command. Returns: - resp: server response if successful - date: Date suitable for newnews/newgroups commands etc. - time: Time suitable for newnews/newgroups commands etc.""" - - resp = self.shortcmd("DATE") - if not resp.startswith(b'111'): + - resp: server response if successful + - date: datetime object + """ + resp = self._shortcmd("DATE") + if not resp.startswith('111'): raise NNTPReplyError(resp) elem = resp.split() if len(elem) != 2: raise NNTPDataError(resp) - date = elem[1][2:8] - time = elem[1][-6:] - if len(date) != 6 or len(time) != 6: + date = elem[1] + if len(date) != 14: raise NNTPDataError(resp) - return resp, date, time + return resp, _parse_datetime(date, None) def _post(self, command, f): - resp = self.shortcmd(command) - # Raises error_??? if posting is not allowed - if not resp.startswith(b'3'): + resp = self._shortcmd(command) + # Raises a specific exception if posting is not allowed + if not resp.startswith('3'): raise NNTPReplyError(resp) - while 1: - line = f.readline() - if not line: - break - if line.endswith(b'\n'): - line = line[:-1] + if isinstance(f, (bytes, bytearray)): + f = f.splitlines() + # We don't use _putline() because: + # - we don't want additional CRLF if the file or iterable is already + # in the right format + # - we don't want a spurious flush() after each line is written + for line in f: + if not line.endswith(_CRLF): + line = line.rstrip(b"\r\n") + _CRLF if line.startswith(b'.'): line = b'.' + line - self.putline(line) - self.putline(b'.') - return self.getresp() + self.file.write(line) + self.file.write(b".\r\n") + self.file.flush() + return self._getresp() - def post(self, f): + def post(self, data): """Process a POST command. Arguments: - - f: file containing the article + - data: bytes object, iterable or file containing the article Returns: - resp: server response if successful""" - return self._post('POST', f) + return self._post('POST', data) - def ihave(self, id, f): + def ihave(self, message_id, data): """Process an IHAVE command. Arguments: - - id: message-id of the article - - f: file containing the article + - message_id: message-id of the article + - data: file containing the article Returns: - resp: server response if successful Note that if the server refuses the article an exception is raised.""" - return self._post('IHAVE {0}'.format(id), f) + return self._post('IHAVE {0}'.format(message_id), data) + + def _close(self): + self.file.close() + del self.file def quit(self): """Process a QUIT command and close the socket. Returns: - resp: server response if successful""" - - resp = self.shortcmd('QUIT') - self.file.close() - self.sock.close() - del self.file, self.sock + try: + resp = self._shortcmd('QUIT') + finally: + self._close() return resp +class NNTP(_NNTPBase): + + def __init__(self, host, port=NNTP_PORT, user=None, password=None, + readermode=None, usenetrc=True, + timeout=_GLOBAL_DEFAULT_TIMEOUT): + """Initialize an instance. Arguments: + - host: hostname to connect to + - port: port to connect to (default the standard NNTP port) + - user: username to authenticate with + - password: password to use with username + - readermode: if true, send 'mode reader' command after + connecting. + - usenetrc: allow loading username and password from ~/.netrc file + if not specified explicitly + - timeout: timeout (in seconds) used for socket connections + + readermode is sometimes necessary if you are connecting to an + NNTP server on the local machine and intend to call + reader-specific comamnds, such as `group'. If you get + unexpected NNTPPermanentErrors, you might need to set + readermode. + """ + self.host = host + self.port = port + self.sock = socket.create_connection((host, port), timeout) + file = self.sock.makefile("rwb") + _NNTPBase.__init__(self, file, user, password, + readermode, usenetrc, timeout) + + def _close(self): + try: + _NNTPBase._close(self) + finally: + self.sock.close() + + # Test retrieval when run as a script. -# Assumption: if there's a local news server, it's called 'news'. -# Assumption: if user queries a remote news server, it's named -# in the environment variable NNTPSERVER (used by slrn and kin) -# and we want readermode off. if __name__ == '__main__': - import os - newshost = 'news' and os.environ["NNTPSERVER"] - if newshost.find('.') == -1: - mode = 'readermode' - else: - mode = None - s = NNTP(newshost, readermode=mode) - resp, count, first, last, name = s.group('comp.lang.python') - print(resp) + import argparse + from email.utils import parsedate + + parser = argparse.ArgumentParser(description="""\ + nntplib built-in demo - display the latest articles in a newsgroup""") + parser.add_argument('-g', '--group', default='gmane.comp.python.general', + help='group to fetch messages from (default: %(default)s)') + parser.add_argument('-s', '--server', default='news.gmane.org', + help='NNTP server hostname (default: %(default)s)') + parser.add_argument('-p', '--port', default=NNTP_PORT, type=int, + help='NNTP port number (default: %(default)s)') + parser.add_argument('-n', '--nb-articles', default=10, type=int, + help='number of articles to fetch (default: %(default)s)') + args = parser.parse_args() + + s = NNTP(host=args.server, port=args.port) + resp, count, first, last, name = s.group(args.group) print('Group', name, 'has', count, 'articles, range', first, 'to', last) - resp, subs = s.xhdr('subject', '{0}-{1}'.format(first, last)) - print(resp) - for item in subs: - print("%7s %s" % item) - resp = s.quit() - print(resp) + + def cut(s, lim): + if len(s) > lim: + s = s[:lim - 4] + "..." + return s + + first = str(int(last) - args.nb_articles + 1) + resp, overviews = s.xover(first, last) + for artnum, over in overviews: + author = decode_header(over['from']).split('<', 1)[0] + subject = decode_header(over['subject']) + lines = int(over[':lines']) + print("{:7} {:20} {:42} ({})".format( + artnum, cut(author, 20), cut(subject, 42), lines) + ) + + s.quit() Added: python/branches/py3k/Lib/test/test_nntplib.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/test/test_nntplib.py Wed Sep 29 17:03:40 2010 @@ -0,0 +1,1091 @@ +import io +import datetime +import textwrap +import unittest +import contextlib +from test import support +from nntplib import NNTP, GroupInfo +import nntplib + +TIMEOUT = 30 + +# TODO: +# - test the `file` arg to more commands +# - test error conditions + + +class NetworkedNNTPTestsMixin: + + def test_welcome(self): + welcome = self.server.getwelcome() + self.assertEqual(str, type(welcome)) + + def test_help(self): + resp, list = self.server.help() + self.assertTrue(resp.startswith("100 "), resp) + for line in list: + self.assertEqual(str, type(line)) + + def test_list(self): + resp, list = self.server.list() + if len(list) > 0: + self.assertEqual(GroupInfo, type(list[0])) + self.assertEqual(str, type(list[0].group)) + + def test_unknown_command(self): + with self.assertRaises(nntplib.NNTPPermanentError) as cm: + self.server._shortcmd("XYZZY") + resp = cm.exception.response + self.assertTrue(resp.startswith("500 "), resp) + + def test_newgroups(self): + # gmane gets a constant influx of new groups. In order not to stress + # the server too much, we choose a recent date in the past. + dt = datetime.date.today() - datetime.timedelta(days=7) + resp, groups = self.server.newgroups(dt) + if len(groups) > 0: + self.assertIsInstance(groups[0], GroupInfo) + self.assertIsInstance(groups[0].group, str) + + def test_description(self): + def _check_desc(desc): + # Sanity checks + self.assertIsInstance(desc, str) + self.assertNotIn(self.GROUP_NAME, desc) + desc = self.server.description(self.GROUP_NAME) + _check_desc(desc) + # Another sanity check + self.assertIn("Python", desc) + # With a pattern + desc = self.server.description(self.GROUP_PAT) + _check_desc(desc) + # Shouldn't exist + desc = self.server.description("zk.brrtt.baz") + self.assertEqual(desc, '') + + def test_descriptions(self): + resp, descs = self.server.descriptions(self.GROUP_PAT) + # 215 for LIST NEWSGROUPS, 282 for XGTITLE + self.assertTrue( + resp.startswith("215 ") or resp.startswith("282 "), resp) + self.assertIsInstance(descs, dict) + desc = descs[self.GROUP_NAME] + self.assertEqual(desc, self.server.description(self.GROUP_NAME)) + + def test_group(self): + result = self.server.group(self.GROUP_NAME) + self.assertEqual(5, len(result)) + resp, count, first, last, group = result + self.assertEqual(group, self.GROUP_NAME) + self.assertIsInstance(count, int) + self.assertIsInstance(first, int) + self.assertIsInstance(last, int) + self.assertLessEqual(first, last) + self.assertTrue(resp.startswith("211 "), resp) + + def test_date(self): + resp, date = self.server.date() + self.assertIsInstance(date, datetime.datetime) + # Sanity check + self.assertGreaterEqual(date.year, 1995) + self.assertLessEqual(date.year, 2030) + + def _check_art_dict(self, art_dict): + # Some sanity checks for a field dictionary returned by OVER / XOVER + self.assertIsInstance(art_dict, dict) + # NNTP has 7 mandatory fields + self.assertGreaterEqual(art_dict.keys(), + {"subject", "from", "date", "message-id", + "references", ":bytes", ":lines"} + ) + for v in art_dict.values(): + self.assertIsInstance(v, str) + + def test_xover(self): + resp, count, first, last, name = self.server.group(self.GROUP_NAME) + resp, lines = self.server.xover(last, last) + art_num, art_dict = lines[0] + self.assertEqual(art_num, last) + self._check_art_dict(art_dict) + + def test_over(self): + resp, count, first, last, name = self.server.group(self.GROUP_NAME) + start = last - 10 + # The "start-" article range form + resp, lines = self.server.over((start, None)) + art_num, art_dict = lines[0] + self._check_art_dict(art_dict) + # The "start-end" article range form + resp, lines = self.server.over((start, last)) + art_num, art_dict = lines[-1] + self.assertEqual(art_num, last) + self._check_art_dict(art_dict) + # XXX The "message_id" form is unsupported by gmane + # 503 Overview by message-ID unsupported + + def test_xhdr(self): + resp, count, first, last, name = self.server.group(self.GROUP_NAME) + resp, lines = self.server.xhdr('subject', last) + for line in lines: + self.assertEqual(str, type(line[1])) + + def check_article_resp(self, resp, article, art_num=None): + self.assertIsInstance(article, nntplib.ArticleInfo) + if art_num is not None: + self.assertEqual(article.number, art_num) + for line in article.lines: + self.assertIsInstance(line, bytes) + # XXX this could exceptionally happen... + self.assertNotIn(article.lines[-1], (b".", b".\n", b".\r\n")) + + def test_article_head_body(self): + resp, count, first, last, name = self.server.group(self.GROUP_NAME) + resp, head = self.server.head(last) + self.assertTrue(resp.startswith("221 "), resp) + self.check_article_resp(resp, head, last) + resp, body = self.server.body(last) + self.assertTrue(resp.startswith("222 "), resp) + self.check_article_resp(resp, body, last) + resp, article = self.server.article(last) + self.assertTrue(resp.startswith("220 "), resp) + self.check_article_resp(resp, article, last) + self.assertEqual(article.lines, head.lines + [b''] + body.lines) + + def test_quit(self): + self.server.quit() + self.server = None + + +class NetworkedNNTPTests(NetworkedNNTPTestsMixin, unittest.TestCase): + NNTP_HOST = 'news.gmane.org' + GROUP_NAME = 'gmane.comp.python.devel' + GROUP_PAT = 'gmane.comp.python.d*' + + def setUp(self): + support.requires("network") + with support.transient_internet(self.NNTP_HOST): + self.server = NNTP(self.NNTP_HOST, timeout=TIMEOUT) + + def tearDown(self): + if self.server is not None: + self.server.quit() + + # Disabled with gmane as it produces too much data + test_list = None + + def test_capabilities(self): + # As of this writing, gmane implements NNTP version 2 and has a + # couple of well-known capabilities. Just sanity check that we + # got them. + def _check_caps(caps): + caps_list = caps['LIST'] + self.assertIsInstance(caps_list, (list, tuple)) + self.assertIn('OVERVIEW.FMT', caps_list) + self.assertGreaterEqual(self.server.nntp_version, 2) + _check_caps(self.server.getcapabilities()) + # This re-emits the command + resp, caps = self.server.capabilities() + _check_caps(caps) + + +# +# Non-networked tests using a local server (or something mocking it). +# + +class _NNTPServerIO(io.RawIOBase): + """A raw IO object allowing NNTP commands to be received and processed + by a handler. The handler can push responses which can then be read + from the IO object.""" + + def __init__(self, handler): + io.RawIOBase.__init__(self) + # The channel from the client + self.c2s = io.BytesIO() + # The channel to the client + self.s2c = io.BytesIO() + self.handler = handler + self.handler.start(self.c2s.readline, self.push_data) + + def readable(self): + return True + + def writable(self): + return True + + def push_data(self, data): + """Push (buffer) some data to send to the client.""" + pos = self.s2c.tell() + self.s2c.seek(0, 2) + self.s2c.write(data) + self.s2c.seek(pos) + + def write(self, b): + """The client sends us some data""" + pos = self.c2s.tell() + self.c2s.write(b) + self.c2s.seek(pos) + self.handler.process_pending() + return len(b) + + def readinto(self, buf): + """The client wants to read a response""" + self.handler.process_pending() + b = self.s2c.read(len(buf)) + n = len(b) + buf[:n] = b + return n + + +class MockedNNTPTestsMixin: + # Override in derived classes + handler_class = None + + def setUp(self): + super().setUp() + self.make_server() + + def tearDown(self): + super().tearDown() + del self.server + + def make_server(self, *args, **kwargs): + self.handler = self.handler_class() + self.sio = _NNTPServerIO(self.handler) + # Using BufferedRWPair instead of BufferedRandom ensures the file + # isn't seekable. + file = io.BufferedRWPair(self.sio, self.sio) + self.server = nntplib._NNTPBase(file, *args, **kwargs) + return self.server + + +class NNTPv1Handler: + """A handler for RFC 977""" + + welcome = "200 NNTP mock server" + + def start(self, readline, push_data): + self.in_body = False + self.allow_posting = True + self._readline = readline + self._push_data = push_data + # Our welcome + self.handle_welcome() + + def _decode(self, data): + return str(data, "utf-8", "surrogateescape") + + def process_pending(self): + if self.in_body: + while True: + line = self._readline() + if not line: + return + self.body.append(line) + if line == b".\r\n": + break + try: + meth, tokens = self.body_callback + meth(*tokens, body=self.body) + finally: + self.body_callback = None + self.body = None + self.in_body = False + while True: + line = self._decode(self._readline()) + if not line: + return + if not line.endswith("\r\n"): + raise ValueError("line doesn't end with \\r\\n: {!r}".format(line)) + line = line[:-2] + cmd, *tokens = line.split() + #meth = getattr(self.handler, "handle_" + cmd.upper(), None) + meth = getattr(self, "handle_" + cmd.upper(), None) + if meth is None: + self.handle_unknown() + else: + try: + meth(*tokens) + except Exception as e: + raise ValueError("command failed: {!r}".format(line)) from e + else: + if self.in_body: + self.body_callback = meth, tokens + self.body = [] + + def expect_body(self): + """Flag that the client is expected to post a request body""" + self.in_body = True + + def push_data(self, data): + """Push some binary data""" + self._push_data(data) + + def push_lit(self, lit): + """Push a string literal""" + lit = textwrap.dedent(lit) + lit = "\r\n".join(lit.splitlines()) + "\r\n" + lit = lit.encode('utf-8') + self.push_data(lit) + + def handle_unknown(self): + self.push_lit("500 What?") + + def handle_welcome(self): + self.push_lit(self.welcome) + + def handle_QUIT(self): + self.push_lit("205 Bye!") + + def handle_DATE(self): + self.push_lit("111 20100914001155") + + def handle_GROUP(self, group): + if group == "fr.comp.lang.python": + self.push_lit("211 486 761 1265 fr.comp.lang.python") + else: + self.push_lit("411 No such group {}".format(group)) + + def handle_HELP(self): + self.push_lit("""\ + 100 Legal commands + authinfo user Name|pass Password|generic + date + help + Report problems to + .""") + + def handle_STAT(self, message_spec=None): + if message_spec is None: + self.push_lit("412 No newsgroup selected") + elif message_spec == "3000234": + self.push_lit("223 3000234 <45223423 at example.com>") + elif message_spec == "<45223423 at example.com>": + self.push_lit("223 0 <45223423 at example.com>") + else: + self.push_lit("430 No Such Article Found") + + def handle_NEXT(self): + self.push_lit("223 3000237 <668929 at example.org> retrieved") + + def handle_LAST(self): + self.push_lit("223 3000234 <45223423 at example.com> retrieved") + + def handle_LIST(self, action=None, param=None): + if action is None: + self.push_lit("""\ + 215 Newsgroups in form "group high low flags". + comp.lang.python 0000052340 0000002828 y + comp.lang.python.announce 0000001153 0000000993 m + free.it.comp.lang.python 0000000002 0000000002 y + fr.comp.lang.python 0000001254 0000000760 y + free.it.comp.lang.python.learner 0000000000 0000000001 y + tw.bbs.comp.lang.python 0000000304 0000000304 y + .""") + elif action == "OVERVIEW.FMT": + self.push_lit("""\ + 215 Order of fields in overview database. + Subject: + From: + Date: + Message-ID: + References: + Bytes: + Lines: + Xref:full + .""") + elif action == "NEWSGROUPS": + assert param is not None + if param == "comp.lang.python": + self.push_lit("""\ + 215 Descriptions in form "group description". + comp.lang.python\tThe Python computer language. + .""") + elif param == "comp.lang.python*": + self.push_lit("""\ + 215 Descriptions in form "group description". + comp.lang.python.announce\tAnnouncements about the Python language. (Moderated) + comp.lang.python\tThe Python computer language. + .""") + else: + self.push_lit("""\ + 215 Descriptions in form "group description". + .""") + else: + self.push_lit('501 Unknown LIST keyword') + + def handle_NEWNEWS(self, group, date_str, time_str): + # We hard code different return messages depending on passed + # argument and date syntax. + if (group == "comp.lang.python" and date_str == "20100913" + and time_str == "082004"): + # Date was passed in RFC 3977 format (NNTP "v2") + self.push_lit("""\ + 230 list of newsarticles (NNTP v2) created after Mon Sep 13 08:20:04 2010 follows + + + .""") + elif (group == "comp.lang.python" and date_str == "100913" + and time_str == "082004"): + # Date was passed in RFC 977 format (NNTP "v1") + self.push_lit("""\ + 230 list of newsarticles (NNTP v1) created after Mon Sep 13 08:20:04 2010 follows + + + .""") + else: + self.push_lit("""\ + 230 An empty list of newsarticles follows + .""") + # (Note for experiments: many servers disable NEWNEWS. + # As of this writing, sicinfo3.epfl.ch doesn't.) + + def handle_XOVER(self, message_spec): + if message_spec == "57-59": + self.push_lit( + "224 Overview information for 57-58 follows\n" + "57\tRe: ANN: New Plone book with strong Python (and Zope) themes throughout" + "\tDoug Hellmann " + "\tSat, 19 Jun 2010 18:04:08 -0400" + "\t<4FD05F05-F98B-44DC-8111-C6009C925F0C at gmail.com>" + "\t\t7103\t16" + "\tXref: news.gmane.org gmane.comp.python.authors:57" + "\n" + "58\tLooking for a few good bloggers" + "\tDoug Hellmann " + "\tThu, 22 Jul 2010 09:14:14 -0400" + "\t" + "\t\t6683\t16" + "\tXref: news.gmane.org gmane.comp.python.authors:58" + "\n" + # An UTF-8 overview line from fr.comp.lang.python + "59\tRe: Message d'erreur incompr?hensible (par moi)" + "\tEric Brunel " + "\tWed, 15 Sep 2010 18:09:15 +0200" + "\t" + "\t<4c90ec87$0$32425$ba4acef3 at reader.news.orange.fr>\t1641\t27" + "\tXref: saria.nerim.net fr.comp.lang.python:1265" + "\n" + ".\n") + else: + self.push_lit("""\ + 224 No articles + .""") + + def handle_POST(self, *, body=None): + if body is None: + if self.allow_posting: + self.push_lit("340 Input article; end with .") + self.expect_body() + else: + self.push_lit("440 Posting not permitted") + else: + assert self.allow_posting + self.push_lit("240 Article received OK") + self.posted_body = body + + def handle_IHAVE(self, message_id, *, body=None): + if body is None: + if (self.allow_posting and + message_id == ""): + self.push_lit("335 Send it; end with .") + self.expect_body() + else: + self.push_lit("435 Article not wanted") + else: + assert self.allow_posting + self.push_lit("235 Article transferred OK") + self.posted_body = body + + sample_head = """\ + From: "Demo User" + Subject: I am just a test article + Content-Type: text/plain; charset=UTF-8; format=flowed + Message-ID: """ + + sample_body = """\ + This is just a test article. + ..Here is a dot-starting line. + + -- Signed by Andr\xe9.""" + + sample_article = sample_head + "\n\n" + sample_body + + def handle_ARTICLE(self, message_spec=None): + if message_spec is None: + self.push_lit("220 3000237 <45223423 at example.com>") + elif message_spec == "<45223423 at example.com>": + self.push_lit("220 0 <45223423 at example.com>") + elif message_spec == "3000234": + self.push_lit("220 3000234 <45223423 at example.com>") + else: + self.push_lit("430 No Such Article Found") + return + self.push_lit(self.sample_article) + self.push_lit(".") + + def handle_HEAD(self, message_spec=None): + if message_spec is None: + self.push_lit("221 3000237 <45223423 at example.com>") + elif message_spec == "<45223423 at example.com>": + self.push_lit("221 0 <45223423 at example.com>") + elif message_spec == "3000234": + self.push_lit("221 3000234 <45223423 at example.com>") + else: + self.push_lit("430 No Such Article Found") + return + self.push_lit(self.sample_head) + self.push_lit(".") + + def handle_BODY(self, message_spec=None): + if message_spec is None: + self.push_lit("222 3000237 <45223423 at example.com>") + elif message_spec == "<45223423 at example.com>": + self.push_lit("222 0 <45223423 at example.com>") + elif message_spec == "3000234": + self.push_lit("222 3000234 <45223423 at example.com>") + else: + self.push_lit("430 No Such Article Found") + return + self.push_lit(self.sample_body) + self.push_lit(".") + + +class NNTPv2Handler(NNTPv1Handler): + """A handler for RFC 3977 (NNTP "v2")""" + + def handle_CAPABILITIES(self): + self.push_lit("""\ + 101 Capability list: + VERSION 2 + IMPLEMENTATION INN 2.5.1 + AUTHINFO USER + HDR + LIST ACTIVE ACTIVE.TIMES DISTRIB.PATS HEADERS NEWSGROUPS OVERVIEW.FMT + OVER + POST + READER + .""") + + def handle_OVER(self, message_spec=None): + return self.handle_XOVER(message_spec) + + +class NNTPv1v2TestsMixin: + + def setUp(self): + super().setUp() + + def test_welcome(self): + self.assertEqual(self.server.welcome, self.handler.welcome) + + def test_date(self): + resp, date = self.server.date() + self.assertEqual(resp, "111 20100914001155") + self.assertEqual(date, datetime.datetime(2010, 9, 14, 0, 11, 55)) + + def test_quit(self): + self.assertFalse(self.sio.closed) + resp = self.server.quit() + self.assertEqual(resp, "205 Bye!") + self.assertTrue(self.sio.closed) + + def test_help(self): + resp, help = self.server.help() + self.assertEqual(resp, "100 Legal commands") + self.assertEqual(help, [ + ' authinfo user Name|pass Password|generic ', + ' date', + ' help', + 'Report problems to ', + ]) + + def test_list(self): + resp, groups = self.server.list() + self.assertEqual(len(groups), 6) + g = groups[1] + self.assertEqual(g, + GroupInfo("comp.lang.python.announce", "0000001153", + "0000000993", "m")) + + def test_stat(self): + resp, art_num, message_id = self.server.stat(3000234) + self.assertEqual(resp, "223 3000234 <45223423 at example.com>") + self.assertEqual(art_num, 3000234) + self.assertEqual(message_id, "<45223423 at example.com>") + resp, art_num, message_id = self.server.stat("<45223423 at example.com>") + self.assertEqual(resp, "223 0 <45223423 at example.com>") + self.assertEqual(art_num, 0) + self.assertEqual(message_id, "<45223423 at example.com>") + with self.assertRaises(nntplib.NNTPTemporaryError) as cm: + self.server.stat("") + self.assertEqual(cm.exception.response, "430 No Such Article Found") + with self.assertRaises(nntplib.NNTPTemporaryError) as cm: + self.server.stat() + self.assertEqual(cm.exception.response, "412 No newsgroup selected") + + def test_next(self): + resp, art_num, message_id = self.server.next() + self.assertEqual(resp, "223 3000237 <668929 at example.org> retrieved") + self.assertEqual(art_num, 3000237) + self.assertEqual(message_id, "<668929 at example.org>") + + def test_last(self): + resp, art_num, message_id = self.server.last() + self.assertEqual(resp, "223 3000234 <45223423 at example.com> retrieved") + self.assertEqual(art_num, 3000234) + self.assertEqual(message_id, "<45223423 at example.com>") + + def test_description(self): + desc = self.server.description("comp.lang.python") + self.assertEqual(desc, "The Python computer language.") + desc = self.server.description("comp.lang.pythonx") + self.assertEqual(desc, "") + + def test_descriptions(self): + resp, groups = self.server.descriptions("comp.lang.python") + self.assertEqual(resp, '215 Descriptions in form "group description".') + self.assertEqual(groups, { + "comp.lang.python": "The Python computer language.", + }) + resp, groups = self.server.descriptions("comp.lang.python*") + self.assertEqual(groups, { + "comp.lang.python": "The Python computer language.", + "comp.lang.python.announce": "Announcements about the Python language. (Moderated)", + }) + resp, groups = self.server.descriptions("comp.lang.pythonx") + self.assertEqual(groups, {}) + + def test_group(self): + resp, count, first, last, group = self.server.group("fr.comp.lang.python") + self.assertTrue(resp.startswith("211 "), resp) + self.assertEqual(first, 761) + self.assertEqual(last, 1265) + self.assertEqual(count, 486) + self.assertEqual(group, "fr.comp.lang.python") + with self.assertRaises(nntplib.NNTPTemporaryError) as cm: + self.server.group("comp.lang.python.devel") + exc = cm.exception + self.assertTrue(exc.response.startswith("411 No such group"), + exc.response) + + def test_newnews(self): + # NEWNEWS comp.lang.python [20]100913 082004 + dt = datetime.datetime(2010, 9, 13, 8, 20, 4) + resp, ids = self.server.newnews("comp.lang.python", dt) + expected = ( + "230 list of newsarticles (NNTP v{0}) " + "created after Mon Sep 13 08:20:04 2010 follows" + ).format(self.nntp_version) + self.assertEqual(resp, expected) + self.assertEqual(ids, [ + "", + "", + ]) + # NEWNEWS fr.comp.lang.python [20]100913 082004 + dt = datetime.datetime(2010, 9, 13, 8, 20, 4) + resp, ids = self.server.newnews("fr.comp.lang.python", dt) + self.assertEqual(resp, "230 An empty list of newsarticles follows") + self.assertEqual(ids, []) + + def _check_article_body(self, lines): + self.assertEqual(len(lines), 4) + self.assertEqual(lines[-1].decode('utf8'), "-- Signed by Andr?.") + self.assertEqual(lines[-2], b"") + self.assertEqual(lines[-3], b".Here is a dot-starting line.") + self.assertEqual(lines[-4], b"This is just a test article.") + + def _check_article_head(self, lines): + self.assertEqual(len(lines), 4) + self.assertEqual(lines[0], b'From: "Demo User" ') + self.assertEqual(lines[3], b"Message-ID: ") + + def _check_article_data(self, lines): + self.assertEqual(len(lines), 9) + self._check_article_head(lines[:4]) + self._check_article_body(lines[-4:]) + self.assertEqual(lines[4], b"") + + def test_article(self): + # ARTICLE + resp, info = self.server.article() + self.assertEqual(resp, "220 3000237 <45223423 at example.com>") + art_num, message_id, lines = info + self.assertEqual(art_num, 3000237) + self.assertEqual(message_id, "<45223423 at example.com>") + self._check_article_data(lines) + # ARTICLE num + resp, info = self.server.article(3000234) + self.assertEqual(resp, "220 3000234 <45223423 at example.com>") + art_num, message_id, lines = info + self.assertEqual(art_num, 3000234) + self.assertEqual(message_id, "<45223423 at example.com>") + self._check_article_data(lines) + # ARTICLE id + resp, info = self.server.article("<45223423 at example.com>") + self.assertEqual(resp, "220 0 <45223423 at example.com>") + art_num, message_id, lines = info + self.assertEqual(art_num, 0) + self.assertEqual(message_id, "<45223423 at example.com>") + self._check_article_data(lines) + # Non-existent id + with self.assertRaises(nntplib.NNTPTemporaryError) as cm: + self.server.article("") + self.assertEqual(cm.exception.response, "430 No Such Article Found") + + def test_article_file(self): + # With a "file" argument + f = io.BytesIO() + resp, info = self.server.article(file=f) + self.assertEqual(resp, "220 3000237 <45223423 at example.com>") + art_num, message_id, lines = info + self.assertEqual(art_num, 3000237) + self.assertEqual(message_id, "<45223423 at example.com>") + self.assertEqual(lines, []) + data = f.getvalue() + self.assertTrue(data.startswith( + b'From: "Demo User" \r\n' + b'Subject: I am just a test article\r\n' + ), ascii(data)) + self.assertTrue(data.endswith( + b'This is just a test article.\r\n' + b'.Here is a dot-starting line.\r\n' + b'\r\n' + b'-- Signed by Andr\xc3\xa9.\r\n' + ), ascii(data)) + + def test_head(self): + # HEAD + resp, info = self.server.head() + self.assertEqual(resp, "221 3000237 <45223423 at example.com>") + art_num, message_id, lines = info + self.assertEqual(art_num, 3000237) + self.assertEqual(message_id, "<45223423 at example.com>") + self._check_article_head(lines) + # HEAD num + resp, info = self.server.head(3000234) + self.assertEqual(resp, "221 3000234 <45223423 at example.com>") + art_num, message_id, lines = info + self.assertEqual(art_num, 3000234) + self.assertEqual(message_id, "<45223423 at example.com>") + self._check_article_head(lines) + # HEAD id + resp, info = self.server.head("<45223423 at example.com>") + self.assertEqual(resp, "221 0 <45223423 at example.com>") + art_num, message_id, lines = info + self.assertEqual(art_num, 0) + self.assertEqual(message_id, "<45223423 at example.com>") + self._check_article_head(lines) + # Non-existent id + with self.assertRaises(nntplib.NNTPTemporaryError) as cm: + self.server.head("") + self.assertEqual(cm.exception.response, "430 No Such Article Found") + + def test_body(self): + # BODY + resp, info = self.server.body() + self.assertEqual(resp, "222 3000237 <45223423 at example.com>") + art_num, message_id, lines = info + self.assertEqual(art_num, 3000237) + self.assertEqual(message_id, "<45223423 at example.com>") + self._check_article_body(lines) + # BODY num + resp, info = self.server.body(3000234) + self.assertEqual(resp, "222 3000234 <45223423 at example.com>") + art_num, message_id, lines = info + self.assertEqual(art_num, 3000234) + self.assertEqual(message_id, "<45223423 at example.com>") + self._check_article_body(lines) + # BODY id + resp, info = self.server.body("<45223423 at example.com>") + self.assertEqual(resp, "222 0 <45223423 at example.com>") + art_num, message_id, lines = info + self.assertEqual(art_num, 0) + self.assertEqual(message_id, "<45223423 at example.com>") + self._check_article_body(lines) + # Non-existent id + with self.assertRaises(nntplib.NNTPTemporaryError) as cm: + self.server.body("") + self.assertEqual(cm.exception.response, "430 No Such Article Found") + + def check_over_xover_resp(self, resp, overviews): + self.assertTrue(resp.startswith("224 "), resp) + self.assertEqual(len(overviews), 3) + art_num, over = overviews[0] + self.assertEqual(art_num, 57) + self.assertEqual(over, { + "from": "Doug Hellmann ", + "subject": "Re: ANN: New Plone book with strong Python (and Zope) themes throughout", + "date": "Sat, 19 Jun 2010 18:04:08 -0400", + "message-id": "<4FD05F05-F98B-44DC-8111-C6009C925F0C at gmail.com>", + "references": "", + ":bytes": "7103", + ":lines": "16", + "xref": "news.gmane.org gmane.comp.python.authors:57" + }) + art_num, over = overviews[2] + self.assertEqual(over["subject"], + "Re: Message d'erreur incompr?hensible (par moi)") + + def test_xover(self): + resp, overviews = self.server.xover(57, 59) + self.check_over_xover_resp(resp, overviews) + + def test_over(self): + # In NNTP "v1", this will fallback on XOVER + resp, overviews = self.server.over((57, 59)) + self.check_over_xover_resp(resp, overviews) + + sample_post = ( + b'From: "Demo User" \r\n' + b'Subject: I am just a test article\r\n' + b'Content-Type: text/plain; charset=UTF-8; format=flowed\r\n' + b'Message-ID: \r\n' + b'\r\n' + b'This is just a test article.\r\n' + b'.Here is a dot-starting line.\r\n' + b'\r\n' + b'-- Signed by Andr\xc3\xa9.\r\n' + ) + + def _check_posted_body(self): + # Check the raw body as received by the server + lines = self.handler.posted_body + # One additional line for the "." terminator + self.assertEqual(len(lines), 10) + self.assertEqual(lines[-1], b'.\r\n') + self.assertEqual(lines[-2], b'-- Signed by Andr\xc3\xa9.\r\n') + self.assertEqual(lines[-3], b'\r\n') + self.assertEqual(lines[-4], b'..Here is a dot-starting line.\r\n') + self.assertEqual(lines[0], b'From: "Demo User" \r\n') + + def _check_post_ihave_sub(self, func, *args, file_factory): + # First the prepared post with CRLF endings + post = self.sample_post + func_args = args + (file_factory(post),) + self.handler.posted_body = None + resp = func(*func_args) + self._check_posted_body() + # Then the same post with "normal" line endings - they should be + # converted by NNTP.post and NNTP.ihave. + post = self.sample_post.replace(b"\r\n", b"\n") + func_args = args + (file_factory(post),) + self.handler.posted_body = None + resp = func(*func_args) + self._check_posted_body() + return resp + + def check_post_ihave(self, func, success_resp, *args): + # With a bytes object + resp = self._check_post_ihave_sub(func, *args, file_factory=bytes) + self.assertEqual(resp, success_resp) + # With a bytearray object + resp = self._check_post_ihave_sub(func, *args, file_factory=bytearray) + self.assertEqual(resp, success_resp) + # With a file object + resp = self._check_post_ihave_sub(func, *args, file_factory=io.BytesIO) + self.assertEqual(resp, success_resp) + # With an iterable of terminated lines + def iterlines(b): + return iter(b.splitlines(True)) + resp = self._check_post_ihave_sub(func, *args, file_factory=iterlines) + self.assertEqual(resp, success_resp) + # With an iterable of non-terminated lines + def iterlines(b): + return iter(b.splitlines(False)) + resp = self._check_post_ihave_sub(func, *args, file_factory=iterlines) + self.assertEqual(resp, success_resp) + + def test_post(self): + self.check_post_ihave(self.server.post, "240 Article received OK") + self.handler.allow_posting = False + with self.assertRaises(nntplib.NNTPTemporaryError) as cm: + self.server.post(self.sample_post) + self.assertEqual(cm.exception.response, + "440 Posting not permitted") + + def test_ihave(self): + self.check_post_ihave(self.server.ihave, "235 Article transferred OK", + "") + with self.assertRaises(nntplib.NNTPTemporaryError) as cm: + self.server.ihave("", self.sample_post) + self.assertEqual(cm.exception.response, + "435 Article not wanted") + + +class NNTPv1Tests(NNTPv1v2TestsMixin, MockedNNTPTestsMixin, unittest.TestCase): + """Tests an NNTP v1 server (no capabilities).""" + + nntp_version = 1 + handler_class = NNTPv1Handler + + def test_caps(self): + caps = self.server.getcapabilities() + self.assertEqual(caps, {}) + self.assertEqual(self.server.nntp_version, 1) + + +class NNTPv2Tests(NNTPv1v2TestsMixin, MockedNNTPTestsMixin, unittest.TestCase): + """Tests an NNTP v2 server (with capabilities).""" + + nntp_version = 2 + handler_class = NNTPv2Handler + + def test_caps(self): + caps = self.server.getcapabilities() + self.assertEqual(caps, { + 'VERSION': ['2'], + 'IMPLEMENTATION': ['INN', '2.5.1'], + 'AUTHINFO': ['USER'], + 'HDR': [], + 'LIST': ['ACTIVE', 'ACTIVE.TIMES', 'DISTRIB.PATS', + 'HEADERS', 'NEWSGROUPS', 'OVERVIEW.FMT'], + 'OVER': [], + 'POST': [], + 'READER': [], + }) + self.assertEqual(self.server.nntp_version, 2) + + +class MiscTests(unittest.TestCase): + + def test_decode_header(self): + def gives(a, b): + self.assertEqual(nntplib.decode_header(a), b) + gives("" , "") + gives("a plain header", "a plain header") + gives(" with extra spaces ", " with extra spaces ") + gives("=?ISO-8859-15?Q?D=E9buter_en_Python?=", "D?buter en Python") + gives("=?utf-8?q?Re=3A_=5Bsqlite=5D_probl=C3=A8me_avec_ORDER_BY_sur_des_cha?=" + " =?utf-8?q?=C3=AEnes_de_caract=C3=A8res_accentu=C3=A9es?=", + "Re: [sqlite] probl?me avec ORDER BY sur des cha?nes de caract?res accentu?es") + gives("Re: =?UTF-8?B?cHJvYmzDqG1lIGRlIG1hdHJpY2U=?=", + "Re: probl?me de matrice") + # A natively utf-8 header (found in the real world!) + gives("Re: Message d'erreur incompr?hensible (par moi)", + "Re: Message d'erreur incompr?hensible (par moi)") + + def test_parse_overview_fmt(self): + # The minimal (default) response + lines = ["Subject:", "From:", "Date:", "Message-ID:", + "References:", ":bytes", ":lines"] + self.assertEqual(nntplib._parse_overview_fmt(lines), + ["subject", "from", "date", "message-id", "references", + ":bytes", ":lines"]) + # The minimal response using alternative names + lines = ["Subject:", "From:", "Date:", "Message-ID:", + "References:", "Bytes:", "Lines:"] + self.assertEqual(nntplib._parse_overview_fmt(lines), + ["subject", "from", "date", "message-id", "references", + ":bytes", ":lines"]) + # Variations in casing + lines = ["subject:", "FROM:", "DaTe:", "message-ID:", + "References:", "BYTES:", "Lines:"] + self.assertEqual(nntplib._parse_overview_fmt(lines), + ["subject", "from", "date", "message-id", "references", + ":bytes", ":lines"]) + # First example from RFC 3977 + lines = ["Subject:", "From:", "Date:", "Message-ID:", + "References:", ":bytes", ":lines", "Xref:full", + "Distribution:full"] + self.assertEqual(nntplib._parse_overview_fmt(lines), + ["subject", "from", "date", "message-id", "references", + ":bytes", ":lines", "xref", "distribution"]) + # Second example from RFC 3977 + lines = ["Subject:", "From:", "Date:", "Message-ID:", + "References:", "Bytes:", "Lines:", "Xref:FULL", + "Distribution:FULL"] + self.assertEqual(nntplib._parse_overview_fmt(lines), + ["subject", "from", "date", "message-id", "references", + ":bytes", ":lines", "xref", "distribution"]) + # A classic response from INN + lines = ["Subject:", "From:", "Date:", "Message-ID:", + "References:", "Bytes:", "Lines:", "Xref:full"] + self.assertEqual(nntplib._parse_overview_fmt(lines), + ["subject", "from", "date", "message-id", "references", + ":bytes", ":lines", "xref"]) + + def test_parse_overview(self): + fmt = nntplib._DEFAULT_OVERVIEW_FMT + ["xref"] + # First example from RFC 3977 + lines = [ + '3000234\tI am just a test article\t"Demo User" ' + '\t6 Oct 1998 04:38:40 -0500\t' + '<45223423 at example.com>\t<45454 at example.net>\t1234\t' + '17\tXref: news.example.com misc.test:3000363', + ] + overview = nntplib._parse_overview(lines, fmt) + (art_num, fields), = overview + self.assertEqual(art_num, 3000234) + self.assertEqual(fields, { + 'subject': 'I am just a test article', + 'from': '"Demo User" ', + 'date': '6 Oct 1998 04:38:40 -0500', + 'message-id': '<45223423 at example.com>', + 'references': '<45454 at example.net>', + ':bytes': '1234', + ':lines': '17', + 'xref': 'news.example.com misc.test:3000363', + }) + + def test_parse_datetime(self): + def gives(a, b, *c): + self.assertEqual(nntplib._parse_datetime(a, b), + datetime.datetime(*c)) + # Output of DATE command + gives("19990623135624", None, 1999, 6, 23, 13, 56, 24) + # Variations + gives("19990623", "135624", 1999, 6, 23, 13, 56, 24) + gives("990623", "135624", 1999, 6, 23, 13, 56, 24) + gives("090623", "135624", 2009, 6, 23, 13, 56, 24) + + def test_unparse_datetime(self): + # Test non-legacy mode + # 1) with a datetime + def gives(y, M, d, h, m, s, date_str, time_str): + dt = datetime.datetime(y, M, d, h, m, s) + self.assertEqual(nntplib._unparse_datetime(dt), + (date_str, time_str)) + self.assertEqual(nntplib._unparse_datetime(dt, False), + (date_str, time_str)) + gives(1999, 6, 23, 13, 56, 24, "19990623", "135624") + gives(2000, 6, 23, 13, 56, 24, "20000623", "135624") + gives(2010, 6, 5, 1, 2, 3, "20100605", "010203") + # 2) with a date + def gives(y, M, d, date_str, time_str): + dt = datetime.date(y, M, d) + self.assertEqual(nntplib._unparse_datetime(dt), + (date_str, time_str)) + self.assertEqual(nntplib._unparse_datetime(dt, False), + (date_str, time_str)) + gives(1999, 6, 23, "19990623", "000000") + gives(2000, 6, 23, "20000623", "000000") + gives(2010, 6, 5, "20100605", "000000") + + def test_unparse_datetime_legacy(self): + # Test legacy mode (RFC 977) + # 1) with a datetime + def gives(y, M, d, h, m, s, date_str, time_str): + dt = datetime.datetime(y, M, d, h, m, s) + self.assertEqual(nntplib._unparse_datetime(dt, True), + (date_str, time_str)) + gives(1999, 6, 23, 13, 56, 24, "990623", "135624") + gives(2000, 6, 23, 13, 56, 24, "000623", "135624") + gives(2010, 6, 5, 1, 2, 3, "100605", "010203") + # 2) with a date + def gives(y, M, d, date_str, time_str): + dt = datetime.date(y, M, d) + self.assertEqual(nntplib._unparse_datetime(dt, True), + (date_str, time_str)) + gives(1999, 6, 23, "990623", "000000") + gives(2000, 6, 23, "000623", "000000") + gives(2010, 6, 5, "100605", "000000") + + +def test_main(): + support.run_unittest(MiscTests, NNTPv1Tests, NNTPv2Tests, + NetworkedNNTPTests + ) + + +if __name__ == "__main__": + test_main() Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 29 17:03:40 2010 @@ -76,6 +76,10 @@ Library ------- +- Issue #9360: Cleanup and improvements to the nntplib module. The API + now conforms to the philosophy of bytes and unicode separation in Python 3. + A test suite has also been added. + - Issue #9962: GzipFile now has the peek() method. - Issue #9090: When a socket with a timeout fails with EWOULDBLOCK or EAGAIN, From python-checkins at python.org Wed Sep 29 17:50:39 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Wed, 29 Sep 2010 17:50:39 +0200 (CEST) Subject: [Python-checkins] r85112 - python/branches/py3k-issue9978 Message-ID: <20100929155039.7CEF8EE9B7@mail.python.org> Author: hirokazu.yamamoto Date: Wed Sep 29 17:50:39 2010 New Revision: 85112 Log: Remove branch. Removed: python/branches/py3k-issue9978/ From python-checkins at python.org Wed Sep 29 18:08:29 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 18:08:29 +0200 (CEST) Subject: [Python-checkins] r85113 - python/branches/py3k/Lib/test/test_nntplib.py Message-ID: <20100929160829.9C858EE9D4@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 18:08:29 2010 New Revision: 85113 Log: Disable the usenetrc option when testing nntplib Modified: python/branches/py3k/Lib/test/test_nntplib.py Modified: python/branches/py3k/Lib/test/test_nntplib.py ============================================================================== --- python/branches/py3k/Lib/test/test_nntplib.py (original) +++ python/branches/py3k/Lib/test/test_nntplib.py Wed Sep 29 18:08:29 2010 @@ -164,7 +164,7 @@ def setUp(self): support.requires("network") with support.transient_internet(self.NNTP_HOST): - self.server = NNTP(self.NNTP_HOST, timeout=TIMEOUT) + self.server = NNTP(self.NNTP_HOST, timeout=TIMEOUT, usenetrc=False) def tearDown(self): if self.server is not None: @@ -254,6 +254,7 @@ # Using BufferedRWPair instead of BufferedRandom ensures the file # isn't seekable. file = io.BufferedRWPair(self.sio, self.sio) + kwargs.setdefault('usenetrc', False) self.server = nntplib._NNTPBase(file, *args, **kwargs) return self.server From python-checkins at python.org Wed Sep 29 18:19:50 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 29 Sep 2010 18:19:50 +0200 (CEST) Subject: [Python-checkins] r85114 - in python/branches/py3k/Lib: nntplib.py test/test_nntplib.py Message-ID: <20100929161950.E04E4FBB1@mail.python.org> Author: antoine.pitrou Date: Wed Sep 29 18:19:50 2010 New Revision: 85114 Log: Fix NNTP when there's a ".netrc" file Modified: python/branches/py3k/Lib/nntplib.py python/branches/py3k/Lib/test/test_nntplib.py Modified: python/branches/py3k/Lib/nntplib.py ============================================================================== --- python/branches/py3k/Lib/nntplib.py (original) +++ python/branches/py3k/Lib/nntplib.py Wed Sep 29 18:19:50 2010 @@ -279,11 +279,12 @@ encoding = 'utf-8' errors = 'surrogateescape' - def __init__(self, file, user=None, password=None, + def __init__(self, file, host, user=None, password=None, readermode=None, usenetrc=True, timeout=_GLOBAL_DEFAULT_TIMEOUT): """Initialize an instance. Arguments: - file: file-like object (open for read/write in binary mode) + - host: hostname of the server (used if `usenetrc` is True) - user: username to authenticate with - password: password to use with username - readermode: if true, send 'mode reader' command after @@ -933,7 +934,7 @@ self.port = port self.sock = socket.create_connection((host, port), timeout) file = self.sock.makefile("rwb") - _NNTPBase.__init__(self, file, user, password, + _NNTPBase.__init__(self, file, host, user, password, readermode, usenetrc, timeout) def _close(self): Modified: python/branches/py3k/Lib/test/test_nntplib.py ============================================================================== --- python/branches/py3k/Lib/test/test_nntplib.py (original) +++ python/branches/py3k/Lib/test/test_nntplib.py Wed Sep 29 18:19:50 2010 @@ -12,6 +12,7 @@ # TODO: # - test the `file` arg to more commands # - test error conditions +# - test auth and `usenetrc` class NetworkedNNTPTestsMixin: @@ -255,7 +256,7 @@ # isn't seekable. file = io.BufferedRWPair(self.sio, self.sio) kwargs.setdefault('usenetrc', False) - self.server = nntplib._NNTPBase(file, *args, **kwargs) + self.server = nntplib._NNTPBase(file, 'test.server', *args, **kwargs) return self.server From python-checkins at python.org Wed Sep 29 18:35:47 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 18:35:47 +0200 (CEST) Subject: [Python-checkins] r85115 - in python/branches/py3k: Include/code.h Objects/codeobject.c Objects/object.c Objects/unicodeobject.c Python/pythonrun.c Message-ID: <20100929163547.BFCEFF000@mail.python.org> Author: victor.stinner Date: Wed Sep 29 18:35:47 2010 New Revision: 85115 Log: Issue #9630: Redecode filenames when setting the filesystem encoding Redecode the filenames of: - all modules: __file__ and __path__ attributes - all code objects: co_filename attribute - sys.path - sys.meta_path - sys.executable - sys.path_importer_cache (keys) Keep weak references to all code objects until initfsencoding() is called, to be able to redecode co_filename attribute of all code objects. Modified: python/branches/py3k/Include/code.h python/branches/py3k/Objects/codeobject.c python/branches/py3k/Objects/object.c python/branches/py3k/Objects/unicodeobject.c python/branches/py3k/Python/pythonrun.c Modified: python/branches/py3k/Include/code.h ============================================================================== --- python/branches/py3k/Include/code.h (original) +++ python/branches/py3k/Include/code.h Wed Sep 29 18:35:47 2010 @@ -99,6 +99,13 @@ PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, PyObject *lineno_obj); +/* List of weak references to all code objects. The list is used by + initfsencoding() to redecode code filenames at startup if the filesystem + encoding changes. At initfsencoding() exit, the list is set to NULL and it + is no more used. */ + +extern PyObject *_Py_code_object_list; + #ifdef __cplusplus } #endif Modified: python/branches/py3k/Objects/codeobject.c ============================================================================== --- python/branches/py3k/Objects/codeobject.c (original) +++ python/branches/py3k/Objects/codeobject.c Wed Sep 29 18:35:47 2010 @@ -5,6 +5,8 @@ #define NAME_CHARS \ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" +PyObject *_Py_code_object_list = NULL; + /* all_name_chars(s): true iff all chars in s are valid NAME_CHARS */ static int @@ -109,8 +111,23 @@ co->co_lnotab = lnotab; co->co_zombieframe = NULL; co->co_weakreflist = NULL; + + if (_Py_code_object_list != NULL) { + int err; + PyObject *ref = PyWeakref_NewRef((PyObject*)co, NULL); + if (ref == NULL) + goto error; + err = PyList_Append(_Py_code_object_list, ref); + Py_DECREF(ref); + if (err) + goto error; + } } return co; + +error: + Py_DECREF(co); + return NULL; } PyCodeObject * Modified: python/branches/py3k/Objects/object.c ============================================================================== --- python/branches/py3k/Objects/object.c (original) +++ python/branches/py3k/Objects/object.c Wed Sep 29 18:35:47 2010 @@ -1604,6 +1604,10 @@ if (PyType_Ready(&PyCode_Type) < 0) Py_FatalError("Can't initialize code type"); + _Py_code_object_list = PyList_New(0); + if (_Py_code_object_list == NULL) + Py_FatalError("Can't initialize code type"); + if (PyType_Ready(&PyFrame_Type) < 0) Py_FatalError("Can't initialize frame type"); Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Wed Sep 29 18:35:47 2010 @@ -1510,10 +1510,14 @@ return PyUnicode_AsEncodedString(unicode, Py_FileSystemDefaultEncoding, "surrogateescape"); - } else + } + else { + /* if you change the default encoding, update also + PyUnicode_DecodeFSDefaultAndSize() and redecode_filenames() */ return PyUnicode_EncodeUTF8(PyUnicode_AS_UNICODE(unicode), PyUnicode_GET_SIZE(unicode), "surrogateescape"); + } } PyObject *PyUnicode_AsEncodedString(PyObject *unicode, @@ -1680,6 +1684,8 @@ "surrogateescape"); } else { + /* if you change the default encoding, update also + PyUnicode_EncodeFSDefault() and redecode_filenames() */ return PyUnicode_DecodeUTF8(s, size, "surrogateescape"); } } Modified: python/branches/py3k/Python/pythonrun.c ============================================================================== --- python/branches/py3k/Python/pythonrun.c (original) +++ python/branches/py3k/Python/pythonrun.c Wed Sep 29 18:35:47 2010 @@ -719,6 +719,259 @@ } } +/* Redecode a filename from the default filesystem encoding (utf-8) to + 'new_encoding' encoding with 'errors' error handler */ +static PyObject* +redecode_filename(PyObject *file, const char *new_encoding, + const char *errors) +{ + PyObject *file_bytes = NULL, *new_file = NULL; + + file_bytes = PyUnicode_EncodeFSDefault(file); + if (file_bytes == NULL) + return NULL; + new_file = PyUnicode_Decode( + PyBytes_AsString(file_bytes), + PyBytes_GET_SIZE(file_bytes), + new_encoding, + errors); + Py_DECREF(file_bytes); + return new_file; +} + +/* Redecode a path list */ +static int +redecode_path_list(PyObject *paths, + const char *new_encoding, const char *errors) +{ + PyObject *filename, *new_filename; + Py_ssize_t i, size; + + size = PyList_Size(paths); + for (i=0; i < size; i++) { + filename = PyList_GetItem(paths, i); + if (filename == NULL) + return -1; + + new_filename = redecode_filename(filename, new_encoding, errors); + if (new_filename == NULL) + return -1; + if (PyList_SetItem(paths, i, new_filename)) { + Py_DECREF(new_filename); + return -1; + } + } + return 0; +} + +/* Redecode __file__ and __path__ attributes of sys.modules */ +static int +redecode_sys_modules(const char *new_encoding, const char *errors) +{ + PyInterpreterState *interp; + PyObject *modules, *values, *file, *new_file, *paths; + PyObject *iter = NULL, *module = NULL; + + interp = PyThreadState_GET()->interp; + modules = interp->modules; + + values = PyObject_CallMethod(modules, "values", ""); + if (values == NULL) + goto error; + + iter = PyObject_GetIter(values); + Py_DECREF(values); + if (iter == NULL) + goto error; + + while (1) + { + module = PyIter_Next(iter); + if (module == NULL) { + if (PyErr_Occurred()) + goto error; + else + break; + } + + file = PyModule_GetFilenameObject(module); + if (file != NULL) { + new_file = redecode_filename(file, new_encoding, errors); + Py_DECREF(file); + if (new_file == NULL) + goto error; + if (PyObject_SetAttrString(module, "__file__", new_file)) { + Py_DECREF(new_file); + goto error; + } + Py_DECREF(new_file); + } + else + PyErr_Clear(); + + paths = PyObject_GetAttrString(module, "__path__"); + if (paths != NULL) { + if (redecode_path_list(paths, new_encoding, errors)) + goto error; + } + else + PyErr_Clear(); + + Py_CLEAR(module); + } + Py_CLEAR(iter); + return 0; + +error: + Py_XDECREF(iter); + Py_XDECREF(module); + return -1; +} + +/* Redecode sys.path_importer_cache keys */ +static int +redecode_sys_path_importer_cache(const char *new_encoding, const char *errors) +{ + PyObject *path_importer_cache, *items, *item, *path, *importer, *new_path; + PyObject *new_cache = NULL, *iter = NULL; + + path_importer_cache = PySys_GetObject("path_importer_cache"); + if (path_importer_cache == NULL) + goto error; + + items = PyObject_CallMethod(path_importer_cache, "items", ""); + if (items == NULL) + goto error; + + iter = PyObject_GetIter(items); + Py_DECREF(items); + if (iter == NULL) + goto error; + + new_cache = PyDict_New(); + if (new_cache == NULL) + goto error; + + while (1) + { + item = PyIter_Next(iter); + if (item == NULL) { + if (PyErr_Occurred()) + goto error; + else + break; + } + path = PyTuple_GET_ITEM(item, 0); + importer = PyTuple_GET_ITEM(item, 1); + + new_path = redecode_filename(path, new_encoding, errors); + if (new_path == NULL) + goto error; + if (PyDict_SetItem(new_cache, new_path, importer)) { + Py_DECREF(new_path); + goto error; + } + Py_DECREF(new_path); + } + Py_CLEAR(iter); + if (PySys_SetObject("path_importer_cache", new_cache)) + goto error; + Py_CLEAR(new_cache); + return 0; + +error: + Py_XDECREF(iter); + Py_XDECREF(new_cache); + return -1; +} + +/* Redecode co_filename attribute of all code objects */ +static int +redecode_code_objects(const char *new_encoding, const char *errors) +{ + Py_ssize_t i, len; + PyCodeObject *co; + PyObject *ref, *new_file; + + len = Py_SIZE(_Py_code_object_list); + for (i=0; i < len; i++) { + ref = PyList_GET_ITEM(_Py_code_object_list, i); + co = (PyCodeObject *)PyWeakref_GetObject(ref); + if ((PyObject*)co == Py_None) + continue; + if (co == NULL) + return -1; + + new_file = redecode_filename(co->co_filename, new_encoding, errors); + if (new_file == NULL) + return -1; + Py_DECREF(co->co_filename); + co->co_filename = new_file; + } + Py_CLEAR(_Py_code_object_list); + return 0; +} + +/* Redecode the filenames of all modules (__file__ and __path__ attributes), + all code objects (co_filename attribute), sys.path, sys.meta_path, + sys.executable and sys.path_importer_cache (keys) when the filesystem + encoding changes from the default encoding (utf-8) to new_encoding */ +static int +redecode_filenames(const char *new_encoding) +{ + char *errors; + PyObject *paths, *executable, *new_executable; + + /* PyUnicode_DecodeFSDefault() and PyUnicode_EncodeFSDefault() do already + use utf-8 if Py_FileSystemDefaultEncoding is NULL */ + if (strcmp(new_encoding, "utf-8") == 0) + return 0; + + if (strcmp(new_encoding, "mbcs") != 0) + errors = "surrogateescape"; + else + errors = NULL; + + /* sys.modules */ + if (redecode_sys_modules(new_encoding, errors)) + return -1; + + /* sys.path and sys.meta_path */ + paths = PySys_GetObject("path"); + if (paths != NULL) { + if (redecode_path_list(paths, new_encoding, errors)) + return -1; + } + paths = PySys_GetObject("meta_path"); + if (paths != NULL) { + if (redecode_path_list(paths, new_encoding, errors)) + return -1; + } + + /* sys.executable */ + executable = PySys_GetObject("executable"); + if (executable == NULL) + return -1; + new_executable = redecode_filename(executable, new_encoding, errors); + if (new_executable == NULL) + return -1; + if (PySys_SetObject("executable", new_executable)) { + Py_DECREF(new_executable); + return -1; + } + Py_DECREF(new_executable); + + /* sys.path_importer_cache */ + if (redecode_sys_path_importer_cache(new_encoding, errors)) + return -1; + + /* code objects */ + if (redecode_code_objects(new_encoding, errors)) + return -1; + + return 0; +} + static void initfsencoding(void) { @@ -744,8 +997,11 @@ codeset = get_codeset(); } if (codeset != NULL) { + if (redecode_filenames(codeset)) + Py_FatalError("Py_Initialize: can't redecode filenames"); Py_FileSystemDefaultEncoding = codeset; Py_HasFileSystemDefaultEncoding = 0; + Py_CLEAR(_Py_code_object_list); return; } else { fprintf(stderr, "Unable to get the locale encoding:\n"); @@ -758,6 +1014,8 @@ } #endif + Py_CLEAR(_Py_code_object_list); + /* the encoding is mbcs, utf-8 or ascii */ codec = _PyCodec_Lookup(Py_FileSystemDefaultEncoding); if (!codec) { From python-checkins at python.org Wed Sep 29 18:59:19 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 18:59:19 +0200 (CEST) Subject: [Python-checkins] r85116 - in python/branches/py3k: Demo/zlib/minigzip.py Lib/posixpath.py Message-ID: <20100929165919.1381DEE9E5@mail.python.org> Author: victor.stinner Date: Wed Sep 29 18:59:18 2010 New Revision: 85116 Log: Use os.fsencode() to support surrogates Modified: python/branches/py3k/Demo/zlib/minigzip.py python/branches/py3k/Lib/posixpath.py Modified: python/branches/py3k/Demo/zlib/minigzip.py ============================================================================== --- python/branches/py3k/Demo/zlib/minigzip.py (original) +++ python/branches/py3k/Demo/zlib/minigzip.py Wed Sep 29 18:59:18 2010 @@ -31,7 +31,7 @@ write32(output, mtime) output.write(b'\002') # ... slowest compression alg. ... output.write(b'\377') # ... OS (=unknown) ... - bfilename = filename.encode(sys.getfilesystemencoding()) + bfilename = os.fsencode(filename) output.write(bfilename + b'\000') # ... original filename ... crcval = zlib.crc32(b'') Modified: python/branches/py3k/Lib/posixpath.py ============================================================================== --- python/branches/py3k/Lib/posixpath.py (original) +++ python/branches/py3k/Lib/posixpath.py Wed Sep 29 18:59:18 2010 @@ -262,7 +262,7 @@ return path userhome = pwent.pw_dir if isinstance(path, bytes): - userhome = userhome.encode(sys.getfilesystemencoding()) + userhome = os.fsencode(userhome) root = b'/' else: root = '/' From python-checkins at python.org Wed Sep 29 18:59:46 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 18:59:46 +0200 (CEST) Subject: [Python-checkins] r85117 - python/branches/py3k/Lib/test/test_imp.py Message-ID: <20100929165946.7FCCBEEA12@mail.python.org> Author: victor.stinner Date: Wed Sep 29 18:59:46 2010 New Revision: 85117 Log: test_imp: getfilesystemencoding() cannot be None anymore And the codec name is normalized. Modified: python/branches/py3k/Lib/test/test_imp.py Modified: python/branches/py3k/Lib/test/test_imp.py ============================================================================== --- python/branches/py3k/Lib/test/test_imp.py (original) +++ python/branches/py3k/Lib/test/test_imp.py Wed Sep 29 18:59:46 2010 @@ -87,7 +87,6 @@ # the return encoding could be uppercase or None fs_encoding = sys.getfilesystemencoding() - fs_encoding = fs_encoding.lower() if fs_encoding else 'ascii' # covers utf-8 and Windows ANSI code pages # one non-space symbol from every page From python-checkins at python.org Wed Sep 29 19:55:13 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 29 Sep 2010 19:55:13 +0200 (CEST) Subject: [Python-checkins] r85118 - python/branches/py3k/Objects/unicodeobject.c Message-ID: <20100929175513.2AD98EEA19@mail.python.org> Author: victor.stinner Date: Wed Sep 29 19:55:12 2010 New Revision: 85118 Log: Fix PyUnicode_AsWideCharString(): set *size if size is not NULL Modified: python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Wed Sep 29 19:55:12 2010 @@ -1215,6 +1215,8 @@ return NULL; } unicode_aswidechar(unicode, buffer, buflen); + if (size) + *size = buflen; return buffer; } From python-checkins at python.org Wed Sep 29 20:41:55 2010 From: python-checkins at python.org (mark.dickinson) Date: Wed, 29 Sep 2010 20:41:55 +0200 (CEST) Subject: [Python-checkins] r85119 - python/branches/py3k/Lib/test/formatfloat_testcases.txt Message-ID: <20100929184155.240B6F126@mail.python.org> Author: mark.dickinson Date: Wed Sep 29 20:41:54 2010 New Revision: 85119 Log: Add testcases from bug report #9980. Modified: python/branches/py3k/Lib/test/formatfloat_testcases.txt Modified: python/branches/py3k/Lib/test/formatfloat_testcases.txt ============================================================================== --- python/branches/py3k/Lib/test/formatfloat_testcases.txt (original) +++ python/branches/py3k/Lib/test/formatfloat_testcases.txt Wed Sep 29 20:41:54 2010 @@ -279,6 +279,11 @@ %.2g 0.000123 -> 0.00012 %.2g 0.0000123 -> 1.2e-05 +-- bad cases from http://bugs.python.org/issue9980 +%.12g 38210.0 -> 38210 +%.12g 37210.0 -> 37210 +%.12g 36210.0 -> 36210 + -- alternate g formatting: always include decimal point and -- exactly significant digits. %#.0g 0 -> 0. From python-checkins at python.org Wed Sep 29 21:06:36 2010 From: python-checkins at python.org (mark.dickinson) Date: Wed, 29 Sep 2010 21:06:36 +0200 (CEST) Subject: [Python-checkins] r85120 - in python/branches/py3k: Lib/test/test_math.py Modules/mathmodule.c Message-ID: <20100929190636.D9CEFDFA9@mail.python.org> Author: mark.dickinson Date: Wed Sep 29 21:06:36 2010 New Revision: 85120 Log: Issue #9599: Further accuracy tweaks to loghelper. For an integer n that's small enough to be converted to a float without overflow, log(n) is now computed as log(float(n)), and similarly for log10. Modified: python/branches/py3k/Lib/test/test_math.py python/branches/py3k/Modules/mathmodule.c Modified: python/branches/py3k/Lib/test/test_math.py ============================================================================== --- python/branches/py3k/Lib/test/test_math.py (original) +++ python/branches/py3k/Lib/test/test_math.py Wed Sep 29 21:06:36 2010 @@ -641,8 +641,12 @@ self.ftest('log(32,2)', math.log(32,2), 5) self.ftest('log(10**40, 10)', math.log(10**40, 10), 40) self.ftest('log(10**40, 10**20)', math.log(10**40, 10**20), 2) - self.assertEquals(math.log(INF), INF) + self.ftest('log(10**1000)', math.log(10**1000), + 2302.5850929940457) + self.assertRaises(ValueError, math.log, -1.5) + self.assertRaises(ValueError, math.log, -10**1000) self.assertRaises(ValueError, math.log, NINF) + self.assertEquals(math.log(INF), INF) self.assertTrue(math.isnan(math.log(NAN))) def testLog1p(self): @@ -655,8 +659,11 @@ self.ftest('log10(0.1)', math.log10(0.1), -1) self.ftest('log10(1)', math.log10(1), 0) self.ftest('log10(10)', math.log10(10), 1) - self.assertEquals(math.log(INF), INF) + self.ftest('log10(10**1000)', math.log10(10**1000), 1000.0) + self.assertRaises(ValueError, math.log10, -1.5) + self.assertRaises(ValueError, math.log10, -10**1000) self.assertRaises(ValueError, math.log10, NINF) + self.assertEquals(math.log(INF), INF) self.assertTrue(math.isnan(math.log10(NAN))) def testModf(self): Modified: python/branches/py3k/Modules/mathmodule.c ============================================================================== --- python/branches/py3k/Modules/mathmodule.c (original) +++ python/branches/py3k/Modules/mathmodule.c Wed Sep 29 21:06:36 2010 @@ -1562,25 +1562,33 @@ { /* If it is long, do it ourselves. */ if (PyLong_Check(arg)) { - double x; + double x, result; Py_ssize_t e; - x = _PyLong_Frexp((PyLongObject *)arg, &e); - if (x == -1.0 && PyErr_Occurred()) - return NULL; - if (x <= 0.0) { + + /* Negative or zero inputs give a ValueError. */ + if (Py_SIZE(arg) <= 0) { PyErr_SetString(PyExc_ValueError, "math domain error"); return NULL; } - /* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. - It's slightly better to compute the log as log(2 * x) + log(2) * (e - - 1): then when 'arg' is a power of 2, 2**k say, this gives us 0.0 + - log(2) * k instead of log(0.5) + log(2)*(k+1), and so marginally - increases the chances of log(arg, 2) returning the correct result. - */ - x = func(2.0 * x) + func(2.0) * (e - 1); - return PyFloat_FromDouble(x); + x = PyLong_AsDouble(arg); + if (x == -1.0 && PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_OverflowError)) + return NULL; + /* Here the conversion to double overflowed, but it's possible + to compute the log anyway. Clear the exception and continue. */ + PyErr_Clear(); + x = _PyLong_Frexp((PyLongObject *)arg, &e); + if (x == -1.0 && PyErr_Occurred()) + return NULL; + /* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */ + result = func(x) + func(2.0) * e; + } + else + /* Successfully converted x to a double. */ + result = func(x); + return PyFloat_FromDouble(result); } /* Else let libm handle it by itself. */ From python-checkins at python.org Wed Sep 29 21:09:33 2010 From: python-checkins at python.org (brian.curtin) Date: Wed, 29 Sep 2010 21:09:33 +0200 (CEST) Subject: [Python-checkins] r85121 - python/branches/py3k/Python/import.c Message-ID: <20100929190933.6B08CF25A@mail.python.org> Author: brian.curtin Date: Wed Sep 29 21:09:33 2010 New Revision: 85121 Log: Remove an unreferenced variable. len is no longer needed. Modified: python/branches/py3k/Python/import.c Modified: python/branches/py3k/Python/import.c ============================================================================== --- python/branches/py3k/Python/import.c (original) +++ python/branches/py3k/Python/import.c Wed Sep 29 21:09:33 2010 @@ -3723,7 +3723,6 @@ PyObject *pathobj; DWORD rv; wchar_t *path; - Py_ssize_t len; if (!_PyArg_NoKeywords("NullImporter()", kwds)) return -1; From solipsis at pitrou.net Thu Sep 30 04:49:19 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 30 Sep 2010 04:49:19 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r85121): sum=0 Message-ID: py3k results for svn r85121 (hg cset 5feac0848f8f) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogWKL_tf', '-x'] From python-checkins at python.org Thu Sep 30 04:51:26 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Thu, 30 Sep 2010 04:51:26 +0200 (CEST) Subject: [Python-checkins] r85122 - python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Message-ID: <20100930025126.43405DDB7@mail.python.org> Author: hirokazu.yamamoto Date: Thu Sep 30 04:51:26 2010 New Revision: 85122 Log: More debug print ... Modified: python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Modified: python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py ============================================================================== --- python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py (original) +++ python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Thu Sep 30 04:51:26 2010 @@ -13,7 +13,7 @@ requires('gui') CODES_SIZE = 100 -WAIT = 10 * 60 # XXX: adjust here +WAIT = 1 * 60 # XXX: adjust here class LabeledScaleTest(unittest.TestCase): @@ -114,9 +114,13 @@ def test_horizontal_range(self): lscale = ttk.LabeledScale(from_=0, to=10) lscale.pack() + print("===> enter lscale.wait_visibility....") lscale.wait_visibility() + print("===> leave lscale.wait_visibility....") lscale.update() + time.sleep(2 * 60) + linfo_1 = lscale.label.place_info() prev_xcoord = lscale.scale.coords()[0] self.assertEqual(prev_xcoord, int(linfo_1['x'])) @@ -281,13 +285,23 @@ if __name__ == "__main__": codes = collections.deque([None] * CODES_SIZE) + code_count = 0 def tracefunc(frame, event, arg): + global code_count + code_count += 1 codes.append(frame.f_code) codes.popleft() sys.settrace(tracefunc) # only main thread now def threadfunc(): - time.sleep(WAIT) + while 1: + old_code_count = code_count + print("----> thread sleeping now...") + time.sleep(WAIT) + print("----> thread awakened ....") + if old_code_count == code_count: + break + print("===================") for code in codes: if code is None: continue From python-checkins at python.org Thu Sep 30 05:06:08 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Thu, 30 Sep 2010 05:06:08 +0200 (CEST) Subject: [Python-checkins] r85123 - python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Message-ID: <20100930030608.8F07BFC46@mail.python.org> Author: hirokazu.yamamoto Date: Thu Sep 30 05:06:08 2010 New Revision: 85123 Log: Probably this is last trial on unittest.... Modified: python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Modified: python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py ============================================================================== --- python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py (original) +++ python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Thu Sep 30 05:06:08 2010 @@ -15,6 +15,9 @@ CODES_SIZE = 100 WAIT = 1 * 60 # XXX: adjust here +def debug(s): + print >>sys.stdout, s + sys.stdout.flush() class LabeledScaleTest(unittest.TestCase): @@ -114,13 +117,11 @@ def test_horizontal_range(self): lscale = ttk.LabeledScale(from_=0, to=10) lscale.pack() - print("===> enter lscale.wait_visibility....") + debug("===> enter lscale.wait_visibility....") lscale.wait_visibility() - print("===> leave lscale.wait_visibility....") + debug("===> leave lscale.wait_visibility....") lscale.update() - time.sleep(2 * 60) - linfo_1 = lscale.label.place_info() prev_xcoord = lscale.scale.coords()[0] self.assertEqual(prev_xcoord, int(linfo_1['x'])) @@ -296,16 +297,16 @@ def threadfunc(): while 1: old_code_count = code_count - print("----> thread sleeping now...") + debug("----> thread sleeping now...") time.sleep(WAIT) - print("----> thread awakened ....") + debug("----> thread awakened ....") if old_code_count == code_count: break - print("===================") + debug("===================") for code in codes: if code is None: continue - print(code) + debug(code) t = threading.Thread(target=threadfunc) t.start() try: From python-checkins at python.org Thu Sep 30 05:27:17 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Thu, 30 Sep 2010 05:27:17 +0200 (CEST) Subject: [Python-checkins] r85124 - python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Message-ID: <20100930032717.42FBEEE9B6@mail.python.org> Author: hirokazu.yamamoto Date: Thu Sep 30 05:27:17 2010 New Revision: 85124 Log: Probably cause is not in python. In tcl/tk code. Modified: python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Modified: python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py ============================================================================== --- python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py (original) +++ python/branches/release27-maint-ttk-debug-on-xp5/Lib/lib-tk/test/test_ttk/test_extensions.py Thu Sep 30 05:27:17 2010 @@ -13,7 +13,7 @@ requires('gui') CODES_SIZE = 100 -WAIT = 1 * 60 # XXX: adjust here +trace_enabled = False def debug(s): print >>sys.stdout, s @@ -115,10 +115,14 @@ def test_horizontal_range(self): + global trace_enabled + lscale = ttk.LabeledScale(from_=0, to=10) lscale.pack() debug("===> enter lscale.wait_visibility....") + trace_enabled = True lscale.wait_visibility() + trace_enabled = False debug("===> leave lscale.wait_visibility....") lscale.update() @@ -285,31 +289,9 @@ tests_gui = (LabeledScaleTest,) if __name__ == "__main__": - codes = collections.deque([None] * CODES_SIZE) - code_count = 0 def tracefunc(frame, event, arg): - global code_count - code_count += 1 - codes.append(frame.f_code) - codes.popleft() + if trace_enabled: + debug(frame.f_code) sys.settrace(tracefunc) # only main thread now - def threadfunc(): - while 1: - old_code_count = code_count - debug("----> thread sleeping now...") - time.sleep(WAIT) - debug("----> thread awakened ....") - if old_code_count == code_count: - break - debug("===================") - for code in codes: - if code is None: - continue - debug(code) - t = threading.Thread(target=threadfunc) - t.start() - try: - run_unittest(*tests_gui) - finally: - t.join() + run_unittest(*tests_gui) From python-checkins at python.org Thu Sep 30 08:09:18 2010 From: python-checkins at python.org (senthil.kumaran) Date: Thu, 30 Sep 2010 08:09:18 +0200 (CEST) Subject: [Python-checkins] r85125 - in python/branches/py3k: Doc/library/http.server.rst Lib/http/server.py Lib/test/test_httpservers.py Misc/NEWS Message-ID: <20100930060918.4092CEEA04@mail.python.org> Author: senthil.kumaran Date: Thu Sep 30 08:09:18 2010 New Revision: 85125 Log: Issue1491 - BaseHTTPServer incorrectly implements response code 100 Modified: python/branches/py3k/Doc/library/http.server.rst python/branches/py3k/Lib/http/server.py python/branches/py3k/Lib/test/test_httpservers.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/http.server.rst ============================================================================== --- python/branches/py3k/Doc/library/http.server.rst (original) +++ python/branches/py3k/Doc/library/http.server.rst Thu Sep 30 08:09:18 2010 @@ -155,6 +155,17 @@ This method will parse and dispatch the request to the appropriate :meth:`do_\*` method. You should never need to override it. + .. method:: handle_expect_100() + + When a HTTP/1.1 compliant server receives a ``Expect: 100-continue`` + request header it responds back with a ``100 Continue`` followed by ``200 + OK`` headers. + This method can be overridden to raise an error if the server does not + want the client to continue. For e.g. server can chose to send ``417 + Expectation Failed`` as a response header and ``return False``. + + .. versionadded:: 3.2 + .. method:: send_error(code, message=None) Sends and logs a complete error reply to the client. The numeric *code* @@ -174,6 +185,15 @@ Writes a specific HTTP header to the output stream. *keyword* should specify the header keyword, with *value* specifying its value. + .. method:: send_response_only(code, message=None) + + Sends the reponse header only, used for the purposes when ``100 + Continue`` response is sent by the server to the client. If the *message* + is not specified, the HTTP message corresponding the response *code* is + sent. + + .. versionadded:: 3.2 + .. method:: end_headers() Sends a blank line, indicating the end of the HTTP headers in the Modified: python/branches/py3k/Lib/http/server.py ============================================================================== --- python/branches/py3k/Lib/http/server.py (original) +++ python/branches/py3k/Lib/http/server.py Thu Sep 30 08:09:18 2010 @@ -322,6 +322,30 @@ elif (conntype.lower() == 'keep-alive' and self.protocol_version >= "HTTP/1.1"): self.close_connection = 0 + # Examine the headers and look for an Expect directive + expect = self.headers.get('Expect', "") + if (expect.lower() == "100-continue" and + self.protocol_version >= "HTTP/1.1" and + self.request_version >= "HTTP/1.1"): + if not self.handle_expect_100(): + return False + return True + + def handle_expect_100(self): + """Decide what to do with an "Expect: 100-continue" header. + + If the client is expecting a 100 Continue response, we must + respond with either a 100 Continue or a final response before + waiting for the request body. The default is to always respond + with a 100 Continue. You can behave differently (for example, + reject unauthorized requests) by overriding this method. + + This method should either return True (possibly after sending + a 100 Continue response) or send an error response and return + False. + + """ + self.send_response_only(100) return True def handle_one_request(self): @@ -400,6 +424,12 @@ """ self.log_request(code) + self.send_response_only(code, message) + self.send_header('Server', self.version_string()) + self.send_header('Date', self.date_time_string()) + + def send_response_only(self, code, message=None): + """Send the response header only.""" if message is None: if code in self.responses: message = self.responses[code][0] @@ -408,9 +438,6 @@ if self.request_version != 'HTTP/0.9': self.wfile.write(("%s %d %s\r\n" % (self.protocol_version, code, message)).encode('ASCII', 'strict')) - # print (self.protocol_version, code, message) - self.send_header('Server', self.version_string()) - self.send_header('Date', self.date_time_string()) def send_header(self, keyword, value): """Send a MIME header.""" Modified: python/branches/py3k/Lib/test/test_httpservers.py ============================================================================== --- python/branches/py3k/Lib/test/test_httpservers.py (original) +++ python/branches/py3k/Lib/test/test_httpservers.py Thu Sep 30 08:09:18 2010 @@ -10,11 +10,13 @@ import os import sys +import re import base64 import shutil import urllib.parse import http.client import tempfile +from io import BytesIO import unittest from test import support @@ -403,8 +405,103 @@ class SocketlessRequestHandler(SimpleHTTPRequestHandler): def __init__(self): + self.get_called = False + self.protocol_version = "HTTP/1.1" + + def do_GET(self): + self.get_called = True + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(b'Data\r\n') + + def log_message(self, format, *args): pass +class RejectingSocketlessRequestHandler(SocketlessRequestHandler): + def handle_expect_100(self): + self.send_error(417) + return False + +class BaseHTTPRequestHandlerTestCase(unittest.TestCase): + """Test the functionaility of the BaseHTTPServer. + + Test the support for the Expect 100-continue header. + """ + + HTTPResponseMatch = re.compile(b'HTTP/1.[0-9]+ 200 OK') + + def setUp (self): + self.handler = SocketlessRequestHandler() + + def send_typical_request(self, message): + input = BytesIO(message) + output = BytesIO() + self.handler.rfile = input + self.handler.wfile = output + self.handler.handle_one_request() + output.seek(0) + return output.readlines() + + def verify_get_called(self): + self.assertTrue(self.handler.get_called) + + def verify_expected_headers(self, headers): + for fieldName in b'Server: ', b'Date: ', b'Content-Type: ': + self.assertEqual(sum(h.startswith(fieldName) for h in headers), 1) + + def verify_http_server_response(self, response): + match = self.HTTPResponseMatch.search(response) + self.assertTrue(match is not None) + + def test_http_1_1(self): + result = self.send_typical_request(b'GET / HTTP/1.1\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], b'Data\r\n') + + def test_http_1_0(self): + result = self.send_typical_request(b'GET / HTTP/1.0\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], b'Data\r\n') + + def test_http_0_9(self): + result = self.send_typical_request(b'GET / HTTP/0.9\r\n\r\n') + self.assertEqual(len(result), 1) + self.assertEqual(result[0], b'Data\r\n') + self.verify_get_called() + + def test_with_continue_1_0(self): + result = self.send_typical_request(b'GET / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], b'Data\r\n') + + def test_with_continue_1_1(self): + result = self.send_typical_request(b'GET / HTTP/1.1\r\nExpect: 100-continue\r\n\r\n') + self.assertEqual(result[0], b'HTTP/1.1 100 Continue\r\n') + self.assertEqual(result[1], b'HTTP/1.1 200 OK\r\n') + self.verify_expected_headers(result[2:-1]) + self.verify_get_called() + self.assertEqual(result[-1], b'Data\r\n') + + def test_with_continue_rejected(self): + usual_handler = self.handler # Save to avoid breaking any subsequent tests. + self.handler = RejectingSocketlessRequestHandler() + result = self.send_typical_request(b'GET / HTTP/1.1\r\nExpect: 100-continue\r\n\r\n') + self.assertEqual(result[0], b'HTTP/1.1 417 Expectation Failed\r\n') + self.verify_expected_headers(result[1:-1]) + # The expect handler should short circuit the usual get method by + # returning false here, so get_called should be false + self.assertFalse(self.handler.get_called) + self.assertEqual(sum(r == b'Connection: close\r\n' for r in result[1:-1]), 1) + self.handler = usual_handler # Restore to avoid breaking any subsequent tests. + + class SimpleHTTPRequestHandlerTestCase(unittest.TestCase): """ Test url parsing """ def setUp(self): @@ -431,6 +528,7 @@ cwd = os.getcwd() try: support.run_unittest( + BaseHTTPRequestHandlerTestCase, BaseHTTPServerTestCase, SimpleHTTPServerTestCase, CGIHTTPServerTestCase, Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Sep 30 08:09:18 2010 @@ -76,6 +76,9 @@ Library ------- +- Issue #1491: BaseHTTPServer nows send a 100 Continue response before sending + a 200 OK for the Expect: 100-continue request header. + - Issue #9360: Cleanup and improvements to the nntplib module. The API now conforms to the philosophy of bytes and unicode separation in Python 3. A test suite has also been added. From python-checkins at python.org Thu Sep 30 08:34:02 2010 From: python-checkins at python.org (senthil.kumaran) Date: Thu, 30 Sep 2010 08:34:02 +0200 (CEST) Subject: [Python-checkins] r85126 - python/branches/release31-maint/Lib/test/test_httpservers.py Message-ID: <20100930063402.7BFC9EEA07@mail.python.org> Author: senthil.kumaran Date: Thu Sep 30 08:34:02 2010 New Revision: 85126 Log: Add BaseHTTPRequestHandler related tests. Modified: python/branches/release31-maint/Lib/test/test_httpservers.py Modified: python/branches/release31-maint/Lib/test/test_httpservers.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_httpservers.py (original) +++ python/branches/release31-maint/Lib/test/test_httpservers.py Thu Sep 30 08:34:02 2010 @@ -10,6 +10,7 @@ import os import sys +import re import base64 import shutil import urllib.parse @@ -18,6 +19,8 @@ import threading import unittest + +from io import BytesIO from test import support class NoLogRequestHandler: @@ -29,6 +32,22 @@ return '' +class SocketlessRequestHandler(BaseHTTPRequestHandler): + def __init__(self): + self.get_called = False + self.protocol_version = "HTTP/1.1" + + def do_GET(self): + self.get_called = True + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(b'Data\r\n') + + def log_message(self, format, *args): + pass + + class TestServerThread(threading.Thread): def __init__(self, test_object, request_handler): threading.Thread.__init__(self) @@ -64,6 +83,61 @@ self.connection.request(method, uri, body, headers) return self.connection.getresponse() +class BaseHTTPRequestHandlerTestCase(unittest.TestCase): + """Test the functionaility of the BaseHTTPServer.""" + + HTTPResponseMatch = re.compile(b'HTTP/1.[0-9]+ 200 OK') + + def setUp (self): + self.handler = SocketlessRequestHandler() + + def send_typical_request(self, message): + input = BytesIO(message) + output = BytesIO() + self.handler.rfile = input + self.handler.wfile = output + self.handler.handle_one_request() + output.seek(0) + return output.readlines() + + def verify_get_called(self): + self.assertTrue(self.handler.get_called) + + def verify_expected_headers(self, headers): + for fieldName in b'Server: ', b'Date: ', b'Content-Type: ': + self.assertEqual(sum(h.startswith(fieldName) for h in headers), 1) + + def verify_http_server_response(self, response): + match = self.HTTPResponseMatch.search(response) + self.assertTrue(match is not None) + + def test_http_1_1(self): + result = self.send_typical_request(b'GET / HTTP/1.1\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], b'Data\r\n') + + def test_http_1_0(self): + result = self.send_typical_request(b'GET / HTTP/1.0\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], b'Data\r\n') + + def test_http_0_9(self): + result = self.send_typical_request(b'GET / HTTP/0.9\r\n\r\n') + self.assertEqual(len(result), 1) + self.assertEqual(result[0], b'Data\r\n') + self.verify_get_called() + + def test_with_continue_1_0(self): + result = self.send_typical_request(b'GET / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], b'Data\r\n') + class BaseHTTPServerTestCase(BaseTestCase): class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler): @@ -400,7 +474,8 @@ def test_main(verbose=None): try: cwd = os.getcwd() - support.run_unittest(BaseHTTPServerTestCase, + support.run_unittest(BaseHTTPRequestHandlerTestCase, + BaseHTTPServerTestCase, SimpleHTTPServerTestCase, CGIHTTPServerTestCase ) From python-checkins at python.org Thu Sep 30 08:40:56 2010 From: python-checkins at python.org (senthil.kumaran) Date: Thu, 30 Sep 2010 08:40:56 +0200 (CEST) Subject: [Python-checkins] r85127 - python/branches/release27-maint/Lib/test/test_httpservers.py Message-ID: <20100930064056.56DD9EEA19@mail.python.org> Author: senthil.kumaran Date: Thu Sep 30 08:40:56 2010 New Revision: 85127 Log: Added BaseHTTPRequestHandler related tests. Modified: python/branches/release27-maint/Lib/test/test_httpservers.py Modified: python/branches/release27-maint/Lib/test/test_httpservers.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_httpservers.py (original) +++ python/branches/release27-maint/Lib/test/test_httpservers.py Thu Sep 30 08:40:56 2010 @@ -11,6 +11,7 @@ import os import sys +import re import base64 import shutil import urllib @@ -18,6 +19,9 @@ import tempfile import unittest + +from StringIO import StringIO + from test import test_support threading = test_support.import_module('threading') @@ -27,6 +31,22 @@ # don't write log messages to stderr pass +class SocketlessRequestHandler(BaseHTTPRequestHandler): + def __init__(self): + self.get_called = False + self.protocol_version = "HTTP/1.1" + + def do_GET(self): + self.get_called = True + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write('Data\r\n') + + def log_message(self, format, *args): + pass + + class TestServerThread(threading.Thread): def __init__(self, test_object, request_handler): @@ -67,6 +87,63 @@ self.connection.request(method, uri, body, headers) return self.connection.getresponse() +class BaseHTTPRequestHandlerTestCase(unittest.TestCase): + """Test the functionaility of the BaseHTTPServer focussing on + BaseHTTPRequestHandler. + """ + + HTTPResponseMatch = re.compile('HTTP/1.[0-9]+ 200 OK') + + def setUp (self): + self.handler = SocketlessRequestHandler() + + def send_typical_request(self, message): + input = StringIO(message) + output = StringIO() + self.handler.rfile = input + self.handler.wfile = output + self.handler.handle_one_request() + output.seek(0) + return output.readlines() + + def verify_get_called(self): + self.assertTrue(self.handler.get_called) + + def verify_expected_headers(self, headers): + for fieldName in 'Server: ', 'Date: ', 'Content-Type: ': + self.assertEqual(sum(h.startswith(fieldName) for h in headers), 1) + + def verify_http_server_response(self, response): + match = self.HTTPResponseMatch.search(response) + self.assertTrue(match is not None) + + def test_http_1_1(self): + result = self.send_typical_request('GET / HTTP/1.1\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], 'Data\r\n') + + def test_http_1_0(self): + result = self.send_typical_request('GET / HTTP/1.0\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], 'Data\r\n') + + def test_http_0_9(self): + result = self.send_typical_request('GET / HTTP/0.9\r\n\r\n') + self.assertEqual(len(result), 1) + self.assertEqual(result[0], 'Data\r\n') + self.verify_get_called() + + def test_with_continue_1_0(self): + result = self.send_typical_request('GET / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.verify_get_called() + self.assertEqual(result[-1], 'Data\r\n') + class BaseHTTPServerTestCase(BaseTestCase): class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler): @@ -402,10 +479,11 @@ def test_main(verbose=None): try: cwd = os.getcwd() - test_support.run_unittest(BaseHTTPServerTestCase, - SimpleHTTPServerTestCase, - CGIHTTPServerTestCase - ) + test_support.run_unittest(BaseHTTPRequestHandlerTestCase, + BaseHTTPServerTestCase, + SimpleHTTPServerTestCase, + CGIHTTPServerTestCase + ) finally: os.chdir(cwd) From python-checkins at python.org Thu Sep 30 16:34:03 2010 From: python-checkins at python.org (martin.v.loewis) Date: Thu, 30 Sep 2010 16:34:03 +0200 (CEST) Subject: [Python-checkins] r85128 - in tracker/instances/python-dev: lib/rietveld lib/rietveld/Makefile lib/rietveld/README lib/rietveld/__init__.py lib/rietveld/manage.py lib/rietveld/patches lib/rietveld/rietveld_helper lib/rietveld/settings.py Message-ID: <20100930143403.DA88AEEA16@mail.python.org> Author: martin.v.loewis Date: Thu Sep 30 16:34:03 2010 New Revision: 85128 Log: Import rietveld. Added: tracker/instances/python-dev/lib/rietveld/ tracker/instances/python-dev/lib/rietveld/Makefile (contents, props changed) tracker/instances/python-dev/lib/rietveld/README (contents, props changed) tracker/instances/python-dev/lib/rietveld/__init__.py (contents, props changed) tracker/instances/python-dev/lib/rietveld/manage.py (contents, props changed) tracker/instances/python-dev/lib/rietveld/patches (contents, props changed) tracker/instances/python-dev/lib/rietveld/rietveld_helper (contents, props changed) tracker/instances/python-dev/lib/rietveld/settings.py (contents, props changed) Modified: tracker/instances/python-dev/ (props changed) Added: tracker/instances/python-dev/lib/rietveld/Makefile ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/Makefile Thu Sep 30 16:34:03 2010 @@ -0,0 +1,40 @@ +RIETVELDREV=510 + +default: + @echo "Run 'make all' to fetch required sources to run this example." + +all: static templates codereview django gae2django dev.db + @echo "Run './manage.py runserver 127.0.0.1:8000' to run Rietveld." + +clean: clean_local clean_external + +clean_external: clean_rietveld clean_django + +clean_rietveld: + rm -rf codereview static templates + +clean_django: + unlink django + +clean_local: + unlink gae2django + rm -f dev.db + +gae2django: + ln -s ../../gae2django . + +dev.db: + ./manage.py syncdb + +codereview: + svn co http://rietveld.googlecode.com/svn/trunk/codereview@$(RIETVELDREV) + +static: + svn co http://rietveld.googlecode.com/svn/trunk/static@$(RIETVELDREV) + patch -p0 < patches/upload.diff + +templates: + svn co http://rietveld.googlecode.com/svn/trunk/templates@$(RIETVELDREV) + +django: + ln -s ../../django . \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/README ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/README Thu Sep 30 16:34:03 2010 @@ -0,0 +1 @@ +This is a copy (with modifications) from gae2django/examples/rietveld. Added: tracker/instances/python-dev/lib/rietveld/__init__.py ============================================================================== Added: tracker/instances/python-dev/lib/rietveld/manage.py ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/manage.py Thu Sep 30 16:34:03 2010 @@ -0,0 +1,16 @@ +#!/usr/bin/env python +import gae2django +# Use gae2django.install(server_software='Dev') to enable a link to the +# admin frontend at the top of each page. By default this link is hidden. +gae2django.install(server_software='Django') + +from django.core.management import execute_manager +try: + import settings # Assumed to be in the same directory. +except ImportError: + import sys + sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) + sys.exit(1) + +if __name__ == "__main__": + execute_manager(settings) Added: tracker/instances/python-dev/lib/rietveld/patches ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/patches Thu Sep 30 16:34:03 2010 @@ -0,0 +1 @@ +link ../../gae2django/examples/rietveld/patches \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/rietveld_helper ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/rietveld_helper Thu Sep 30 16:34:03 2010 @@ -0,0 +1 @@ +link ../../gae2django/examples/rietveld/rietveld_helper \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/settings.py ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/settings.py Thu Sep 30 16:34:03 2010 @@ -0,0 +1,98 @@ +# Django settings for django_gae2django project. + +# NOTE: Keep the settings.py in examples directories in sync with this one! + +import os + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + # ('Your Name', 'your_email at domain.com'), +) + +MANAGERS = ADMINS + +DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. +DATABASE_NAME = 'dev.db' # Or path to database file if using sqlite3. +DATABASE_USER = '' # Not used with sqlite3. +DATABASE_PASSWORD = '' # Not used with sqlite3. +DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. +DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'America/Chicago' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# Absolute path to the directory that holds media. +# Example: "/home/media/media.lawrence.com/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash if there is a path component (optional in other cases). +# Examples: "http://media.lawrence.com", "http://example.com/media/" +MEDIA_URL = '/static/' + +# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a +# trailing slash. +# Examples: "http://foo.com/media/", "/media/". +ADMIN_MEDIA_PREFIX = '/media/' + +# Make this unique, and don't share it with anybody. +SECRET_KEY = 'el at 4s$*(idwm5-87teftxlksckmy8$tyo7(tm!n-5x)zeuheex' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.load_template_source', + 'django.template.loaders.app_directories.load_template_source', +# 'django.template.loaders.eggs.load_template_source', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'gae2django.middleware.FixRequestUserMiddleware', + 'rietveld_helper.middleware.AddUserToRequestMiddleware', + 'django.middleware.doc.XViewMiddleware', +) + +TEMPLATE_CONTEXT_PROCESSORS = ( + 'django.core.context_processors.request', +) + +ROOT_URLCONF = 'rietveld_helper.urls' + +TEMPLATE_DIRS = ( + os.path.join(os.path.dirname(__file__), 'templates'), +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.admin', + 'gae2django', + 'rietveld_helper', + 'codereview', +) + +AUTH_PROFILE_MODULE = 'codereview.Account' +LOGIN_REDIRECT_URL = '/' + +# This won't work with gae2django. +RIETVELD_INCOMING_MAIL_ADDRESS = None From python-checkins at python.org Thu Sep 30 23:07:31 2010 From: python-checkins at python.org (martin.v.loewis) Date: Thu, 30 Sep 2010 23:07:31 +0200 (CEST) Subject: [Python-checkins] r85129 - in tracker/instances/python-dev/lib/rietveld: Makefile roundup_helper roundup_helper/__init__.py roundup_helper/middleware.py roundup_helper/models.py roundup_helper/urls.py settings.py templates templates/404.html templates/500.html templates/all.html templates/base.html templates/branch_edit.html templates/branch_new.html templates/diff.html templates/diff2.html templates/diff_navigation.html templates/draft_message.html templates/edit.html templates/feeds templates/file_navigation.html templates/inline_comment.html templates/issue.html templates/issue_base.html templates/issue_heading.html templates/issue_row.html templates/issue_star.html templates/live_revision.html templates/mails templates/new.html templates/patch.html templates/patchset.html templates/publish.html templates/repo_new.html templates/repos.html templates/repos_base.html templates/settings.html templates/starred.html templates/use_uploadpy.html templates/user.html templates/user_popup.html templates/view_details_select.html Message-ID: <20100930210731.72222EEA56@mail.python.org> Author: martin.v.loewis Date: Thu Sep 30 23:07:30 2010 New Revision: 85129 Log: Integrate with roundup user management; adjust UI. Added: tracker/instances/python-dev/lib/rietveld/roundup_helper/ tracker/instances/python-dev/lib/rietveld/roundup_helper/__init__.py (contents, props changed) tracker/instances/python-dev/lib/rietveld/roundup_helper/middleware.py (contents, props changed) tracker/instances/python-dev/lib/rietveld/roundup_helper/models.py (contents, props changed) tracker/instances/python-dev/lib/rietveld/roundup_helper/urls.py (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/ tracker/instances/python-dev/lib/rietveld/templates/404.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/500.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/all.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/base.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/branch_edit.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/branch_new.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/diff.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/diff2.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/diff_navigation.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/draft_message.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/edit.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/feeds (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/file_navigation.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/inline_comment.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/issue.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/issue_base.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/issue_heading.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/issue_row.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/issue_star.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/live_revision.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/mails (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/new.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/patch.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/patchset.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/publish.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/repo_new.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/repos.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/repos_base.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/settings.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/starred.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/use_uploadpy.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/user.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/user_popup.html (contents, props changed) tracker/instances/python-dev/lib/rietveld/templates/view_details_select.html (contents, props changed) Modified: tracker/instances/python-dev/lib/rietveld/Makefile tracker/instances/python-dev/lib/rietveld/settings.py Modified: tracker/instances/python-dev/lib/rietveld/Makefile ============================================================================== --- tracker/instances/python-dev/lib/rietveld/Makefile (original) +++ tracker/instances/python-dev/lib/rietveld/Makefile Thu Sep 30 23:07:30 2010 @@ -11,7 +11,7 @@ clean_external: clean_rietveld clean_django clean_rietveld: - rm -rf codereview static templates + rm -rf codereview static templates_svn clean_django: unlink django @@ -21,9 +21,9 @@ rm -f dev.db gae2django: - ln -s ../../gae2django . + ln -s ../../gae2django/gae2django . -dev.db: +syncdb: ./manage.py syncdb codereview: @@ -33,8 +33,8 @@ svn co http://rietveld.googlecode.com/svn/trunk/static@$(RIETVELDREV) patch -p0 < patches/upload.diff -templates: - svn co http://rietveld.googlecode.com/svn/trunk/templates@$(RIETVELDREV) +templates_svn: + svn co http://rietveld.googlecode.com/svn/trunk/templates@$(RIETVELDREV) templates_svn django: - ln -s ../../django . \ No newline at end of file + ln -s ../../gae2django/django . Added: tracker/instances/python-dev/lib/rietveld/roundup_helper/__init__.py ============================================================================== Added: tracker/instances/python-dev/lib/rietveld/roundup_helper/middleware.py ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/roundup_helper/middleware.py Thu Sep 30 23:07:30 2010 @@ -0,0 +1,48 @@ +from models import Session, User +from django.contrib import auth +from django.contrib.auth.backends import RemoteUserBackend + +class UserBackend(RemoteUserBackend): + def configure_user(self, user): + roundup_user = User.objects.filter(_username=user.username)[0] + user.email = roundup_user._address + user.save() + from codereview import models + account = models.Account.get_account_for_user(user) + account.nickname = user.username + account.save() + return user + +class LookupRoundupUser(object): + + def process_request(self, request): + session_key = request.COOKIES.get('roundup_session_Tracker', None) + if not session_key: + self.logout(request) + return + session = Session.objects.filter(session_key = session_key) + if not session: + self.logout(request) + return + username = eval(session[0].session_value)['user'] + # the username comes from the cookie, so it really ought to exist + roundup_user = User.objects.filter(_username=username)[0] + if not roundup_user._address: + # Rietveld insists that user objects must have email addresses + return + # Taken from RemoteUserMiddleware: auto-create the user if it's new + if request.user.is_authenticated(): + if request.user.username == username: + return + # We are seeing this user for the first time in this session, attempt + # to authenticate the user. + user = auth.authenticate(remote_user=username) + if user: + # User is valid. Set request.user and persist user in the session + # by logging the user in. + request.user = user + auth.login(request, user) + + def logout(self, request): + # Clear django session if roundup session is gone. + auth.logout(request) Added: tracker/instances/python-dev/lib/rietveld/roundup_helper/models.py ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/roundup_helper/models.py Thu Sep 30 23:07:30 2010 @@ -0,0 +1,15 @@ +# Django mappings for roundup tables +from django.db import models + +class Session(models.Model): + session_key = models.CharField(primary_key=True, max_length=255) + session_value = models.TextField() + class Meta: + db_table = 'sessions' + +class User(models.Model): + _username = models.CharField() + _realname = models.CharField() + _address = models.CharField() + class Meta: + db_table = '_user' Added: tracker/instances/python-dev/lib/rietveld/roundup_helper/urls.py ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/roundup_helper/urls.py Thu Sep 30 23:07:30 2010 @@ -0,0 +1,7 @@ +from django.conf.urls.defaults import * +from django.contrib import admin + +urlpatterns = patterns('', + ('python-dev/', 'roundup'), + ('review/', include('rietveld_helper.urls')), + ) Modified: tracker/instances/python-dev/lib/rietveld/settings.py ============================================================================== --- tracker/instances/python-dev/lib/rietveld/settings.py (original) +++ tracker/instances/python-dev/lib/rietveld/settings.py Thu Sep 30 23:07:30 2010 @@ -2,7 +2,7 @@ # NOTE: Keep the settings.py in examples directories in sync with this one! -import os +import os, ConfigParser DEBUG = True TEMPLATE_DEBUG = DEBUG @@ -13,19 +13,22 @@ MANAGERS = ADMINS -DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. -DATABASE_NAME = 'dev.db' # Or path to database file if using sqlite3. -DATABASE_USER = '' # Not used with sqlite3. -DATABASE_PASSWORD = '' # Not used with sqlite3. -DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. -DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. +_c = ConfigParser.ConfigParser({'password':'', 'port':''}) +_c.read(os.path.dirname(__file__)+"/../../config.ini") + +DATABASE_ENGINE = 'postgresql_psycopg2' +DATABASE_NAME = _c.get('rdbms', 'name') +DATABASE_USER = _c.get('rdbms', 'user') +DATABASE_PASSWORD = _c.get('rdbms', 'password') +DATABASE_HOST = _c.get('rdbms', 'host') +DATABASE_PORT = _c.get('rdbms', 'port') # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. # If running in a Windows environment this must be set to the same as your # system time zone. -TIME_ZONE = 'America/Chicago' +TIME_ZONE = 'Europe/Amsterdam' # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html @@ -44,7 +47,7 @@ # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash if there is a path component (optional in other cases). # Examples: "http://media.lawrence.com", "http://example.com/media/" -MEDIA_URL = '/static/' +MEDIA_URL = '/review/static/' # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a # trailing slash. @@ -52,7 +55,7 @@ ADMIN_MEDIA_PREFIX = '/media/' # Make this unique, and don't share it with anybody. -SECRET_KEY = 'el at 4s$*(idwm5-87teftxlksckmy8$tyo7(tm!n-5x)zeuheex' +SECRET_KEY = _c.get('django', 'secret_key') # List of callables that know how to import templates from various sources. TEMPLATE_LOADERS = ( @@ -61,10 +64,12 @@ # 'django.template.loaders.eggs.load_template_source', ) +AUTHENTICATION_BACKENDS = ('roundup_helper.middleware.UserBackend',) MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'roundup_helper.middleware.LookupRoundupUser', 'gae2django.middleware.FixRequestUserMiddleware', 'rietveld_helper.middleware.AddUserToRequestMiddleware', 'django.middleware.doc.XViewMiddleware', @@ -74,7 +79,7 @@ 'django.core.context_processors.request', ) -ROOT_URLCONF = 'rietveld_helper.urls' +ROOT_URLCONF = 'roundup_helper.urls' TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), 'templates'), Added: tracker/instances/python-dev/lib/rietveld/templates/404.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/404.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/404.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/500.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/500.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/500.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/all.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/all.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/all.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/base.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/base.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1,250 @@ + + + + + {%if is_dev%} + (DEV) + {%endif%} + {%block title1%} + {%if patch%}{{patch.filename}} -{%endif%} + {%endblock%} + {%block title2%} + {%if issue%}Issue {{issue.key.id}}: {{issue.subject}} -{%endif%} + {%endblock%} + Code Review + + + + + + {%if user%} + + + + {%endif%} + {%if issue%} + + {%endif%} + + + + + +
- From python-checkins at python.org Wed Sep 1 23:14:16 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 23:14:16 +0200 (CEST) Subject: [Python-checkins] r84408 - in python/branches/py3k: Lib/test/test_memoryview.py Misc/NEWS Objects/memoryobject.c Message-ID: <20100901211416.BAC7CEE981@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 23:14:16 2010 New Revision: 84408 Log: Issue #9737: Fix a crash when trying to delete a slice or an item from a memoryview object. Modified: python/branches/py3k/Lib/test/test_memoryview.py python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/memoryobject.c Modified: python/branches/py3k/Lib/test/test_memoryview.py ============================================================================== --- python/branches/py3k/Lib/test/test_memoryview.py (original) +++ python/branches/py3k/Lib/test/test_memoryview.py Wed Sep 1 23:14:16 2010 @@ -111,6 +111,15 @@ m = None self.assertEquals(sys.getrefcount(b), oldrefcount) + def test_delitem(self): + for tp in self._types: + b = tp(self._source) + m = self._view(b) + with self.assertRaises(TypeError): + del m[1] + with self.assertRaises(TypeError): + del m[1:4] + def test_tobytes(self): for tp in self._types: m = self._view(tp(self._source)) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 1 23:14:16 2010 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #9737: Fix a crash when trying to delete a slice or an item from + a memoryview object. + - Issue #9549: sys.setdefaultencoding() and PyUnicode_SetDefaultEncoding() are now removed, since their effect was inexistent in 3.x (the default encoding is hardcoded to utf-8 and cannot be changed). Modified: python/branches/py3k/Objects/memoryobject.c ============================================================================== --- python/branches/py3k/Objects/memoryobject.c (original) +++ python/branches/py3k/Objects/memoryobject.c Wed Sep 1 23:14:16 2010 @@ -631,6 +631,11 @@ "cannot modify read-only memory"); return -1; } + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "cannot delete memory"); + return -1; + } if (view->ndim != 1) { PyErr_SetNone(PyExc_NotImplementedError); return -1; From python-checkins at python.org Wed Sep 1 23:14:46 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 23:14:46 +0200 (CEST) Subject: [Python-checkins] r84409 - python/branches/py3k/Objects/memoryobject.c Message-ID: <20100901211446.91BABF6D2@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 23:14:46 2010 New Revision: 84409 Log: Fix a compilation warning Modified: python/branches/py3k/Objects/memoryobject.c Modified: python/branches/py3k/Objects/memoryobject.c ============================================================================== --- python/branches/py3k/Objects/memoryobject.c (original) +++ python/branches/py3k/Objects/memoryobject.c Wed Sep 1 23:14:46 2010 @@ -179,7 +179,7 @@ int k; Py_ssize_t elements; char *ptr; - void (*func)(int, Py_ssize_t *, Py_ssize_t *); + void (*func)(int, Py_ssize_t *, const Py_ssize_t *); if (view->ndim > PY_SSIZE_T_MAX / sizeof(Py_ssize_t)) { PyErr_NoMemory(); From python-checkins at python.org Wed Sep 1 23:16:10 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 23:16:10 +0200 (CEST) Subject: [Python-checkins] r84410 - in python/branches/release31-maint: Lib/test/test_memoryview.py Misc/NEWS Objects/memoryobject.c Message-ID: <20100901211610.CA504EE981@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 23:16:10 2010 New Revision: 84410 Log: Merged revisions 84408-84409 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84408 | antoine.pitrou | 2010-09-01 23:14:16 +0200 (mer., 01 sept. 2010) | 4 lines Issue #9737: Fix a crash when trying to delete a slice or an item from a memoryview object. ........ r84409 | antoine.pitrou | 2010-09-01 23:14:46 +0200 (mer., 01 sept. 2010) | 3 lines Fix a compilation warning ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_memoryview.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Objects/memoryobject.c Modified: python/branches/release31-maint/Lib/test/test_memoryview.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_memoryview.py (original) +++ python/branches/release31-maint/Lib/test/test_memoryview.py Wed Sep 1 23:16:10 2010 @@ -111,6 +111,15 @@ m = None self.assertEquals(sys.getrefcount(b), oldrefcount) + def test_delitem(self): + for tp in self._types: + b = tp(self._source) + m = self._view(b) + with self.assertRaises(TypeError): + del m[1] + with self.assertRaises(TypeError): + del m[1:4] + def test_tobytes(self): for tp in self._types: m = self._view(tp(self._source)) Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Wed Sep 1 23:16:10 2010 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #9737: Fix a crash when trying to delete a slice or an item from + a memoryview object. + - Issue #7415: PyUnicode_FromEncodedObject() now uses the new buffer API properly. Patch by Stefan Behnel. Modified: python/branches/release31-maint/Objects/memoryobject.c ============================================================================== --- python/branches/release31-maint/Objects/memoryobject.c (original) +++ python/branches/release31-maint/Objects/memoryobject.c Wed Sep 1 23:16:10 2010 @@ -179,7 +179,7 @@ int k; Py_ssize_t elements; char *ptr; - void (*func)(int, Py_ssize_t *, Py_ssize_t *); + void (*func)(int, Py_ssize_t *, const Py_ssize_t *); if (view->ndim > PY_SSIZE_T_MAX / sizeof(Py_ssize_t)) { PyErr_NoMemory(); @@ -631,6 +631,11 @@ "cannot modify read-only memory"); return -1; } + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "cannot delete memory"); + return -1; + } if (view->ndim != 1) { PyErr_SetNone(PyExc_NotImplementedError); return -1; From python-checkins at python.org Wed Sep 1 23:17:35 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 1 Sep 2010 23:17:35 +0200 (CEST) Subject: [Python-checkins] r84411 - in python/branches/release27-maint: Lib/test/test_memoryview.py Misc/NEWS Objects/memoryobject.c Message-ID: <20100901211735.1D7FEEE981@mail.python.org> Author: antoine.pitrou Date: Wed Sep 1 23:17:34 2010 New Revision: 84411 Log: Merged revisions 84408-84409 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84408 | antoine.pitrou | 2010-09-01 23:14:16 +0200 (mer., 01 sept. 2010) | 4 lines Issue #9737: Fix a crash when trying to delete a slice or an item from a memoryview object. ........ r84409 | antoine.pitrou | 2010-09-01 23:14:46 +0200 (mer., 01 sept. 2010) | 3 lines Fix a compilation warning ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_memoryview.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Objects/memoryobject.c Modified: python/branches/release27-maint/Lib/test/test_memoryview.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_memoryview.py (original) +++ python/branches/release27-maint/Lib/test/test_memoryview.py Wed Sep 1 23:17:34 2010 @@ -117,6 +117,15 @@ m = None self.assertEquals(sys.getrefcount(b), oldrefcount) + def test_delitem(self): + for tp in self._types: + b = tp(self._source) + m = self._view(b) + with self.assertRaises(TypeError): + del m[1] + with self.assertRaises(TypeError): + del m[1:4] + def test_tobytes(self): for tp in self._types: m = self._view(tp(self._source)) Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Wed Sep 1 23:17:34 2010 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #9737: Fix a crash when trying to delete a slice or an item from + a memoryview object. + - Restore GIL in nis_cat in case of error. - Issue #9688: __basicsize__ and __itemsize__ must be accessed as Py_ssize_t. Modified: python/branches/release27-maint/Objects/memoryobject.c ============================================================================== --- python/branches/release27-maint/Objects/memoryobject.c (original) +++ python/branches/release27-maint/Objects/memoryobject.c Wed Sep 1 23:17:34 2010 @@ -179,7 +179,7 @@ int k; Py_ssize_t elements; char *ptr; - void (*func)(int, Py_ssize_t *, Py_ssize_t *); + void (*func)(int, Py_ssize_t *, const Py_ssize_t *); if (view->ndim > PY_SSIZE_T_MAX / sizeof(Py_ssize_t)) { PyErr_NoMemory(); @@ -639,6 +639,11 @@ "cannot modify read-only memory"); return -1; } + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "cannot delete memory"); + return -1; + } if (view->ndim != 1) { PyErr_SetNone(PyExc_NotImplementedError); return -1; From python-checkins at python.org Wed Sep 1 23:20:07 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 23:20:07 +0200 (CEST) Subject: [Python-checkins] r84412 - python/branches/release27-maint/Doc/library/heapq.rst Message-ID: <20100901212007.33A9FEEA06@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 23:20:07 2010 New Revision: 84412 Log: Cleanup heapq docs Modified: python/branches/release27-maint/Doc/library/heapq.rst Modified: python/branches/release27-maint/Doc/library/heapq.rst ============================================================================== --- python/branches/release27-maint/Doc/library/heapq.rst (original) +++ python/branches/release27-maint/Doc/library/heapq.rst Wed Sep 1 23:20:07 2010 @@ -63,45 +63,16 @@ Pop and return the smallest item from the *heap*, and also push the new *item*. The heap size doesn't change. If the heap is empty, :exc:`IndexError` is raised. - This is more efficient than :func:`heappop` followed by :func:`heappush`, and - can be more appropriate when using a fixed-size heap. Note that the value - returned may be larger than *item*! That constrains reasonable uses of this - routine unless written as part of a conditional replacement:: - - if item > heap[0]: - item = heapreplace(heap, item) - -Example of use: - - >>> from heapq import heappush, heappop - >>> heap = [] - >>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] - >>> for item in data: - ... heappush(heap, item) - ... - >>> ordered = [] - >>> while heap: - ... ordered.append(heappop(heap)) - ... - >>> print ordered - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> data.sort() - >>> print data == ordered - True - -Using a heap to insert items at the correct place in a priority queue: - - >>> heap = [] - >>> data = [(1, 'J'), (4, 'N'), (3, 'H'), (2, 'O')] - >>> for item in data: - ... heappush(heap, item) - ... - >>> while heap: - ... print heappop(heap)[1] - J - O - H - N + + This one step operation is more efficient than a :func:`heappop` followed by + :func:`heappush` and can be more appropriate when using a fixed-size heap. + The pop/push combination always returns an element from the heap and replaces + it with *item*. + + The value returned may be larger than the *item* added. If that isn't + desired, consider using :func:`heappushpop` instead. Its push/pop + combination returns the smaller of the two values, leaving the larger value + on the heap. The module also offers three general purpose functions based on heaps. @@ -152,6 +123,35 @@ functions. +Basic Examples +-------------- + +A `heapsort `_ can be implemented by +pushing all values onto a heap and then popping off the smallest values one at a +time:: + + >>> def heapsort(iterable): + ... 'Equivalent to sorted(iterable)' + ... h = [] + ... for value in iterable: + ... heappush(h, value) + ... return [heappop(h) for i in range(len(h))] + ... + >>> heapsort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0]) + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + +Heap elements can be tuples. This is useful for assigning comparison values +(such as task priorities) alongside the main record being tracked:: + + >>> h = [] + >>> heappush(h, (5, 'write code')) + >>> heappush(h, (7, 'release product')) + >>> heappush(h, (1, 'write spec')) + >>> heappush(h, (3, 'create tests')) + >>> heappop(h) + (1, 'write spec') + + Priority Queue Implementation Notes ----------------------------------- From python-checkins at python.org Wed Sep 1 23:26:16 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 23:26:16 +0200 (CEST) Subject: [Python-checkins] r84413 - python/branches/release31-maint/Doc/library/heapq.rst Message-ID: <20100901212616.3F1CFEE981@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 23:26:16 2010 New Revision: 84413 Log: Cleanup heapq docs Modified: python/branches/release31-maint/Doc/library/heapq.rst Modified: python/branches/release31-maint/Doc/library/heapq.rst ============================================================================== --- python/branches/release31-maint/Doc/library/heapq.rst (original) +++ python/branches/release31-maint/Doc/library/heapq.rst Wed Sep 1 23:26:16 2010 @@ -61,45 +61,16 @@ Pop and return the smallest item from the *heap*, and also push the new *item*. The heap size doesn't change. If the heap is empty, :exc:`IndexError` is raised. - This is more efficient than :func:`heappop` followed by :func:`heappush`, and - can be more appropriate when using a fixed-size heap. Note that the value - returned may be larger than *item*! That constrains reasonable uses of this - routine unless written as part of a conditional replacement:: - - if item > heap[0]: - item = heapreplace(heap, item) - -Example of use: - - >>> from heapq import heappush, heappop - >>> heap = [] - >>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] - >>> for item in data: - ... heappush(heap, item) - ... - >>> ordered = [] - >>> while heap: - ... ordered.append(heappop(heap)) - ... - >>> ordered - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> data.sort() - >>> data == ordered - True - -Using a heap to insert items at the correct place in a priority queue: - - >>> heap = [] - >>> data = [(1, 'J'), (4, 'N'), (3, 'H'), (2, 'O')] - >>> for item in data: - ... heappush(heap, item) - ... - >>> while heap: - ... print(heappop(heap)[1]) - J - O - H - N + + This one step operation is more efficient than a :func:`heappop` followed by + :func:`heappush` and can be more appropriate when using a fixed-size heap. + The pop/push combination always returns an element from the heap and replaces + it with *item*. + + The value returned may be larger than the *item* added. If that isn't + desired, consider using :func:`heappushpop` instead. Its push/pop + combination returns the smaller of the two values, leaving the larger value + on the heap. The module also offers three general purpose functions based on heaps. @@ -139,6 +110,35 @@ functions. +Basic Examples +-------------- + +A `heapsort `_ can be implemented by +pushing all values onto a heap and then popping off the smallest values one at a +time:: + + >>> def heapsort(iterable): + ... 'Equivalent to sorted(iterable)' + ... h = [] + ... for value in iterable: + ... heappush(h, value) + ... return [heappop(h) for i in range(len(h))] + ... + >>> heapsort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0]) + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + +Heap elements can be tuples. This is useful for assigning comparison values +(such as task priorities) alongside the main record being tracked:: + + >>> h = [] + >>> heappush(h, (5, 'write code')) + >>> heappush(h, (7, 'release product')) + >>> heappush(h, (1, 'write spec')) + >>> heappush(h, (3, 'create tests')) + >>> heappop(h) + (1, 'write spec') + + Priority Queue Implementation Notes ----------------------------------- From python-checkins at python.org Wed Sep 1 23:27:32 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 1 Sep 2010 23:27:32 +0200 (CEST) Subject: [Python-checkins] r84414 - python/branches/py3k/Doc/library/heapq.rst Message-ID: <20100901212732.2312BEE9B1@mail.python.org> Author: raymond.hettinger Date: Wed Sep 1 23:27:31 2010 New Revision: 84414 Log: Cleanup heapq docs Modified: python/branches/py3k/Doc/library/heapq.rst Modified: python/branches/py3k/Doc/library/heapq.rst ============================================================================== --- python/branches/py3k/Doc/library/heapq.rst (original) +++ python/branches/py3k/Doc/library/heapq.rst Wed Sep 1 23:27:31 2010 @@ -61,45 +61,16 @@ Pop and return the smallest item from the *heap*, and also push the new *item*. The heap size doesn't change. If the heap is empty, :exc:`IndexError` is raised. - This is more efficient than :func:`heappop` followed by :func:`heappush`, and - can be more appropriate when using a fixed-size heap. Note that the value - returned may be larger than *item*! That constrains reasonable uses of this - routine unless written as part of a conditional replacement:: - - if item > heap[0]: - item = heapreplace(heap, item) - -Example of use: - - >>> from heapq import heappush, heappop - >>> heap = [] - >>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] - >>> for item in data: - ... heappush(heap, item) - ... - >>> ordered = [] - >>> while heap: - ... ordered.append(heappop(heap)) - ... - >>> ordered - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> data.sort() - >>> data == ordered - True - -Using a heap to insert items at the correct place in a priority queue: - - >>> heap = [] - >>> data = [(1, 'J'), (4, 'N'), (3, 'H'), (2, 'O')] - >>> for item in data: - ... heappush(heap, item) - ... - >>> while heap: - ... print(heappop(heap)[1]) - J - O - H - N + + This one step operation is more efficient than a :func:`heappop` followed by + :func:`heappush` and can be more appropriate when using a fixed-size heap. + The pop/push combination always returns an element from the heap and replaces + it with *item*. + + The value returned may be larger than the *item* added. If that isn't + desired, consider using :func:`heappushpop` instead. Its push/pop + combination returns the smaller of the two values, leaving the larger value + on the heap. The module also offers three general purpose functions based on heaps. @@ -139,6 +110,35 @@ functions. +Basic Examples +-------------- + +A `heapsort `_ can be implemented by +pushing all values onto a heap and then popping off the smallest values one at a +time:: + + >>> def heapsort(iterable): + ... 'Equivalent to sorted(iterable)' + ... h = [] + ... for value in iterable: + ... heappush(h, value) + ... return [heappop(h) for i in range(len(h))] + ... + >>> heapsort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0]) + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + +Heap elements can be tuples. This is useful for assigning comparison values +(such as task priorities) alongside the main record being tracked:: + + >>> h = [] + >>> heappush(h, (5, 'write code')) + >>> heappush(h, (7, 'release product')) + >>> heappush(h, (1, 'write spec')) + >>> heappush(h, (3, 'create tests')) + >>> heappop(h) + (1, 'write spec') + + Priority Queue Implementation Notes ----------------------------------- From ncoghlan at gmail.com Wed Sep 1 23:42:34 2010 From: ncoghlan at gmail.com (Nick Coghlan) Date: Thu, 2 Sep 2010 07:42:34 +1000 Subject: [Python-checkins] r84391 - in python/branches/py3k: Include/abstract.h Misc/NEWS Objects/abstract.c Objects/memoryobject.c In-Reply-To: <20100901125825.55BB5EE98A@mail.python.org> References: <20100901125825.55BB5EE98A@mail.python.org> Message-ID: On Wed, Sep 1, 2010 at 10:58 PM, antoine.pitrou wrote: > Modified: python/branches/py3k/Misc/NEWS > ============================================================================== > --- python/branches/py3k/Misc/NEWS ? ? ?(original) > +++ python/branches/py3k/Misc/NEWS ? ? ?Wed Sep ?1 14:58:21 2010 > @@ -354,6 +354,9 @@ > ?Build > ?----- > > +- Issue #3101: Helper functions _add_one_to_C() and _add_one_to_F() become > + ?_Py_add_one_to_C() and _Py_add_one_to_F(), respectively. > + Minor point: the names are actually *_index_C and *_index_F. (the patch is right, just NEWS and the checkin comment are off) Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From python-checkins at python.org Thu Sep 2 00:11:53 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 2 Sep 2010 00:11:53 +0200 (CEST) Subject: [Python-checkins] r84415 - python/branches/py3k/Doc/glossary.rst Message-ID: <20100901221153.9D909EE985@mail.python.org> Author: raymond.hettinger Date: Thu Sep 2 00:11:53 2010 New Revision: 84415 Log: Glossary cleanup Modified: python/branches/py3k/Doc/glossary.rst Modified: python/branches/py3k/Doc/glossary.rst ============================================================================== --- python/branches/py3k/Doc/glossary.rst (original) +++ python/branches/py3k/Doc/glossary.rst Thu Sep 2 00:11:53 2010 @@ -140,10 +140,9 @@ For more information about descriptors' methods, see :ref:`descriptors`. dictionary - An associative array, where arbitrary keys are mapped to values. The use - of :class:`dict` closely resembles that for :class:`list`, but the keys can - be any object with a :meth:`__hash__` function, not just integers. - Called a hash in Perl. + An associative array, where arbitrary keys are mapped to values. The keys + can be any object with :meth:`__hash__` function and :meth:`__eq__` + methods. Called a hash in Perl. docstring A string literal which appears as the first expression in a class, @@ -192,9 +191,11 @@ :term:`abstract base class`. floor division - Mathematical division discarding any remainder. The floor division - operator is ``//``. For example, the expression ``11//4`` evaluates to - ``2`` in contrast to the ``2.75`` returned by float true division. + Mathematical division that rounds down to nearest integer. The floor + division operator is ``//``. For example, the expression ``11 // 4`` + evaluates to ``2`` in contrast to the ``2.75`` returned by float true + division. Note that ``(-11) // 4`` is ``-3`` because that is ``-2.75`` + rounded *downward*. See :pep:`238`. function A series of statements which returns some value to a caller. It can also @@ -202,7 +203,7 @@ the body. See also :term:`argument` and :term:`method`. __future__ - A pseudo module which programmers can use to enable new language features + A pseudo-module which programmers can use to enable new language features which are not compatible with the current interpreter. By importing the :mod:`__future__` module and evaluating its variables, @@ -222,13 +223,13 @@ generator A function which returns an iterator. It looks like a normal function - except that values are returned to the caller using a :keyword:`yield` - statement instead of a :keyword:`return` statement. Generator functions - often contain one or more :keyword:`for` or :keyword:`while` loops which - :keyword:`yield` elements back to the caller. The function execution is - stopped at the :keyword:`yield` keyword (returning the result) and is - resumed there when the next element is requested by calling the - :meth:`__next__` method of the returned iterator. + except that it contains :keyword:`yield` statements for producing a series + a values usable in a for-loop or that can be retrieved one at a time with + the :func:`next` function. Each :keyword:`yield` temporarily suspends + processing, remembering the location execution state (including local + variables and pending try-statements). When the generator resumes, it + picks-up where it left-off (in contrast to functions which start fresh on + every invocation. .. index:: single: generator expression @@ -273,9 +274,7 @@ IDLE An Integrated Development Environment for Python. IDLE is a basic editor and interpreter environment which ships with the standard distribution of - Python. Good for beginners, it also serves as clear example code for - those wanting to implement a moderately sophisticated, multi-platform GUI - application. + Python. immutable An object with a fixed value. Immutable objects include numbers, strings and @@ -377,7 +376,8 @@ mapping A container object (such as :class:`dict`) which supports arbitrary key - lookups using the special method :meth:`__getitem__`. + lookups using the special method :meth:`__getitem__`. Mappings also + support :meth:`__len__`, :meth:`__iter__`, and :meth:`__contains__`. metaclass The class of a class. Class definitions create a class name, a class From python-checkins at python.org Thu Sep 2 00:14:09 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 00:14:09 +0200 (CEST) Subject: [Python-checkins] r84416 - in python/branches/import_unicode: Include/unicodeobject.h Objects/unicodeobject.c Message-ID: <20100901221409.A2306EE98D@mail.python.org> Author: victor.stinner Date: Thu Sep 2 00:14:09 2010 New Revision: 84416 Log: Create Py_UNICODE_strdup() function Modified: python/branches/import_unicode/Include/unicodeobject.h python/branches/import_unicode/Objects/unicodeobject.c Modified: python/branches/import_unicode/Include/unicodeobject.h ============================================================================== --- python/branches/import_unicode/Include/unicodeobject.h (original) +++ python/branches/import_unicode/Include/unicodeobject.h Thu Sep 2 00:14:09 2010 @@ -1634,6 +1634,14 @@ const Py_UNICODE *s, Py_UNICODE c ); +/* Create a copy of a unicode string ending with a nul character. + Return NULL on memory error, otherwise return a new allocated buffer: + use PyMem_Free() to free the buffer. */ + +PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strdup( + PyObject *unicode + ); + #ifdef __cplusplus } #endif Modified: python/branches/import_unicode/Objects/unicodeobject.c ============================================================================== --- python/branches/import_unicode/Objects/unicodeobject.c (original) +++ python/branches/import_unicode/Objects/unicodeobject.c Thu Sep 2 00:14:09 2010 @@ -10018,6 +10018,22 @@ return NULL; } +Py_UNICODE* +Py_UNICODE_strdup(PyObject *object) +{ + PyUnicodeObject *unicode = (PyUnicodeObject *)object; + Py_UNICODE *copy; + Py_ssize_t size; + size = PyUnicode_GET_SIZE(unicode) + 1; /* copy the nul character */ + size *= sizeof(Py_UNICODE); + copy = PyMem_Malloc(size); + if (copy == NULL) { + PyErr_NoMemory(); + return NULL; + } + memcpy(copy, PyUnicode_AS_UNICODE(unicode), size); + return copy; +} #ifdef __cplusplus } From python-checkins at python.org Thu Sep 2 00:14:12 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 00:14:12 +0200 (CEST) Subject: [Python-checkins] r84417 - in python/branches/import_unicode: Include/unicodeobject.h Objects/unicodeobject.c Message-ID: <20100901221412.C50F6EE9EE@mail.python.org> Author: victor.stinner Date: Thu Sep 2 00:14:12 2010 New Revision: 84417 Log: Create Py_UNICODE_strcat() function Modified: python/branches/import_unicode/Include/unicodeobject.h python/branches/import_unicode/Objects/unicodeobject.c Modified: python/branches/import_unicode/Include/unicodeobject.h ============================================================================== --- python/branches/import_unicode/Include/unicodeobject.h (original) +++ python/branches/import_unicode/Include/unicodeobject.h Thu Sep 2 00:14:12 2010 @@ -1620,6 +1620,9 @@ PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strcpy( Py_UNICODE *s1, const Py_UNICODE *s2); +PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strcat( + Py_UNICODE *s1, const Py_UNICODE *s2); + PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strncpy( Py_UNICODE *s1, const Py_UNICODE *s2, size_t n); Modified: python/branches/import_unicode/Objects/unicodeobject.c ============================================================================== --- python/branches/import_unicode/Objects/unicodeobject.c (original) +++ python/branches/import_unicode/Objects/unicodeobject.c Thu Sep 2 00:14:12 2010 @@ -9980,6 +9980,13 @@ return s1; } +Py_UNICODE* +Py_UNICODE_strcat(Py_UNICODE *s1, const Py_UNICODE *s2) +{ + s1 += Py_UNICODE_strlen(s1); + return Py_UNICODE_strcpy(s1, s2); +} + int Py_UNICODE_strcmp(const Py_UNICODE *s1, const Py_UNICODE *s2) { From python-checkins at python.org Thu Sep 2 00:14:15 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 00:14:15 +0200 (CEST) Subject: [Python-checkins] r84418 - in python/branches/import_unicode: Include/unicodeobject.h Objects/unicodeobject.c Message-ID: <20100901221415.558F7EE98D@mail.python.org> Author: victor.stinner Date: Thu Sep 2 00:14:15 2010 New Revision: 84418 Log: Backport Py_UNICODE_strncmp() functio Modified: python/branches/import_unicode/Include/unicodeobject.h python/branches/import_unicode/Objects/unicodeobject.c Modified: python/branches/import_unicode/Include/unicodeobject.h ============================================================================== --- python/branches/import_unicode/Include/unicodeobject.h (original) +++ python/branches/import_unicode/Include/unicodeobject.h Thu Sep 2 00:14:15 2010 @@ -1629,6 +1629,12 @@ PyAPI_FUNC(int) Py_UNICODE_strcmp( const Py_UNICODE *s1, const Py_UNICODE *s2); +PyAPI_FUNC(int) Py_UNICODE_strncmp( + const Py_UNICODE *s1, + const Py_UNICODE *s2, + size_t n + ); + PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strchr( const Py_UNICODE *s, Py_UNICODE c ); Modified: python/branches/import_unicode/Objects/unicodeobject.c ============================================================================== --- python/branches/import_unicode/Objects/unicodeobject.c (original) +++ python/branches/import_unicode/Objects/unicodeobject.c Thu Sep 2 00:14:15 2010 @@ -10025,6 +10025,23 @@ return NULL; } +int +Py_UNICODE_strncmp(const Py_UNICODE *s1, const Py_UNICODE *s2, size_t n) +{ + register Py_UNICODE u1, u2; + for (; n != 0; n--) { + u1 = *s1; + u2 = *s2; + if (u1 != u2) + return (u1 < u2) ? -1 : +1; + if (u1 == '\0') + return 0; + s1++; + s2++; + } + return 0; +} + Py_UNICODE* Py_UNICODE_strdup(PyObject *object) { From python-checkins at python.org Thu Sep 2 00:14:17 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 00:14:17 +0200 (CEST) Subject: [Python-checkins] r84419 - python/branches/import_unicode/Python/import.c Message-ID: <20100901221417.B27D8EEAD3@mail.python.org> Author: victor.stinner Date: Thu Sep 2 00:14:17 2010 New Revision: 84419 Log: make_source_pathname() uses unicode Modified: python/branches/import_unicode/Python/import.c Modified: python/branches/import_unicode/Python/import.c ============================================================================== --- python/branches/import_unicode/Python/import.c (original) +++ python/branches/import_unicode/Python/import.c Thu Sep 2 00:14:17 2010 @@ -853,6 +853,8 @@ /* Like strrchr(string, '/') but searches for the rightmost of either SEP or ALTSEP, if the latter is defined. */ + +/* FIXME: Remove this function and rename rightmost_sep_unicode() */ static char * rightmost_sep(char *s) { @@ -870,6 +872,23 @@ return found; } +static Py_UNICODE * +rightmost_sep_unicode(Py_UNICODE *s) +{ + Py_UNICODE *found, c; + for (found = NULL; (c = *s); s++) { + if (c == SEP +#ifdef ALTSEP + || c == ALTSEP +#endif + ) + { + found = s; + } + } + return found; +} + /* Given a pathname for a Python source file, fill a buffer with the pathname for the corresponding compiled file. Return the pathname @@ -1010,77 +1029,73 @@ 3147 style, NULL is returned. buf must be at least as big as pathname; the resulting path will always be shorter. */ -/* FIXME: use Py_UNICODE* instead of char* */ -static char * -_make_source_pathname(char *pathname, char *buf) +static PyObject* +make_source_pathname(PyObject *pathobj) { + Py_UNICODE buf[MAXPATHLEN + 1]; + PyObject *cpathname; + Py_UNICODE *pathname; /* __pycache__/foo..pyc -> foo.py */ - size_t i, j; - char *left, *right, *dot0, *dot1, sep; + Py_UNICODE *left, *right, *dot0, *dot1, sep; + PyObject *cachedir = NULL; + Py_ssize_t len; + + pathname = Py_UNICODE_strdup(pathobj); + if (pathname == NULL) + return PyErr_NoMemory(); + cachedir = PyUnicode_FromString(CACHEDIR); /* Look back two slashes from the end. In between these two slashes must be the string __pycache__ or this is not a PEP 3147 style path. It's possible for there to be only one slash. */ - if ((right = rightmost_sep(pathname)) == NULL) - return NULL; + if ((right = rightmost_sep_unicode(pathname)) == NULL) + goto error; sep = *right; *right = '\0'; - left = rightmost_sep(pathname); + left = rightmost_sep_unicode(pathname); *right = sep; if (left == NULL) left = pathname; else left++; - if (right-left != strlen(CACHEDIR) || - strncmp(left, CACHEDIR, right-left) != 0) - return NULL; + if (right-left != PyUnicode_GET_SIZE(cachedir) || + Py_UNICODE_strncmp(left, PyUnicode_AS_UNICODE(cachedir), right-left) != 0) + goto error; /* Now verify that the path component to the right of the last slash has two dots in it. */ - if ((dot0 = strchr(right + 1, '.')) == NULL) - return NULL; - if ((dot1 = strchr(dot0 + 1, '.')) == NULL) - return NULL; + if ((dot0 = Py_UNICODE_strchr(right + 1, '.')) == NULL) + goto error; + if ((dot1 = Py_UNICODE_strchr(dot0 + 1, '.')) == NULL) + goto error; /* Too many dots? */ - if (strchr(dot1 + 1, '.') != NULL) - return NULL; + if (Py_UNICODE_strchr(dot1 + 1, '.') != NULL) + goto error; /* This is a PEP 3147 path. Start by copying everything from the start of pathname up to and including the leftmost slash. Then copy the file's basename, removing the magic tag and adding a .py suffix. */ - strncpy(buf, pathname, (i=left-pathname)); - strncpy(buf+i, right+1, (j=dot0-right)); - strcpy(buf+i+j, "py"); - return buf; -} - -static PyObject* -make_source_pathname(PyObject *pathobj) -{ - /* FIXME: use Py_UNICODE* instead of char* */ - char buf[MAXPATHLEN + 1]; - PyObject *pathbytes; - char *pathname, *cpathname; - - pathbytes = PyUnicode_EncodeFSDefault(pathobj); - if (pathbytes == NULL) - return NULL; + len = left-pathname; + Py_UNICODE_strncpy(buf, pathname, len); + Py_UNICODE_strncpy(buf+len, right+1, dot0-right); + len += dot0 - right; + buf[len++] = 'p'; + buf[len++] = 'y'; + buf[len] = '\0'; - pathname = strdup(PyBytes_AsString(pathbytes)); - Py_DECREF(pathbytes); - if (pathname == NULL) - return PyErr_NoMemory(); + cpathname = PyUnicode_FromUnicode(buf, len); + goto finally; - cpathname = _make_source_pathname(pathname, buf); - free(pathname); - if (cpathname != NULL) - return PyUnicode_DecodeFSDefault(cpathname); - else - return NULL; +error: + cpathname = NULL; +finally: + PyMem_Free(pathname); + Py_XDECREF(cachedir); + return cpathname; } /* Given a pathname for a Python source file, its time of last From python-checkins at python.org Thu Sep 2 00:14:20 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 00:14:20 +0200 (CEST) Subject: [Python-checkins] r84420 - python/branches/import_unicode/Python/import.c Message-ID: <20100901221420.03EF3EEA7D@mail.python.org> Author: victor.stinner Date: Thu Sep 2 00:14:19 2010 New Revision: 84420 Log: make_compiled_pathname() uses unicode Modified: python/branches/import_unicode/Python/import.c Modified: python/branches/import_unicode/Python/import.c ============================================================================== --- python/branches/import_unicode/Python/import.c (original) +++ python/branches/import_unicode/Python/import.c Thu Sep 2 00:14:19 2010 @@ -895,15 +895,42 @@ for the compiled file, or NULL if there's no space in the buffer. Doesn't set an exception. */ -/* FIXME: use Py_UNICODE*, not char* */ -static char * -_make_compiled_pathname(char *pathname, char *buf, size_t buflen, int debug) +static PyObject* +make_compiled_pathname(PyObject *pathobj, int debug) { /* foo.py -> __pycache__/foo..pyc */ - size_t len = strlen(pathname); size_t i, save; - char *pos; + Py_UNICODE *pos; int sep = SEP; + Py_UNICODE buf[MAXPATHLEN+1]; + size_t buflen = sizeof(buf); + PyObject *cpathname; + Py_UNICODE *pathname; + PyObject *cachedir = NULL; + PyObject *pyc_tag_unicode = NULL; + PyObject *suffix = NULL; + Py_ssize_t len; + + pathname = Py_UNICODE_strdup(pathobj); + if (pathname == NULL) + return NULL; + cachedir = PyUnicode_FromString(CACHEDIR); + if (cachedir == NULL) { + PyErr_NoMemory(); + goto error; + } + pyc_tag_unicode = PyUnicode_FromString(pyc_tag); + if (pyc_tag_unicode == NULL) { + PyErr_NoMemory(); + goto error; + } + suffix = PyUnicode_FromString(debug ? ".pyc" : ".pyo"); + if (suffix == NULL) { + PyErr_NoMemory(); + goto error; + } + + len = PyUnicode_GET_SIZE(pathobj); /* Sanity check that the buffer has roughly enough space to hold what will eventually be the full path to the compiled file. The 5 extra @@ -914,35 +941,35 @@ sanity check before writing the extension to ensure we do not overflow the buffer. */ - if (len + strlen(CACHEDIR) + strlen(pyc_tag) + 5 > buflen) - return NULL; + if (len + PyUnicode_GET_SIZE(cachedir) + PyUnicode_GET_SIZE(pyc_tag_unicode) + 5 > buflen) + goto error; /* Find the last path separator and copy everything from the start of the source string up to and including the separator. */ - if ((pos = rightmost_sep(pathname)) == NULL) { + if ((pos = rightmost_sep_unicode(pathname)) == NULL) { i = 0; } else { sep = *pos; i = pos - pathname + 1; - strncpy(buf, pathname, i); + Py_UNICODE_strncpy(buf, pathname, i); } save = i; buf[i++] = '\0'; /* Add __pycache__/ */ - strcat(buf, CACHEDIR); - i += strlen(CACHEDIR) - 1; + Py_UNICODE_strcat(buf, PyUnicode_AS_UNICODE(cachedir)); + i += PyUnicode_GET_SIZE(cachedir) - 1; buf[i++] = sep; buf[i++] = '\0'; /* Add the base filename, but remove the .py or .pyw extension, since the tag name must go before the extension. */ - strcat(buf, pathname + save); - if ((pos = strrchr(buf, '.')) != NULL) + Py_UNICODE_strcat(buf, pathname + save); + if ((pos = Py_UNICODE_strrchr(buf, '.')) != NULL) *++pos = '\0'; - strcat(buf, pyc_tag); + Py_UNICODE_strcat(buf, PyUnicode_AS_UNICODE(pyc_tag_unicode)); /* The length test above assumes that we're only adding one character to the end of what would normally be the extension. What if there is no extension, or the string ends in '.' or '.p', and otherwise @@ -992,34 +1019,22 @@ #if 0 printf("strlen(buf): %d; buflen: %d\n", (int)strlen(buf), (int)buflen); #endif - if (strlen(buf) + 5 > buflen) - return NULL; - strcat(buf, debug ? ".pyc" : ".pyo"); - assert(strlen(buf) < buflen); - return buf; -} - -static PyObject* -make_compiled_pathname(PyObject *pathobj, int debug) -{ - /* FIXME: use Py_UNICODE* instead of char* */ - char buf[MAXPATHLEN+1]; - PyObject *pathbytes; - char *pathname, *cpathname; - - pathbytes = PyUnicode_EncodeFSDefault(pathobj); - if (pathbytes == NULL) - return NULL; - - pathname = strdup(PyBytes_AsString(pathbytes)); - Py_DECREF(pathbytes); + if (Py_UNICODE_strlen(buf) + 5 > buflen) + goto error; + Py_UNICODE_strcat(buf, PyUnicode_AS_UNICODE(suffix)); + len = Py_UNICODE_strlen(buf); + assert(len < buflen); + cpathname = PyUnicode_FromUnicode(buf, len); + goto finally; - cpathname = _make_compiled_pathname(pathname, buf, sizeof(buf), debug); - free(pathname); - if (cpathname != NULL) - return PyUnicode_DecodeFSDefault(cpathname); - else - return NULL; +error: + cpathname = NULL; +finally: + PyMem_Free(pathname); + Py_XDECREF(cachedir); + Py_XDECREF(pyc_tag_unicode); + Py_XDECREF(suffix); + return cpathname; } @@ -1044,6 +1059,10 @@ if (pathname == NULL) return PyErr_NoMemory(); cachedir = PyUnicode_FromString(CACHEDIR); + if (cachedir == NULL) { + PyErr_NoMemory(); + goto error; + } /* Look back two slashes from the end. In between these two slashes must be the string __pycache__ or this is not a PEP 3147 style From python-checkins at python.org Thu Sep 2 00:14:22 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 00:14:22 +0200 (CEST) Subject: [Python-checkins] r84421 - python/branches/import_unicode/Python/import.c Message-ID: <20100901221422.3833DEEAAC@mail.python.org> Author: victor.stinner Date: Thu Sep 2 00:14:22 2010 New Revision: 84421 Log: write_compiled_module() uses PySys_FormatStderr() Modified: python/branches/import_unicode/Python/import.c Modified: python/branches/import_unicode/Python/import.c ============================================================================== --- python/branches/import_unicode/Python/import.c (original) +++ python/branches/import_unicode/Python/import.c Thu Sep 2 00:14:22 2010 @@ -1311,9 +1311,9 @@ dirpath = rightmost_sep(cpathname); if (dirpath == NULL) { if (Py_VerboseFlag) - PySys_WriteStderr( - "# no %s path found %s\n", - CACHEDIR, cpathname); + PySys_FormatStderr( + "# no %U path found %U\n", + CACHEDIR, cpathobj); Py_DECREF(cpathbytes); return; } @@ -1323,8 +1323,8 @@ if (mkdir(cpathname, dirmode) < 0 && errno != EEXIST) { *dirpath = saved; if (Py_VerboseFlag) - PySys_WriteStderr( - "# cannot create cache dir %s\n", cpathname); + PySys_FormatStderr( + "# cannot create cache directory %U\n", cpathobj); Py_DECREF(cpathbytes); return; } @@ -1333,8 +1333,8 @@ fp = open_exclusive(cpathname, mode); if (fp == NULL) { if (Py_VerboseFlag) - PySys_WriteStderr( - "# can't create %s\n", cpathname); + PySys_FormatStderr( + "# can't create %U\n", cpathobj); Py_DECREF(cpathbytes); return; } @@ -1344,7 +1344,7 @@ PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION); if (fflush(fp) != 0 || ferror(fp)) { if (Py_VerboseFlag) - PySys_WriteStderr("# can't write %s\n", cpathname); + PySys_FormatStderr("# can't write %U\n", cpathobj); /* Don't keep partial file */ fclose(fp); (void) unlink(cpathname); @@ -1358,7 +1358,7 @@ fflush(fp); fclose(fp); if (Py_VerboseFlag) - PySys_WriteStderr("# wrote %s\n", cpathname); + PySys_FormatStderr("# wrote %U\n", cpathobj); Py_DECREF(cpathbytes); } From python-checkins at python.org Thu Sep 2 00:14:24 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 00:14:24 +0200 (CEST) Subject: [Python-checkins] r84422 - python/branches/import_unicode/Modules/posixmodule.c Message-ID: <20100901221424.CFA30EEA81@mail.python.org> Author: victor.stinner Date: Thu Sep 2 00:14:24 2010 New Revision: 84422 Log: Remove outdated comment Modified: python/branches/import_unicode/Modules/posixmodule.c Modified: python/branches/import_unicode/Modules/posixmodule.c ============================================================================== --- python/branches/import_unicode/Modules/posixmodule.c (original) +++ python/branches/import_unicode/Modules/posixmodule.c Thu Sep 2 00:14:24 2010 @@ -2771,8 +2771,6 @@ return NULL; path = PyBytes_AsString(opath); Py_BEGIN_ALLOW_THREADS - /* PyUnicode_AS_UNICODE OK without thread lock as - it is a simple dereference. */ res = CreateDirectoryA(path, NULL); Py_END_ALLOW_THREADS if (!res) { From python-checkins at python.org Thu Sep 2 00:14:27 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 00:14:27 +0200 (CEST) Subject: [Python-checkins] r84423 - python/branches/import_unicode/Python/import.c Message-ID: <20100901221427.2A43BEE9F5@mail.python.org> Author: victor.stinner Date: Thu Sep 2 00:14:26 2010 New Revision: 84423 Log: write_compiled_module() uses unicode on Windows Modified: python/branches/import_unicode/Python/import.c Modified: python/branches/import_unicode/Python/import.c ============================================================================== --- python/branches/import_unicode/Python/import.c (original) +++ python/branches/import_unicode/Python/import.c Thu Sep 2 00:14:26 2010 @@ -854,7 +854,7 @@ or ALTSEP, if the latter is defined. */ -/* FIXME: Remove this function and rename rightmost_sep_unicode() */ +#ifndef MS_WINDOWS static char * rightmost_sep(char *s) { @@ -871,6 +871,7 @@ } return found; } +#endif static Py_UNICODE * rightmost_sep_unicode(Py_UNICODE *s) @@ -1241,6 +1242,23 @@ /* Helper to open a bytecode file for writing in exclusive mode */ +#ifdef MS_WINDOWS /* since Windows uses different permissions */ +static FILE * +open_exclusive(Py_UNICODE *filename, mode_t mode) +{ + int fd; + (void)DeleteFileW(filename); + + /* hFile = CreateFileW(filename, + FILE_WRITE_ATTRIBUTES, 0, + NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); */ + fd = open(@@@ filename @@@, O_EXCL|O_CREAT|O_WRONLY|O_TRUNC|O_BINARY, mode); + if (fd < 0) + return NULL; + return fdopen(fd, "wb"); +} +#else static FILE * open_exclusive(char *filename, mode_t mode) { @@ -1271,6 +1289,7 @@ return fopen(filename, "wb"); #endif } +#endif /* Write a compiled module to a file, placing the time of last @@ -1283,50 +1302,66 @@ struct stat *srcstat) { FILE *fp; - char *dirpath; time_t mtime = srcstat->st_mtime; #ifdef MS_WINDOWS /* since Windows uses different permissions */ mode_t mode = srcstat->st_mode & ~S_IEXEC; mode_t dirmode = srcstat->st_mode | S_IEXEC; /* XXX Is this correct for Windows? 2010-04-07 BAW */ + Py_UNICODE saved; + Py_UNICODE *dirpath; + Py_UNICODE *cpathname; + int res; #else mode_t mode = srcstat->st_mode & ~S_IXUSR & ~S_IXGRP & ~S_IXOTH; mode_t dirmode = (srcstat->st_mode | S_IXUSR | S_IXGRP | S_IXOTH | S_IWUSR | S_IWGRP | S_IWOTH); -#endif - int saved; - PyObject *cpathbytes; + char saved; + char *dirpath; char *cpathname; + PyObject *cpathbytes; +#endif + int err; +#ifdef MS_WINDOWS + cpathname = PyUnicode_AS_UNICODE(cpathobj); + dirpath = rightmost_sep_unicode(cpathname); +#else cpathbytes = PyUnicode_EncodeFSDefault(cpathobj); if (cpathbytes == NULL) { PyErr_Clear(); return; } cpathname = PyBytes_AS_STRING(cpathbytes); + dirpath = rightmost_sep(cpathname); +#endif /* Ensure that the __pycache__ directory exists. */ - dirpath = rightmost_sep(cpathname); if (dirpath == NULL) { if (Py_VerboseFlag) PySys_FormatStderr( "# no %U path found %U\n", CACHEDIR, cpathobj); - Py_DECREF(cpathbytes); - return; + goto finally; } saved = *dirpath; *dirpath = '\0'; - /* XXX call os.mkdir() or maybe CreateDirectoryA() on Windows? */ - if (mkdir(cpathname, dirmode) < 0 && errno != EEXIST) { +#ifdef MS_WINDOWS + res = CreateDirectoryW(cpathname, NULL); /* FIXME: use dirmode? */ + err = !res; +#else + if (mkdir(cpathname, dirmode) < 0) + err = (errno != EEXIST); + else + err = 0; +#endif + if (err) { *dirpath = saved; if (Py_VerboseFlag) PySys_FormatStderr( "# cannot create cache directory %U\n", cpathobj); - Py_DECREF(cpathbytes); - return; + goto finally; } *dirpath = saved; @@ -1335,8 +1370,7 @@ if (Py_VerboseFlag) PySys_FormatStderr( "# can't create %U\n", cpathobj); - Py_DECREF(cpathbytes); - return; + goto finally; } PyMarshal_WriteLongToFile(pyc_magic, fp, Py_MARSHAL_VERSION); /* First write a 0 for mtime */ @@ -1347,9 +1381,12 @@ PySys_FormatStderr("# can't write %U\n", cpathobj); /* Don't keep partial file */ fclose(fp); +#ifdef MS_WINDOWS + (void)DeleteFileW(cpathname); +#else (void) unlink(cpathname); - Py_DECREF(cpathbytes); - return; +#endif + goto finally; } /* Now write the true mtime */ fseek(fp, 4L, 0); @@ -1359,7 +1396,11 @@ fclose(fp); if (Py_VerboseFlag) PySys_FormatStderr("# wrote %U\n", cpathobj); + +finally: +#ifndef MS_WINDOWS Py_DECREF(cpathbytes); +#endif } static void From python-checkins at python.org Thu Sep 2 00:14:29 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 00:14:29 +0200 (CEST) Subject: [Python-checkins] r84424 - in python/branches/import_unicode: Include/import.h Modules/signalmodule.c Python/import.c Python/importdl.c Python/pythonrun.c Message-ID: <20100901221429.9D510EEAD3@mail.python.org> Author: victor.stinner Date: Thu Sep 2 00:14:29 2010 New Revision: 84424 Log: Create _PyImport_FindBuiltinExtension() and _PyImport_FixupBuiltinExtension() * Remove _PyImport_FindExtension() and _PyImport_FixupExtension() * Rename _PyImport_FindExtensionUnicode() to _PyImport_FindExtension() * Rename _PyImport_FixupExtensionUnicode() to_PyImport_FixupExtension() Modified: python/branches/import_unicode/Include/import.h python/branches/import_unicode/Modules/signalmodule.c python/branches/import_unicode/Python/import.c python/branches/import_unicode/Python/importdl.c python/branches/import_unicode/Python/pythonrun.c Modified: python/branches/import_unicode/Include/import.h ============================================================================== --- python/branches/import_unicode/Include/import.h (original) +++ python/branches/import_unicode/Include/import.h Thu Sep 2 00:14:29 2010 @@ -50,10 +50,10 @@ PyAPI_FUNC(int) _PyImport_IsScript(struct filedescr *); PyAPI_FUNC(void) _PyImport_ReInitLock(void); -PyAPI_FUNC(PyObject *)_PyImport_FindExtension(char *, char *); -PyAPI_FUNC(PyObject *)_PyImport_FindExtensionUnicode(char *, PyObject *); -PyAPI_FUNC(int)_PyImport_FixupExtension(PyObject*, char *, char *); -PyAPI_FUNC(int)_PyImport_FixupExtensionUnicode(PyObject*, char *, PyObject *); +PyAPI_FUNC(PyObject *)_PyImport_FindBuiltinExtension(char *); +PyAPI_FUNC(PyObject *)_PyImport_FindExtension(char *, PyObject *); +PyAPI_FUNC(int)_PyImport_FixupBuiltinExtension(PyObject*, char *); +PyAPI_FUNC(int)_PyImport_FixupExtension(PyObject*, char *, PyObject *); struct _inittab { char *name; Modified: python/branches/import_unicode/Modules/signalmodule.c ============================================================================== --- python/branches/import_unicode/Modules/signalmodule.c (original) +++ python/branches/import_unicode/Modules/signalmodule.c Thu Sep 2 00:14:29 2010 @@ -932,7 +932,7 @@ { PyObject *m = PyInit_signal(); if (m) { - _PyImport_FixupExtension(m, "signal", "signal"); + _PyImport_FixupBuiltinExtension(m, "signal"); Py_DECREF(m); } } Modified: python/branches/import_unicode/Python/import.c ============================================================================== --- python/branches/import_unicode/Python/import.c (original) +++ python/branches/import_unicode/Python/import.c Thu Sep 2 00:14:29 2010 @@ -585,7 +585,7 @@ */ int -_PyImport_FixupExtensionUnicode(PyObject *mod, char *name, PyObject *filename) +_PyImport_FixupExtension(PyObject *mod, char *name, PyObject *filename) { PyObject *modules, *dict; struct PyModuleDef *def; @@ -625,32 +625,30 @@ if (def->m_base.m_copy == NULL) return -1; } - PyDict_SetItem(extensions, filename, (PyObject*)def); + if (filename != NULL) + PyDict_SetItem(extensions, filename, (PyObject*)def); + else + PyDict_SetItemString(extensions, name, (PyObject*)def); return 0; } int -_PyImport_FixupExtension(PyObject *mod, char *name, char *filename) +_PyImport_FixupBuiltinExtension(PyObject *mod, char *name) { - PyObject *fileobj; - int result; - fileobj = PyUnicode_FromString(filename); - if (fileobj == NULL) - return -1; - result = _PyImport_FixupExtensionUnicode(mod, name, fileobj); - Py_DECREF(fileobj); - return result; + return _PyImport_FixupExtension(mod, name, NULL); } - PyObject * -_PyImport_FindExtensionUnicode(char *name, PyObject *filename) +_PyImport_FindExtension(char *name, PyObject *filename) { PyObject *mod, *mdict; PyModuleDef* def; if (extensions == NULL) return NULL; - def = (PyModuleDef*)PyDict_GetItem(extensions, filename); + if (filename != NULL) + def = (PyModuleDef*)PyDict_GetItem(extensions, filename); + else + def = (PyModuleDef*)PyDict_GetItemString(extensions, name); if (def == NULL) return NULL; if (def->m_size == -1) { @@ -680,23 +678,22 @@ Py_DECREF(mod); return NULL; } - if (Py_VerboseFlag) - PySys_FormatStderr("import %s # previously loaded (%U)\n", - name, filename); + if (Py_VerboseFlag) { + if (filename != NULL) + PySys_FormatStderr("import %s # previously loaded (%U)\n", + name, filename); + else + PySys_FormatStderr("import %s\n", name); + } return mod; } PyObject * -_PyImport_FindExtension(char *name, char *filename) +_PyImport_FindBuiltinExtension(char *name) { - PyObject *fileobj, *mod; - fileobj = PyUnicode_DecodeFSDefault(filename); - mod = _PyImport_FindExtensionUnicode(name, fileobj); - Py_DECREF(fileobj); - return mod; + return _PyImport_FindExtension(name, NULL); } - /* Get the module object corresponding to a module name. First check the modules dictionary if there's one there, if not, create a new one and insert it in the modules dictionary. @@ -2446,7 +2443,7 @@ PyObject *path; path = PyUnicode_FromString(name); - if (_PyImport_FindExtensionUnicode(name, path) != NULL) { + if (_PyImport_FindExtension(name, path) != NULL) { Py_DECREF(path); return 1; } @@ -2468,7 +2465,7 @@ Py_DECREF(path); return -1; } - if (_PyImport_FixupExtensionUnicode(mod, name, path) < 0) { + if (_PyImport_FixupExtension(mod, name, path) < 0) { Py_DECREF(path); return -1; } Modified: python/branches/import_unicode/Python/importdl.c ============================================================================== --- python/branches/import_unicode/Python/importdl.c (original) +++ python/branches/import_unicode/Python/importdl.c Thu Sep 2 00:14:29 2010 @@ -28,7 +28,7 @@ PyObject* (*p)(void); struct PyModuleDef *def; - if ((m = _PyImport_FindExtensionUnicode(name, path)) != NULL) { + if ((m = _PyImport_FindExtension(name, path)) != NULL) { Py_INCREF(m); return m; } @@ -83,7 +83,7 @@ Py_DECREF(path); } - if (_PyImport_FixupExtensionUnicode(m, name, path) < 0) + if (_PyImport_FixupExtension(m, name, path) < 0) return NULL; if (Py_VerboseFlag) PySys_FormatStderr( Modified: python/branches/import_unicode/Python/pythonrun.c ============================================================================== --- python/branches/import_unicode/Python/pythonrun.c (original) +++ python/branches/import_unicode/Python/pythonrun.c Thu Sep 2 00:14:29 2010 @@ -240,7 +240,7 @@ bimod = _PyBuiltin_Init(); if (bimod == NULL) Py_FatalError("Py_Initialize: can't initialize builtins modules"); - _PyImport_FixupExtension(bimod, "builtins", "builtins"); + _PyImport_FixupBuiltinExtension(bimod, "builtins"); interp->builtins = PyModule_GetDict(bimod); if (interp->builtins == NULL) Py_FatalError("Py_Initialize: can't initialize builtins dict"); @@ -256,7 +256,7 @@ if (interp->sysdict == NULL) Py_FatalError("Py_Initialize: can't initialize sys dict"); Py_INCREF(interp->sysdict); - _PyImport_FixupExtension(sysmod, "sys", "sys"); + _PyImport_FixupBuiltinExtension(sysmod, "sys"); PySys_SetPath(Py_GetPath()); PyDict_SetItemString(interp->sysdict, "modules", interp->modules); @@ -557,7 +557,7 @@ interp->modules = PyDict_New(); interp->modules_reloading = PyDict_New(); - bimod = _PyImport_FindExtension("builtins", "builtins"); + bimod = _PyImport_FindBuiltinExtension("builtins"); if (bimod != NULL) { interp->builtins = PyModule_GetDict(bimod); if (interp->builtins == NULL) @@ -568,7 +568,7 @@ /* initialize builtin exceptions */ _PyExc_Init(); - sysmod = _PyImport_FindExtension("sys", "sys"); + sysmod = _PyImport_FindBuiltinExtension("sys"); if (bimod != NULL && sysmod != NULL) { PyObject *pstderr; interp->sysdict = PyModule_GetDict(sysmod); From python-checkins at python.org Thu Sep 2 00:21:36 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 2 Sep 2010 00:21:36 +0200 (CEST) Subject: [Python-checkins] r84425 - python/branches/release31-maint/Doc/glossary.rst Message-ID: <20100901222136.744C2EE985@mail.python.org> Author: raymond.hettinger Date: Thu Sep 2 00:21:36 2010 New Revision: 84425 Log: Glossary cleanup Modified: python/branches/release31-maint/Doc/glossary.rst Modified: python/branches/release31-maint/Doc/glossary.rst ============================================================================== --- python/branches/release31-maint/Doc/glossary.rst (original) +++ python/branches/release31-maint/Doc/glossary.rst Thu Sep 2 00:21:36 2010 @@ -134,10 +134,9 @@ For more information about descriptors' methods, see :ref:`descriptors`. dictionary - An associative array, where arbitrary keys are mapped to values. The use - of :class:`dict` closely resembles that for :class:`list`, but the keys can - be any object with a :meth:`__hash__` function, not just integers. - Called a hash in Perl. + An associative array, where arbitrary keys are mapped to values. The keys + can be any object with :meth:`__hash__` function and :meth:`__eq__` + methods. Called a hash in Perl. docstring A string literal which appears as the first expression in a class, @@ -186,9 +185,11 @@ :term:`abstract base class`. floor division - Mathematical division discarding any remainder. The floor division - operator is ``//``. For example, the expression ``11//4`` evaluates to - ``2`` in contrast to the ``2.75`` returned by float true division. + Mathematical division that rounds down to nearest integer. The floor + division operator is ``//``. For example, the expression ``11 // 4`` + evaluates to ``2`` in contrast to the ``2.75`` returned by float true + division. Note that ``(-11) // 4`` is ``-3`` because that is ``-2.75`` + rounded *downward*. See :pep:`238`. function A series of statements which returns some value to a caller. It can also @@ -196,7 +197,7 @@ the body. See also :term:`argument` and :term:`method`. __future__ - A pseudo module which programmers can use to enable new language features + A pseudo-module which programmers can use to enable new language features which are not compatible with the current interpreter. By importing the :mod:`__future__` module and evaluating its variables, @@ -214,13 +215,13 @@ generator A function which returns an iterator. It looks like a normal function - except that values are returned to the caller using a :keyword:`yield` - statement instead of a :keyword:`return` statement. Generator functions - often contain one or more :keyword:`for` or :keyword:`while` loops which - :keyword:`yield` elements back to the caller. The function execution is - stopped at the :keyword:`yield` keyword (returning the result) and is - resumed there when the next element is requested by calling the - :meth:`__next__` method of the returned iterator. + except that it contains :keyword:`yield` statements for producing a series + a values usable in a for-loop or that can be retrieved one at a time with + the :func:`next` function. Each :keyword:`yield` temporarily suspends + processing, remembering the location execution state (including local + variables and pending try-statements). When the generator resumes, it + picks-up where it left-off (in contrast to functions which start fresh on + every invocation. .. index:: single: generator expression @@ -265,9 +266,7 @@ IDLE An Integrated Development Environment for Python. IDLE is a basic editor and interpreter environment which ships with the standard distribution of - Python. Good for beginners, it also serves as clear example code for - those wanting to implement a moderately sophisticated, multi-platform GUI - application. + Python. immutable An object with a fixed value. Immutable objects include numbers, strings and @@ -369,7 +368,8 @@ mapping A container object (such as :class:`dict`) which supports arbitrary key - lookups using the special method :meth:`__getitem__`. + lookups using the special method :meth:`__getitem__`. Mappings also + support :meth:`__len__`, :meth:`__iter__`, and :meth:`__contains__`. metaclass The class of a class. Class definitions create a class name, a class From python-checkins at python.org Thu Sep 2 00:25:41 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 2 Sep 2010 00:25:41 +0200 (CEST) Subject: [Python-checkins] r84426 - python/branches/release27-maint/Doc/glossary.rst Message-ID: <20100901222541.6C6D2EE98D@mail.python.org> Author: raymond.hettinger Date: Thu Sep 2 00:25:41 2010 New Revision: 84426 Log: Glossary cleanup Modified: python/branches/release27-maint/Doc/glossary.rst Modified: python/branches/release27-maint/Doc/glossary.rst ============================================================================== --- python/branches/release27-maint/Doc/glossary.rst (original) +++ python/branches/release27-maint/Doc/glossary.rst Thu Sep 2 00:25:41 2010 @@ -143,10 +143,9 @@ For more information about descriptors' methods, see :ref:`descriptors`. dictionary - An associative array, where arbitrary keys are mapped to values. The use - of :class:`dict` closely resembles that for :class:`list`, but the keys can - be any object with a :meth:`__hash__` function, not just integers. - Called a hash in Perl. + An associative array, where arbitrary keys are mapped to values. The keys + can be any object with :meth:`__hash__` function and :meth:`__eq__` + methods. Called a hash in Perl. docstring A string literal which appears as the first expression in a class, @@ -193,13 +192,20 @@ implement a method named :meth:`find_module`. See :pep:`302` for details. + floor division + Mathematical division that rounds down to nearest integer. The floor + division operator is ``//``. For example, the expression ``11 // 4`` + evaluates to ``2`` in contrast to the ``2.75`` returned by float true + division. Note that ``(-11) // 4`` is ``-3`` because that is ``-2.75`` + rounded *downward*. See :pep:`238`. + function A series of statements which returns some value to a caller. It can also be passed zero or more arguments which may be used in the execution of the body. See also :term:`argument` and :term:`method`. __future__ - A pseudo module which programmers can use to enable new language features + A pseudo-module which programmers can use to enable new language features which are not compatible with the current interpreter. For example, the expression ``11/4`` currently evaluates to ``2``. If the module in which it is executed had enabled *true division* by executing:: @@ -224,13 +230,13 @@ generator A function which returns an iterator. It looks like a normal function - except that values are returned to the caller using a :keyword:`yield` - statement instead of a :keyword:`return` statement. Generator functions - often contain one or more :keyword:`for` or :keyword:`while` loops which - :keyword:`yield` elements back to the caller. The function execution is - stopped at the :keyword:`yield` keyword (returning the result) and is - resumed there when the next element is requested by calling the - :meth:`next` method of the returned iterator. + except that it contains :keyword:`yield` statements for producing a series + a values usable in a for-loop or that can be retrieved one at a time with + the :func:`next` function. Each :keyword:`yield` temporarily suspends + processing, remembering the location execution state (including local + variables and pending try-statements). When the generator resumes, it + picks-up where it left-off (in contrast to functions which start fresh on + every invocation). .. index:: single: generator expression @@ -275,9 +281,7 @@ IDLE An Integrated Development Environment for Python. IDLE is a basic editor and interpreter environment which ships with the standard distribution of - Python. Good for beginners, it also serves as clear example code for - those wanting to implement a moderately sophisticated, multi-platform GUI - application. + Python. immutable An object with a fixed value. Immutable objects include numbers, strings and @@ -389,7 +393,8 @@ mapping A container object (such as :class:`dict`) which supports arbitrary key - lookups using the special method :meth:`__getitem__`. + lookups using the special method :meth:`__getitem__`. Mappings also + support :meth:`__len__`, :meth:`__iter__`, and :meth:`__contains__`. metaclass The class of a class. Class definitions create a class name, a class From python-checkins at python.org Thu Sep 2 00:37:17 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 2 Sep 2010 00:37:17 +0200 (CEST) Subject: [Python-checkins] r84427 - in python/branches/release27-maint/Doc: conf.py tools/sphinxext/indexcontent.html using/index.rst Message-ID: <20100901223717.5FBF9EEAFC@mail.python.org> Author: raymond.hettinger Date: Thu Sep 2 00:37:17 2010 New Revision: 84427 Log: More descriptive title. Modified: python/branches/release27-maint/Doc/conf.py python/branches/release27-maint/Doc/tools/sphinxext/indexcontent.html python/branches/release27-maint/Doc/using/index.rst Modified: python/branches/release27-maint/Doc/conf.py ============================================================================== --- python/branches/release27-maint/Doc/conf.py (original) +++ python/branches/release27-maint/Doc/conf.py Thu Sep 2 00:37:17 2010 @@ -125,7 +125,7 @@ ('tutorial/index', 'tutorial.tex', 'Python Tutorial', _stdauthor, 'manual'), ('using/index', 'using.tex', - 'Using Python', _stdauthor, 'manual'), + 'Python Setup', _stdauthor, 'manual'), ('whatsnew/' + version, 'whatsnew.tex', 'What\'s New in Python', 'A. M. Kuchling', 'howto'), ] Modified: python/branches/release27-maint/Doc/tools/sphinxext/indexcontent.html ============================================================================== --- python/branches/release27-maint/Doc/tools/sphinxext/indexcontent.html (original) +++ python/branches/release27-maint/Doc/tools/sphinxext/indexcontent.html Thu Sep 2 00:37:17 2010 @@ -7,12 +7,12 @@ or all "What's new" documents since 2.0

- +
Modified: python/branches/release27-maint/Doc/using/index.rst ============================================================================== --- python/branches/release27-maint/Doc/using/index.rst (original) +++ python/branches/release27-maint/Doc/using/index.rst Thu Sep 2 00:37:17 2010 @@ -1,7 +1,7 @@ .. _using-index: ################ - Using Python + Python Setup ################ From python-checkins at python.org Thu Sep 2 00:52:25 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 2 Sep 2010 00:52:25 +0200 (CEST) Subject: [Python-checkins] r84428 - in python/branches/release31-maint/Doc: conf.py tools/sphinxext/indexcontent.html using/index.rst Message-ID: <20100901225225.ED117EE981@mail.python.org> Author: raymond.hettinger Date: Thu Sep 2 00:52:25 2010 New Revision: 84428 Log: More descriptive title. Modified: python/branches/release31-maint/Doc/conf.py python/branches/release31-maint/Doc/tools/sphinxext/indexcontent.html python/branches/release31-maint/Doc/using/index.rst Modified: python/branches/release31-maint/Doc/conf.py ============================================================================== --- python/branches/release31-maint/Doc/conf.py (original) +++ python/branches/release31-maint/Doc/conf.py Thu Sep 2 00:52:25 2010 @@ -128,7 +128,7 @@ ('tutorial/index', 'tutorial.tex', 'Python Tutorial', _stdauthor, 'manual'), ('using/index', 'using.tex', - 'Using Python', _stdauthor, 'manual'), + 'Python Setup', _stdauthor, 'manual'), ('whatsnew/' + version, 'whatsnew.tex', 'What\'s New in Python', 'A. M. Kuchling', 'howto'), ] Modified: python/branches/release31-maint/Doc/tools/sphinxext/indexcontent.html ============================================================================== --- python/branches/release31-maint/Doc/tools/sphinxext/indexcontent.html (original) +++ python/branches/release31-maint/Doc/tools/sphinxext/indexcontent.html Thu Sep 2 00:52:25 2010 @@ -7,12 +7,12 @@ or all "What's new" documents since 2.0

- +
Modified: python/branches/release31-maint/Doc/using/index.rst ============================================================================== --- python/branches/release31-maint/Doc/using/index.rst (original) +++ python/branches/release31-maint/Doc/using/index.rst Thu Sep 2 00:52:25 2010 @@ -1,7 +1,7 @@ .. _using-index: ################ - Using Python + Python Setup ################ From python-checkins at python.org Thu Sep 2 01:43:50 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 01:43:50 +0200 (CEST) Subject: [Python-checkins] r84429 - in python/branches/py3k: Include/unicodeobject.h Objects/unicodeobject.c Message-ID: <20100901234350.C1A65EEAD0@mail.python.org> Author: victor.stinner Date: Thu Sep 2 01:43:50 2010 New Revision: 84429 Log: Create Py_UNICODE_strcat() function Modified: python/branches/py3k/Include/unicodeobject.h python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Include/unicodeobject.h ============================================================================== --- python/branches/py3k/Include/unicodeobject.h (original) +++ python/branches/py3k/Include/unicodeobject.h Thu Sep 2 01:43:50 2010 @@ -1573,6 +1573,9 @@ Py_UNICODE *s1, const Py_UNICODE *s2); +PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strcat( + Py_UNICODE *s1, const Py_UNICODE *s2); + PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strncpy( Py_UNICODE *s1, const Py_UNICODE *s2, Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Thu Sep 2 01:43:50 2010 @@ -9951,6 +9951,15 @@ return s1; } +Py_UNICODE* +Py_UNICODE_strcat(Py_UNICODE *s1, const Py_UNICODE *s2) +{ + Py_UNICODE *u1 = s1; + u1 += Py_UNICODE_strlen(u1); + Py_UNICODE_strcpy(u1, s2); + return s1; +} + int Py_UNICODE_strcmp(const Py_UNICODE *s1, const Py_UNICODE *s2) { From python-checkins at python.org Thu Sep 2 01:43:53 2010 From: python-checkins at python.org (victor.stinner) Date: Thu, 2 Sep 2010 01:43:53 +0200 (CEST) Subject: [Python-checkins] r84430 - in python/branches/py3k: Include/unicodeobject.h Objects/unicodeobject.c Message-ID: <20100901234353.468A5EEAAC@mail.python.org> Author: victor.stinner Date: Thu Sep 2 01:43:53 2010 New Revision: 84430 Log: Create PyUnicode_strdup() function Modified: python/branches/py3k/Include/unicodeobject.h python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Include/unicodeobject.h ============================================================================== --- python/branches/py3k/Include/unicodeobject.h (original) +++ python/branches/py3k/Include/unicodeobject.h Thu Sep 2 01:43:53 2010 @@ -220,6 +220,7 @@ # define _PyUnicode_AsDefaultEncodedString _PyUnicodeUCS2_AsDefaultEncodedString # define _PyUnicode_Fini _PyUnicodeUCS2_Fini # define _PyUnicode_Init _PyUnicodeUCS2_Init +# define PyUnicode_strdup PyUnicodeUCS2_strdup #else @@ -302,7 +303,7 @@ # define _PyUnicode_AsDefaultEncodedString _PyUnicodeUCS4_AsDefaultEncodedString # define _PyUnicode_Fini _PyUnicodeUCS4_Fini # define _PyUnicode_Init _PyUnicodeUCS4_Init - +# define PyUnicode_strdup PyUnicodeUCS4_strdup #endif @@ -1602,6 +1603,14 @@ Py_UNICODE c ); +/* Create a copy of a unicode string ending with a nul character. Return NULL + and raise a MemoryError exception on memory allocation failure, otherwise + return a new allocated buffer (use PyMem_Free() to free the buffer). */ + +PyAPI_FUNC(Py_UNICODE*) PyUnicode_strdup( + PyObject *unicode + ); + #ifdef __cplusplus } #endif Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Thu Sep 2 01:43:53 2010 @@ -10014,6 +10014,28 @@ return NULL; } +Py_UNICODE* +PyUnicode_strdup(PyObject *object) +{ + PyUnicodeObject *unicode = (PyUnicodeObject *)object; + Py_UNICODE *copy; + Py_ssize_t size; + + /* Ensure we won't overflow the size. */ + if (PyUnicode_GET_SIZE(unicode) > ((PY_SSIZE_T_MAX / sizeof(Py_UNICODE)) - 1)) { + PyErr_NoMemory(); + return NULL; + } + size = PyUnicode_GET_SIZE(unicode) + 1; /* copy the nul character */ + size *= sizeof(Py_UNICODE); + copy = PyMem_Malloc(size); + if (copy == NULL) { + PyErr_NoMemory(); + return NULL; + } + memcpy(copy, PyUnicode_AS_UNICODE(unicode), size); + return copy; +} #ifdef __cplusplus } From solipsis at pitrou.net Thu Sep 2 05:02:41 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 02 Sep 2010 05:02:41 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84430): sum=0 Message-ID: py3k results for svn r84430 (hg cset aabd2132bb90) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflog-hkpsv', '-x'] From python-checkins at python.org Thu Sep 2 11:17:32 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 2 Sep 2010 11:17:32 +0200 (CEST) Subject: [Python-checkins] r84431 - python/branches/py3k/Doc/tools/sphinxext/indexcontent.html Message-ID: <20100902091732.10276EE99C@mail.python.org> Author: raymond.hettinger Date: Thu Sep 2 11:17:31 2010 New Revision: 84431 Log: Keep contents order the same between versions. Modified: python/branches/py3k/Doc/tools/sphinxext/indexcontent.html Modified: python/branches/py3k/Doc/tools/sphinxext/indexcontent.html ============================================================================== --- python/branches/py3k/Doc/tools/sphinxext/indexcontent.html (original) +++ python/branches/py3k/Doc/tools/sphinxext/indexcontent.html Thu Sep 2 11:17:31 2010 @@ -11,10 +11,10 @@ keep this under your pillow

- +
From python-checkins at python.org Thu Sep 2 11:44:29 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 2 Sep 2010 11:44:29 +0200 (CEST) Subject: [Python-checkins] r84432 - in python/branches/py3k/Lib: collections.py functools.py Message-ID: <20100902094429.2EAC4FAEF@mail.python.org> Author: raymond.hettinger Date: Thu Sep 2 11:44:28 2010 New Revision: 84432 Log: Speed-up cache updates Modified: python/branches/py3k/Lib/collections.py python/branches/py3k/Lib/functools.py Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Thu Sep 2 11:44:28 2010 @@ -161,6 +161,19 @@ def __del__(self): self.clear() # eliminate cyclical references + def _move_to_end(self, key, PREV=0, NEXT=1): + 'Fast version of self[key]=self.pop(key). Private method for internal use.' + link = self.__map[key] + link_prev = link[PREV] + link_next = link[NEXT] + link_prev[NEXT] = link_next + link_next[PREV] = link_prev + root = self.__root + last = root[PREV] + link[PREV] = last + link[NEXT] = root + last[NEXT] = root[PREV] = link + ################################################################################ ### namedtuple Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Thu Sep 2 11:44:28 2010 @@ -139,8 +139,7 @@ try: with lock: result = cache[key] - del cache[key] - cache[key] = result # record recent use of this key + cache._move_to_end(key) # record recent use of this key wrapper.hits += 1 except KeyError: result = user_function(*args, **kwds) From python-checkins at python.org Thu Sep 2 16:57:19 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Thu, 2 Sep 2010 16:57:19 +0200 (CEST) Subject: [Python-checkins] r84433 - peps/trunk/pep-0007.txt Message-ID: <20100902145719.0445AEE984@mail.python.org> Author: daniel.stutzbach Date: Thu Sep 2 16:57:18 2010 New Revision: 84433 Log: Updated PEP-0007 to refer to PyAPI_FUNC instead of DL_IMPORT, which was deprecated in Python 2.3. Modified: peps/trunk/pep-0007.txt Modified: peps/trunk/pep-0007.txt ============================================================================== --- peps/trunk/pep-0007.txt (original) +++ peps/trunk/pep-0007.txt Thu Sep 2 16:57:18 2010 @@ -121,9 +121,9 @@ - For external functions and variables, we always have a declaration in an appropriate header file in the "Include" - directory, which uses the DL_IMPORT() macro, like this: + directory, which uses the PyAPI_FUNC() macro, like this: - extern DL_IMPORT(PyObject *) PyObject_Repr(PyObject *); + PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *); Naming conventions From python-checkins at python.org Thu Sep 2 17:06:03 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Thu, 2 Sep 2010 17:06:03 +0200 (CEST) Subject: [Python-checkins] r84434 - python/branches/py3k/Objects/setobject.c Message-ID: <20100902150603.321DFEEA24@mail.python.org> Author: daniel.stutzbach Date: Thu Sep 2 17:06:03 2010 New Revision: 84434 Log: Removed an extraneous semicolon Modified: python/branches/py3k/Objects/setobject.c Modified: python/branches/py3k/Objects/setobject.c ============================================================================== --- python/branches/py3k/Objects/setobject.c (original) +++ python/branches/py3k/Objects/setobject.c Thu Sep 2 17:06:03 2010 @@ -1439,7 +1439,7 @@ while ((key = PyIter_Next(it)) != NULL) { int rv; setentry entry; - long hash = PyObject_Hash(key);; + long hash = PyObject_Hash(key); if (hash == -1) { Py_DECREF(key); From python-checkins at python.org Thu Sep 2 17:06:06 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Thu, 2 Sep 2010 17:06:06 +0200 (CEST) Subject: [Python-checkins] r84435 - in python/branches/py3k: Doc/library/stdtypes.rst Lib/test/test_dictviews.py Misc/NEWS Objects/dictobject.c Message-ID: <20100902150606.ABF1AEEA24@mail.python.org> Author: daniel.stutzbach Date: Thu Sep 2 17:06:06 2010 New Revision: 84435 Log: Issue #9212: Added the missing isdisjoint method to the dict_keys and dict_items views. The method is required by the collections.Set ABC, which the views register as supporting. Modified: python/branches/py3k/Doc/library/stdtypes.rst python/branches/py3k/Lib/test/test_dictviews.py python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/dictobject.c Modified: python/branches/py3k/Doc/library/stdtypes.rst ============================================================================== --- python/branches/py3k/Doc/library/stdtypes.rst (original) +++ python/branches/py3k/Doc/library/stdtypes.rst Thu Sep 2 17:06:06 2010 @@ -2216,6 +2216,11 @@ Return the symmetric difference (all elements either in *dictview* or *other*, but not in both) of the dictview and the other object as a new set. +.. method:: dictview.isdisjoint(other) + + Return True if the view has no elements in common with *other*. Sets are + disjoint if and only if their intersection is the empty set. + An example of dictionary view usage:: Modified: python/branches/py3k/Lib/test/test_dictviews.py ============================================================================== --- python/branches/py3k/Lib/test/test_dictviews.py (original) +++ python/branches/py3k/Lib/test/test_dictviews.py Thu Sep 2 17:06:06 2010 @@ -112,6 +112,24 @@ self.assertEqual(d1.keys() ^ set(d3.keys()), {'a', 'b', 'd', 'e'}) + self.assertFalse(d1.keys().isdisjoint(d1.keys())) + self.assertFalse(d1.keys().isdisjoint(d2.keys())) + self.assertFalse(d1.keys().isdisjoint(list(d2.keys()))) + self.assertFalse(d1.keys().isdisjoint(set(d2.keys()))) + self.assertTrue(d1.keys().isdisjoint({'x', 'y', 'z'})) + self.assertTrue(d1.keys().isdisjoint(['x', 'y', 'z'])) + self.assertTrue(d1.keys().isdisjoint(set(['x', 'y', 'z']))) + self.assertTrue(d1.keys().isdisjoint(set(['x', 'y']))) + self.assertTrue(d1.keys().isdisjoint(['x', 'y'])) + self.assertTrue(d1.keys().isdisjoint({})) + self.assertTrue(d1.keys().isdisjoint(d3.keys())) + + de = {} + self.assertTrue(de.keys().isdisjoint(set())) + self.assertTrue(de.keys().isdisjoint([])) + self.assertTrue(de.keys().isdisjoint(de.keys())) + self.assertTrue(de.keys().isdisjoint([1])) + def test_items_set_operations(self): d1 = {'a': 1, 'b': 2} d2 = {'a': 2, 'b': 2} @@ -144,6 +162,23 @@ self.assertEqual(d1.items() ^ d3.items(), {('a', 1), ('b', 2), ('d', 4), ('e', 5)}) + self.assertFalse(d1.items().isdisjoint(d1.items())) + self.assertFalse(d1.items().isdisjoint(d2.items())) + self.assertFalse(d1.items().isdisjoint(list(d2.items()))) + self.assertFalse(d1.items().isdisjoint(set(d2.items()))) + self.assertTrue(d1.items().isdisjoint({'x', 'y', 'z'})) + self.assertTrue(d1.items().isdisjoint(['x', 'y', 'z'])) + self.assertTrue(d1.items().isdisjoint(set(['x', 'y', 'z']))) + self.assertTrue(d1.items().isdisjoint(set(['x', 'y']))) + self.assertTrue(d1.items().isdisjoint({})) + self.assertTrue(d1.items().isdisjoint(d3.items())) + + de = {} + self.assertTrue(de.items().isdisjoint(set())) + self.assertTrue(de.items().isdisjoint([])) + self.assertTrue(de.items().isdisjoint(de.items())) + self.assertTrue(de.items().isdisjoint([1])) + def test_main(): support.run_unittest(DictSetTest) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Sep 2 17:06:06 2010 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #9212: dict_keys and dict_items now provide the isdisjoint() + method, to conform to the Set ABC. + - Issue #9737: Fix a crash when trying to delete a slice or an item from a memoryview object. Modified: python/branches/py3k/Objects/dictobject.c ============================================================================== --- python/branches/py3k/Objects/dictobject.c (original) +++ python/branches/py3k/Objects/dictobject.c Thu Sep 2 17:06:06 2010 @@ -2807,7 +2807,63 @@ (binaryfunc)dictviews_or, /*nb_or*/ }; +static PyObject* +dictviews_isdisjoint(PyObject *self, PyObject *other) +{ + PyObject *it; + PyObject *item = NULL; + + if (self == other) { + if (dictview_len((dictviewobject *)self) == 0) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } + + /* Iterate over the shorter object (only if other is a set, + * because PySequence_Contains may be expensive otherwise): */ + if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) { + Py_ssize_t len_self = dictview_len((dictviewobject *)self); + Py_ssize_t len_other = PyObject_Size(other); + if (len_other == -1) + return NULL; + + if ((len_other > len_self)) { + PyObject *tmp = other; + other = self; + self = tmp; + } + } + + it = PyObject_GetIter(other); + if (it == NULL) + return NULL; + + while ((item = PyIter_Next(it)) != NULL) { + int contains = PySequence_Contains(self, item); + Py_DECREF(item); + if (contains == -1) { + Py_DECREF(it); + return NULL; + } + + if (contains) { + Py_DECREF(it); + Py_RETURN_FALSE; + } + } + Py_DECREF(it); + if (PyErr_Occurred()) + return NULL; /* PyIter_Next raised an exception. */ + Py_RETURN_TRUE; +} + +PyDoc_STRVAR(isdisjoint_doc, +"Return True if the view and the given iterable have a null intersection."); + static PyMethodDef dictkeys_methods[] = { + {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, + isdisjoint_doc}, {NULL, NULL} /* sentinel */ }; @@ -2892,6 +2948,8 @@ }; static PyMethodDef dictitems_methods[] = { + {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, + isdisjoint_doc}, {NULL, NULL} /* sentinel */ }; From python-checkins at python.org Thu Sep 2 17:13:35 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Thu, 2 Sep 2010 17:13:35 +0200 (CEST) Subject: [Python-checkins] r84436 - python/branches/py3k/Misc/NEWS Message-ID: <20100902151335.D1568EE98A@mail.python.org> Author: daniel.stutzbach Date: Thu Sep 2 17:13:35 2010 New Revision: 84436 Log: Credit where credit is due Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Sep 2 17:13:35 2010 @@ -13,7 +13,7 @@ ----------------- - Issue #9212: dict_keys and dict_items now provide the isdisjoint() - method, to conform to the Set ABC. + method, to conform to the Set ABC. Patch by Daniel Urban. - Issue #9737: Fix a crash when trying to delete a slice or an item from a memoryview object. From python-checkins at python.org Thu Sep 2 20:44:16 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 2 Sep 2010 20:44:16 +0200 (CEST) Subject: [Python-checkins] r84437 - python/branches/py3k/Lib/collections.py Message-ID: <20100902184416.4CCFBF80D@mail.python.org> Author: raymond.hettinger Date: Thu Sep 2 20:44:16 2010 New Revision: 84437 Log: Make OrderedDict.popitem() a bit smarter and faster Modified: python/branches/py3k/Lib/collections.py Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Thu Sep 2 20:44:16 2010 @@ -108,25 +108,37 @@ pass dict.clear(self) - setdefault = MutableMapping.setdefault - update = MutableMapping.update - pop = MutableMapping.pop - keys = MutableMapping.keys - values = MutableMapping.values - items = MutableMapping.items - __ne__ = MutableMapping.__ne__ - - def popitem(self, last=True): + def popitem(self, last=True, PREV=0, NEXT=1, KEY=2, dict_pop=dict.pop): '''od.popitem() -> (k, v), return and remove a (key, value) pair. Pairs are returned in LIFO order if last is true or FIFO order if false. ''' if not self: raise KeyError('dictionary is empty') - key = next(reversed(self) if last else iter(self)) - value = self.pop(key) + root = self.__root + if last: # link_prev <--> link <--> root + link = root[PREV] + link_prev = link[PREV] + link_prev[NEXT] = root + root[PREV] = link_prev + else: # root <--> link <--> link_next + link = root[NEXT] + link_next = link[NEXT] + root[NEXT] = link_next + link_next[PREV] = root + key = link[KEY] + del self.__map[key] + value = dict_pop(self, key) return key, value + setdefault = MutableMapping.setdefault + update = MutableMapping.update + pop = MutableMapping.pop + keys = MutableMapping.keys + values = MutableMapping.values + items = MutableMapping.items + __ne__ = MutableMapping.__ne__ + def __repr__(self): 'od.__repr__() <==> repr(od)' if not self: From python-checkins at python.org Thu Sep 2 21:48:07 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 2 Sep 2010 21:48:07 +0200 (CEST) Subject: [Python-checkins] r84438 - in python/branches/py3k/Modules/_io: bytesio.c stringio.c Message-ID: <20100902194807.D0691EE984@mail.python.org> Author: antoine.pitrou Date: Thu Sep 2 21:48:07 2010 New Revision: 84438 Log: BytesIO.getvalue() and StringIO.getvalue() are METH_NOARGS. Modified: python/branches/py3k/Modules/_io/bytesio.c python/branches/py3k/Modules/_io/stringio.c Modified: python/branches/py3k/Modules/_io/bytesio.c ============================================================================== --- python/branches/py3k/Modules/_io/bytesio.c (original) +++ python/branches/py3k/Modules/_io/bytesio.c Thu Sep 2 21:48:07 2010 @@ -818,7 +818,7 @@ {"readline", (PyCFunction)bytesio_readline, METH_VARARGS, readline_doc}, {"readlines", (PyCFunction)bytesio_readlines, METH_VARARGS, readlines_doc}, {"read", (PyCFunction)bytesio_read, METH_VARARGS, read_doc}, - {"getvalue", (PyCFunction)bytesio_getvalue, METH_VARARGS, getval_doc}, + {"getvalue", (PyCFunction)bytesio_getvalue, METH_NOARGS, getval_doc}, {"seek", (PyCFunction)bytesio_seek, METH_VARARGS, seek_doc}, {"truncate", (PyCFunction)bytesio_truncate, METH_VARARGS, truncate_doc}, {"__getstate__", (PyCFunction)bytesio_getstate, METH_NOARGS, NULL}, Modified: python/branches/py3k/Modules/_io/stringio.c ============================================================================== --- python/branches/py3k/Modules/_io/stringio.c (original) +++ python/branches/py3k/Modules/_io/stringio.c Thu Sep 2 21:48:07 2010 @@ -827,7 +827,7 @@ static struct PyMethodDef stringio_methods[] = { {"close", (PyCFunction)stringio_close, METH_NOARGS, stringio_close_doc}, - {"getvalue", (PyCFunction)stringio_getvalue, METH_VARARGS, stringio_getvalue_doc}, + {"getvalue", (PyCFunction)stringio_getvalue, METH_NOARGS, stringio_getvalue_doc}, {"read", (PyCFunction)stringio_read, METH_VARARGS, stringio_read_doc}, {"readline", (PyCFunction)stringio_readline, METH_VARARGS, stringio_readline_doc}, {"tell", (PyCFunction)stringio_tell, METH_NOARGS, stringio_tell_doc}, From python-checkins at python.org Thu Sep 2 21:56:28 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 2 Sep 2010 21:56:28 +0200 (CEST) Subject: [Python-checkins] r84439 - in python/branches/py3k/Lib: collections.py functools.py Message-ID: <20100902195628.429FBEE99C@mail.python.org> Author: raymond.hettinger Date: Thu Sep 2 21:56:28 2010 New Revision: 84439 Log: Better method name. Tighten inner-loop with bound methods. Modified: python/branches/py3k/Lib/collections.py python/branches/py3k/Lib/functools.py Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Thu Sep 2 21:56:28 2010 @@ -173,7 +173,7 @@ def __del__(self): self.clear() # eliminate cyclical references - def _move_to_end(self, key, PREV=0, NEXT=1): + def _renew(self, key, PREV=0, NEXT=1): 'Fast version of self[key]=self.pop(key). Private method for internal use.' link = self.__map[key] link_prev = link[PREV] Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Thu Sep 2 21:56:28 2010 @@ -128,6 +128,7 @@ def decorating_function(user_function, tuple=tuple, sorted=sorted, len=len, KeyError=KeyError): cache = OrderedDict() # ordered least recent to most recent + cache_popitem, cache_renew = cache.popitem, cache._renew kwd_mark = object() # separate positional and keyword args lock = Lock() @@ -139,7 +140,7 @@ try: with lock: result = cache[key] - cache._move_to_end(key) # record recent use of this key + cache_renew(key) # record recent use of this key wrapper.hits += 1 except KeyError: result = user_function(*args, **kwds) @@ -147,7 +148,7 @@ cache[key] = result # record recent use of this key wrapper.misses += 1 if len(cache) > maxsize: - cache.popitem(0) # purge least recently used cache entry + cache_popitem(0) # purge least recently used cache entry return result def clear(): From python-checkins at python.org Thu Sep 2 21:58:35 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 2 Sep 2010 21:58:35 +0200 (CEST) Subject: [Python-checkins] r84440 - python/branches/py3k/Lib/functools.py Message-ID: <20100902195835.3CD4CEE99C@mail.python.org> Author: raymond.hettinger Date: Thu Sep 2 21:58:35 2010 New Revision: 84440 Log: Readability nit. Modified: python/branches/py3k/Lib/functools.py Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Thu Sep 2 21:58:35 2010 @@ -128,7 +128,8 @@ def decorating_function(user_function, tuple=tuple, sorted=sorted, len=len, KeyError=KeyError): cache = OrderedDict() # ordered least recent to most recent - cache_popitem, cache_renew = cache.popitem, cache._renew + cache_popitem = cache.popitem + cache_renew = cache._renew kwd_mark = object() # separate positional and keyword args lock = Lock() From python-checkins at python.org Fri Sep 3 00:21:32 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 3 Sep 2010 00:21:32 +0200 (CEST) Subject: [Python-checkins] r84441 - in python/branches/release31-maint: Modules/_io/bytesio.c Modules/_io/stringio.c Message-ID: <20100902222132.6D282EEB02@mail.python.org> Author: antoine.pitrou Date: Fri Sep 3 00:21:32 2010 New Revision: 84441 Log: Merged revisions 84438 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84438 | antoine.pitrou | 2010-09-02 21:48:07 +0200 (jeu., 02 sept. 2010) | 3 lines BytesIO.getvalue() and StringIO.getvalue() are METH_NOARGS. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Modules/_io/bytesio.c python/branches/release31-maint/Modules/_io/stringio.c Modified: python/branches/release31-maint/Modules/_io/bytesio.c ============================================================================== --- python/branches/release31-maint/Modules/_io/bytesio.c (original) +++ python/branches/release31-maint/Modules/_io/bytesio.c Fri Sep 3 00:21:32 2010 @@ -704,7 +704,7 @@ {"readline", (PyCFunction)bytesio_readline, METH_VARARGS, readline_doc}, {"readlines", (PyCFunction)bytesio_readlines, METH_VARARGS, readlines_doc}, {"read", (PyCFunction)bytesio_read, METH_VARARGS, read_doc}, - {"getvalue", (PyCFunction)bytesio_getvalue, METH_VARARGS, getval_doc}, + {"getvalue", (PyCFunction)bytesio_getvalue, METH_NOARGS, getval_doc}, {"seek", (PyCFunction)bytesio_seek, METH_VARARGS, seek_doc}, {"truncate", (PyCFunction)bytesio_truncate, METH_VARARGS, truncate_doc}, {NULL, NULL} /* sentinel */ Modified: python/branches/release31-maint/Modules/_io/stringio.c ============================================================================== --- python/branches/release31-maint/Modules/_io/stringio.c (original) +++ python/branches/release31-maint/Modules/_io/stringio.c Fri Sep 3 00:21:32 2010 @@ -698,7 +698,7 @@ static struct PyMethodDef stringio_methods[] = { {"close", (PyCFunction)stringio_close, METH_NOARGS, stringio_close_doc}, - {"getvalue", (PyCFunction)stringio_getvalue, METH_VARARGS, stringio_getvalue_doc}, + {"getvalue", (PyCFunction)stringio_getvalue, METH_NOARGS, stringio_getvalue_doc}, {"read", (PyCFunction)stringio_read, METH_VARARGS, stringio_read_doc}, {"readline", (PyCFunction)stringio_readline, METH_VARARGS, stringio_readline_doc}, {"tell", (PyCFunction)stringio_tell, METH_NOARGS, stringio_tell_doc}, From python-checkins at python.org Fri Sep 3 00:23:19 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 3 Sep 2010 00:23:19 +0200 (CEST) Subject: [Python-checkins] r84442 - in python/branches/release27-maint: Modules/_io/bytesio.c Modules/_io/stringio.c Message-ID: <20100902222319.7ACDBEEA5D@mail.python.org> Author: antoine.pitrou Date: Fri Sep 3 00:23:19 2010 New Revision: 84442 Log: Merged revisions 84438 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84438 | antoine.pitrou | 2010-09-02 21:48:07 +0200 (jeu., 02 sept. 2010) | 3 lines BytesIO.getvalue() and StringIO.getvalue() are METH_NOARGS. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Modules/_io/bytesio.c python/branches/release27-maint/Modules/_io/stringio.c Modified: python/branches/release27-maint/Modules/_io/bytesio.c ============================================================================== --- python/branches/release27-maint/Modules/_io/bytesio.c (original) +++ python/branches/release27-maint/Modules/_io/bytesio.c Fri Sep 3 00:23:19 2010 @@ -825,7 +825,7 @@ {"readline", (PyCFunction)bytesio_readline, METH_VARARGS, readline_doc}, {"readlines", (PyCFunction)bytesio_readlines, METH_VARARGS, readlines_doc}, {"read", (PyCFunction)bytesio_read, METH_VARARGS, read_doc}, - {"getvalue", (PyCFunction)bytesio_getvalue, METH_VARARGS, getval_doc}, + {"getvalue", (PyCFunction)bytesio_getvalue, METH_NOARGS, getval_doc}, {"seek", (PyCFunction)bytesio_seek, METH_VARARGS, seek_doc}, {"truncate", (PyCFunction)bytesio_truncate, METH_VARARGS, truncate_doc}, {"__getstate__", (PyCFunction)bytesio_getstate, METH_NOARGS, NULL}, Modified: python/branches/release27-maint/Modules/_io/stringio.c ============================================================================== --- python/branches/release27-maint/Modules/_io/stringio.c (original) +++ python/branches/release27-maint/Modules/_io/stringio.c Fri Sep 3 00:23:19 2010 @@ -809,7 +809,7 @@ static struct PyMethodDef stringio_methods[] = { {"close", (PyCFunction)stringio_close, METH_NOARGS, stringio_close_doc}, - {"getvalue", (PyCFunction)stringio_getvalue, METH_VARARGS, stringio_getvalue_doc}, + {"getvalue", (PyCFunction)stringio_getvalue, METH_NOARGS, stringio_getvalue_doc}, {"read", (PyCFunction)stringio_read, METH_VARARGS, stringio_read_doc}, {"readline", (PyCFunction)stringio_readline, METH_VARARGS, stringio_readline_doc}, {"tell", (PyCFunction)stringio_tell, METH_NOARGS, stringio_tell_doc}, From solipsis at pitrou.net Fri Sep 3 05:02:36 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 03 Sep 2010 05:02:36 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84440): sum=0 Message-ID: py3k results for svn r84440 (hg cset 38425b522554) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogHYQbeb', '-x'] From python-checkins at python.org Fri Sep 3 05:55:50 2010 From: python-checkins at python.org (fred.drake) Date: Fri, 3 Sep 2010 05:55:50 +0200 (CEST) Subject: [Python-checkins] r84443 - in python/branches/release27-maint: Lib/ConfigParser.py Lib/test/test_cfgparser.py Misc/NEWS Message-ID: <20100903035550.ED98FEE99E@mail.python.org> Author: fred.drake Date: Fri Sep 3 05:55:50 2010 New Revision: 84443 Log: fix output from RawConfigParser.write and ConfigParser.write for None values (http://bugs.python.org/issue7005) Modified: python/branches/release27-maint/Lib/ConfigParser.py python/branches/release27-maint/Lib/test/test_cfgparser.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/ConfigParser.py ============================================================================== --- python/branches/release27-maint/Lib/ConfigParser.py (original) +++ python/branches/release27-maint/Lib/ConfigParser.py Fri Sep 3 05:55:50 2010 @@ -400,7 +400,7 @@ for (key, value) in self._sections[section].items(): if key == "__name__": continue - if value is not None: + if (value is not None) or (self._optcre == self.OPTCRE): key = " = ".join((key, str(value).replace('\n', '\n\t'))) fp.write("%s\n" % (key)) fp.write("\n") Modified: python/branches/release27-maint/Lib/test/test_cfgparser.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_cfgparser.py (original) +++ python/branches/release27-maint/Lib/test/test_cfgparser.py Fri Sep 3 05:55:50 2010 @@ -530,6 +530,33 @@ allow_no_value = True +class Issue7005TestCase(unittest.TestCase): + """Test output when None is set() as a value and allow_no_value == False. + + http://bugs.python.org/issue7005 + + """ + + expected_output = "[section]\noption = None\n\n" + + def prepare(self, config_class): + # This is the default, but that's the point. + cp = config_class(allow_no_value=False) + cp.add_section("section") + cp.set("section", "option", None) + sio = StringIO.StringIO() + cp.write(sio) + return sio.getvalue() + + def test_none_as_value_stringified(self): + output = self.prepare(ConfigParser.ConfigParser) + self.assertEqual(output, self.expected_output) + + def test_none_as_value_stringified_raw(self): + output = self.prepare(ConfigParser.RawConfigParser) + self.assertEqual(output, self.expected_output) + + class SortedTestCase(RawConfigParserTestCase): def newconfig(self, defaults=None): self.cf = self.config_class(defaults=defaults, dict_type=SortedDict) @@ -563,6 +590,7 @@ SafeConfigParserTestCase, SafeConfigParserTestCaseNoValue, SortedTestCase, + Issue7005TestCase, ) Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Fri Sep 3 05:55:50 2010 @@ -36,6 +36,9 @@ Library ------- +- Issue #7005: Fixed output of None values for RawConfigParser.write and + ConfigParser.write. + - Issue #808164: Fixed socket.close to avoid references to globals, to avoid issues when socket.close is called from a __del__ method. From python-checkins at python.org Fri Sep 3 06:22:36 2010 From: python-checkins at python.org (fred.drake) Date: Fri, 3 Sep 2010 06:22:36 +0200 (CEST) Subject: [Python-checkins] r84444 - in python/branches/py3k: Lib/configparser.py Lib/test/test_cfgparser.py Misc/NEWS Message-ID: <20100903042236.B6B5BEE99E@mail.python.org> Author: fred.drake Date: Fri Sep 3 06:22:36 2010 New Revision: 84444 Log: fix output from RawConfigParser.write and ConfigParser.write for None values (http://bugs.python.org/issue7005) (merged r84443 from the release27-mmaint branch, with changes to reflect changes in Python 3) Modified: python/branches/py3k/Lib/configparser.py python/branches/py3k/Lib/test/test_cfgparser.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/configparser.py ============================================================================== --- python/branches/py3k/Lib/configparser.py (original) +++ python/branches/py3k/Lib/configparser.py Fri Sep 3 06:22:36 2010 @@ -637,7 +637,7 @@ for key, value in section_items: if key == "__name__": continue - if value is not None: + if (value is not None) or (self._optcre == self.OPTCRE): value = delimiter + str(value).replace('\n', '\n\t') else: value = "" Modified: python/branches/py3k/Lib/test/test_cfgparser.py ============================================================================== --- python/branches/py3k/Lib/test/test_cfgparser.py (original) +++ python/branches/py3k/Lib/test/test_cfgparser.py Fri Sep 3 06:22:36 2010 @@ -755,6 +755,34 @@ with self.assertRaises(UnicodeDecodeError): cf.read(tricky, encoding='ascii') + +class Issue7005TestCase(unittest.TestCase): + """Test output when None is set() as a value and allow_no_value == False. + + http://bugs.python.org/issue7005 + + """ + + expected_output = "[section]\noption = None\n\n" + + def prepare(self, config_class): + # This is the default, but that's the point. + cp = config_class(allow_no_value=False) + cp.add_section("section") + cp.set("section", "option", None) + sio = io.StringIO() + cp.write(sio) + return sio.getvalue() + + def test_none_as_value_stringified(self): + output = self.prepare(configparser.ConfigParser) + self.assertEqual(output, self.expected_output) + + def test_none_as_value_stringified_raw(self): + output = self.prepare(configparser.RawConfigParser) + self.assertEqual(output, self.expected_output) + + class SortedTestCase(RawConfigParserTestCase): dict_type = SortedDict @@ -811,6 +839,7 @@ SafeConfigParserTestCaseNoValue, SafeConfigParserTestCaseTrickyFile, SortedTestCase, + Issue7005TestCase, StrictTestCase, CompatibleTestCase, ) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Sep 3 06:22:36 2010 @@ -155,6 +155,9 @@ Library ------- +- Issue #7005: Fixed output of None values for RawConfigParser.write and + ConfigParser.write. + - Issue #8990: array.fromstring() and array.tostring() get renamed to frombytes() and tobytes(), respectively, to avoid confusion. Furthermore, array.frombytes(), array.extend() as well as the array.array() From python-checkins at python.org Fri Sep 3 11:06:07 2010 From: python-checkins at python.org (vinay.sajip) Date: Fri, 3 Sep 2010 11:06:07 +0200 (CEST) Subject: [Python-checkins] r84445 - python/branches/release27-maint/Lib/logging/handlers.py Message-ID: <20100903090607.E5FFBEE9A3@mail.python.org> Author: vinay.sajip Date: Fri Sep 3 11:06:07 2010 New Revision: 84445 Log: Issue #7077: Backported fix from py3k. Modified: python/branches/release27-maint/Lib/logging/handlers.py Modified: python/branches/release27-maint/Lib/logging/handlers.py ============================================================================== --- python/branches/release27-maint/Lib/logging/handlers.py (original) +++ python/branches/release27-maint/Lib/logging/handlers.py Fri Sep 3 11:06:07 2010 @@ -786,20 +786,19 @@ The record is formatted, and then sent to the syslog server. If exception information is present, it is NOT sent to the server. """ - msg = self.format(record) + msg = self.format(record) + '\000' """ We need to convert record level to lowercase, maybe this will change in the future. """ - msg = self.log_format_string % ( - self.encodePriority(self.facility, - self.mapPriority(record.levelname)), - msg) - # Treat unicode messages as required by RFC 5424 - if _unicode and type(msg) is unicode: + prio = '<%d>' % self.encodePriority(self.facility, + self.mapPriority(record.levelname)) + # Message is a string. Convert to bytes as required by RFC 5424 + if type(msg) is unicode: msg = msg.encode('utf-8') if codecs: msg = codecs.BOM_UTF8 + msg + msg = prio + msg try: if self.unixsocket: try: From python-checkins at python.org Fri Sep 3 11:26:14 2010 From: python-checkins at python.org (armin.rigo) Date: Fri, 3 Sep 2010 11:26:14 +0200 (CEST) Subject: [Python-checkins] r84446 - python/branches/release27-maint/Lib/test/crashers/gc_has_finalizer.py Message-ID: <20100903092614.52612EEA53@mail.python.org> Author: armin.rigo Date: Fri Sep 3 11:26:14 2010 New Revision: 84446 Log: An example that shows that _PyInstance_Lookup() does not fulfill its documented purpose. Added: python/branches/release27-maint/Lib/test/crashers/gc_has_finalizer.py (contents, props changed) Added: python/branches/release27-maint/Lib/test/crashers/gc_has_finalizer.py ============================================================================== --- (empty file) +++ python/branches/release27-maint/Lib/test/crashers/gc_has_finalizer.py Fri Sep 3 11:26:14 2010 @@ -0,0 +1,36 @@ +""" +The gc module can still invoke arbitrary Python code and crash. +This is an attack against _PyInstance_Lookup(), which is documented +as follows: + + The point of this routine is that it never calls arbitrary Python + code, so is always "safe": all it does is dict lookups. + +But of course dict lookups can call arbitrary Python code. +The following code causes mutation of the object graph during +the call to has_finalizer() in gcmodule.c, and that might +segfault. +""" + +import gc + + +class A: + def __hash__(self): + return hash("__del__") + def __eq__(self, other): + del self.other + return False + +a = A() +b = A() + +a.__dict__[b] = 'A' + +a.other = b +b.other = a + +gc.collect() +del a, b + +gc.collect() From python-checkins at python.org Fri Sep 3 12:00:51 2010 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 3 Sep 2010 12:00:51 +0200 (CEST) Subject: [Python-checkins] r84447 - in python/branches/py3k: Lib/test/test_set.py Objects/setobject.c Message-ID: <20100903100051.16CA7EEA4D@mail.python.org> Author: raymond.hettinger Date: Fri Sep 3 12:00:50 2010 New Revision: 84447 Log: Issue 8420: Fix obscure set crashers. Modified: python/branches/py3k/Lib/test/test_set.py python/branches/py3k/Objects/setobject.c Modified: python/branches/py3k/Lib/test/test_set.py ============================================================================== --- python/branches/py3k/Lib/test/test_set.py (original) +++ python/branches/py3k/Lib/test/test_set.py Fri Sep 3 12:00:50 2010 @@ -1660,6 +1660,39 @@ self.assertRaises(TypeError, getattr(set('january'), methname), N(data)) self.assertRaises(ZeroDivisionError, getattr(set('january'), methname), E(data)) +class bad_eq: + def __eq__(self, other): + if be_bad: + set2.clear() + raise ZeroDivisionError + return self is other + def __hash__(self): + return 0 + +class bad_dict_clear: + def __eq__(self, other): + if be_bad: + dict2.clear() + return self is other + def __hash__(self): + return 0 + +class Test_Weird_Bugs(unittest.TestCase): + def test_8420_set_merge(self): + # This used to segfault + global be_bad, set2, dict2 + be_bad = False + set1 = {bad_eq()} + set2 = {bad_eq() for i in range(75)} + be_bad = True + self.assertRaises(ZeroDivisionError, set1.update, set2) + + be_bad = False + set1 = {bad_dict_clear()} + dict2 = {bad_dict_clear(): None} + be_bad = True + set1.symmetric_difference_update(dict2) + # Application tests (based on David Eppstein's graph recipes ==================================== def powerset(U): @@ -1804,6 +1837,7 @@ TestIdentities, TestVariousIteratorArgs, TestGraphs, + Test_Weird_Bugs, ) support.run_unittest(*test_classes) Modified: python/branches/py3k/Objects/setobject.c ============================================================================== --- python/branches/py3k/Objects/setobject.c (original) +++ python/branches/py3k/Objects/setobject.c Fri Sep 3 12:00:50 2010 @@ -364,12 +364,13 @@ set_add_entry(register PySetObject *so, setentry *entry) { register Py_ssize_t n_used; + PyObject *key = entry->key; assert(so->fill <= so->mask); /* at least one empty slot */ n_used = so->used; - Py_INCREF(entry->key); - if (set_insert_key(so, entry->key, (long) entry->hash) == -1) { - Py_DECREF(entry->key); + Py_INCREF(key); + if (set_insert_key(so, key, (long) entry->hash) == -1) { + Py_DECREF(key); return -1; } if (!(so->used > n_used && so->fill*3 >= (so->mask+1)*2)) @@ -637,6 +638,7 @@ set_merge(PySetObject *so, PyObject *otherset) { PySetObject *other; + PyObject *key; register Py_ssize_t i; register setentry *entry; @@ -657,11 +659,12 @@ } for (i = 0; i <= other->mask; i++) { entry = &other->table[i]; - if (entry->key != NULL && - entry->key != dummy) { - Py_INCREF(entry->key); - if (set_insert_key(so, entry->key, (long) entry->hash) == -1) { - Py_DECREF(entry->key); + key = entry->key; + if (key != NULL && + key != dummy) { + Py_INCREF(key); + if (set_insert_key(so, key, (long) entry->hash) == -1) { + Py_DECREF(key); return -1; } } @@ -1642,15 +1645,22 @@ while (_PyDict_Next(other, &pos, &key, &value, &hash)) { setentry an_entry; + Py_INCREF(key); an_entry.hash = hash; an_entry.key = key; + rv = set_discard_entry(so, &an_entry); - if (rv == -1) + if (rv == -1) { + Py_DECREF(key); return NULL; + } if (rv == DISCARD_NOTFOUND) { - if (set_add_entry(so, &an_entry) == -1) + if (set_add_entry(so, &an_entry) == -1) { + Py_DECREF(key); return NULL; + } } + Py_DECREF(key); } Py_RETURN_NONE; } From python-checkins at python.org Fri Sep 3 12:52:56 2010 From: python-checkins at python.org (georg.brandl) Date: Fri, 3 Sep 2010 12:52:56 +0200 (CEST) Subject: [Python-checkins] r84448 - python/branches/py3k/Objects/setobject.c Message-ID: <20100903105256.0DED3EE9D0@mail.python.org> Author: georg.brandl Date: Fri Sep 3 12:52:55 2010 New Revision: 84448 Log: Reindent. Modified: python/branches/py3k/Objects/setobject.c Modified: python/branches/py3k/Objects/setobject.c ============================================================================== --- python/branches/py3k/Objects/setobject.c (original) +++ python/branches/py3k/Objects/setobject.c Fri Sep 3 12:52:55 2010 @@ -1650,7 +1650,7 @@ an_entry.key = key; rv = set_discard_entry(so, &an_entry); - if (rv == -1) { + if (rv == -1) { Py_DECREF(key); return NULL; } @@ -1660,7 +1660,7 @@ return NULL; } } - Py_DECREF(key); + Py_DECREF(key); } Py_RETURN_NONE; } From python-checkins at python.org Fri Sep 3 13:11:43 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Fri, 3 Sep 2010 13:11:43 +0200 (CEST) Subject: [Python-checkins] r84449 - python/branches/py3k/Lib/test/test_socket.py Message-ID: <20100903111143.E58B7EEA16@mail.python.org> Author: daniel.stutzbach Date: Fri Sep 3 13:11:43 2010 New Revision: 84449 Log: fromfd exists on Windows now Modified: python/branches/py3k/Lib/test/test_socket.py Modified: python/branches/py3k/Lib/test/test_socket.py ============================================================================== --- python/branches/py3k/Lib/test/test_socket.py (original) +++ python/branches/py3k/Lib/test/test_socket.py Fri Sep 3 13:11:43 2010 @@ -708,8 +708,6 @@ def testFromFd(self): # Testing fromfd() - if not hasattr(socket, "fromfd"): - return # On Windows, this doesn't exist fd = self.cli_conn.fileno() sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) msg = sock.recv(1024) From python-checkins at python.org Fri Sep 3 14:38:33 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Fri, 3 Sep 2010 14:38:33 +0200 (CEST) Subject: [Python-checkins] r84450 - in python/branches/py3k: Misc/NEWS Modules/socketmodule.c Message-ID: <20100903123833.4A192FBD4@mail.python.org> Author: daniel.stutzbach Date: Fri Sep 3 14:38:33 2010 New Revision: 84450 Log: Fix Issue9753: socket.dup() does not always work right on Windows Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/socketmodule.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Sep 3 14:38:33 2010 @@ -155,6 +155,9 @@ Library ------- +- Issue #9753: Fixed socket.dup, which did not always work correctly + on Windows. + - Issue #7005: Fixed output of None values for RawConfigParser.write and ConfigParser.write. Modified: python/branches/py3k/Modules/socketmodule.c ============================================================================== --- python/branches/py3k/Modules/socketmodule.c (original) +++ python/branches/py3k/Modules/socketmodule.c Fri Sep 3 14:38:33 2010 @@ -351,16 +351,13 @@ static SOCKET dup_socket(SOCKET handle) { - HANDLE newhandle; + WSAPROTOCOL_INFO info; - if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)handle, - GetCurrentProcess(), &newhandle, - 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - WSASetLastError(GetLastError()); + if (WSADuplicateSocket(handle, GetCurrentProcessId(), &info)) return INVALID_SOCKET; - } - return (SOCKET)newhandle; + + return WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, &info, 0, 0); } #define SOCKETCLOSE closesocket #else From python-checkins at python.org Fri Sep 3 14:42:06 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Fri, 3 Sep 2010 14:42:06 +0200 (CEST) Subject: [Python-checkins] r84451 - in python/branches/release31-maint: Misc/NEWS Modules/socketmodule.c Message-ID: <20100903124206.7F34DEE99A@mail.python.org> Author: daniel.stutzbach Date: Fri Sep 3 14:42:06 2010 New Revision: 84451 Log: Merged revisions 84450 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84450 | daniel.stutzbach | 2010-09-03 07:38:33 -0500 (Fri, 03 Sep 2010) | 1 line Fix Issue9753: socket.dup() does not always work right on Windows ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/socketmodule.c Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Fri Sep 3 14:42:06 2010 @@ -105,6 +105,9 @@ Library ------- +- Issue #9753: Fixed socket.dup, which did not always work correctly + on Windows. + - Issue #1868: Eliminate subtle timing issues in thread-local objects by getting rid of the cached copy of thread-local attribute dictionary. Modified: python/branches/release31-maint/Modules/socketmodule.c ============================================================================== --- python/branches/release31-maint/Modules/socketmodule.c (original) +++ python/branches/release31-maint/Modules/socketmodule.c Fri Sep 3 14:42:06 2010 @@ -351,16 +351,13 @@ static SOCKET dup_socket(SOCKET handle) { - HANDLE newhandle; + WSAPROTOCOL_INFO info; - if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)handle, - GetCurrentProcess(), &newhandle, - 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - WSASetLastError(GetLastError()); + if (WSADuplicateSocket(handle, GetCurrentProcessId(), &info)) return INVALID_SOCKET; - } - return (SOCKET)newhandle; + + return WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, &info, 0, 0); } #define SOCKETCLOSE closesocket #else From python-checkins at python.org Fri Sep 3 18:12:14 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 3 Sep 2010 18:12:14 +0200 (CEST) Subject: [Python-checkins] r84452 - python/branches/py3k/Misc/NEWS Message-ID: <20100903161214.94BF8FC5E@mail.python.org> Author: antoine.pitrou Date: Fri Sep 3 18:12:14 2010 New Revision: 84452 Log: Fix NEWS entry. Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Sep 3 18:12:14 2010 @@ -378,8 +378,9 @@ Build ----- -- Issue #3101: Helper functions _add_one_to_C() and _add_one_to_F() become - _Py_add_one_to_C() and _Py_add_one_to_F(), respectively. +- Issue #3101: Helper functions _add_one_to_index_C() and + _add_one_to_index_F() become _Py_add_one_to_index_C() and + _Py_add_one_to_index_F(), respectively. - Issue #9700: define HAVE_BROKEN_POSIX_SEMAPHORES under AIX 6.x. Patch by S?bastien Sabl?. From python-checkins at python.org Fri Sep 3 18:14:15 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 3 Sep 2010 18:14:15 +0200 (CEST) Subject: [Python-checkins] r84453 - in python/branches/release31-maint: Misc/NEWS Message-ID: <20100903161415.16FE9FC5E@mail.python.org> Author: antoine.pitrou Date: Fri Sep 3 18:14:14 2010 New Revision: 84453 Log: Merged revisions 84452 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84452 | antoine.pitrou | 2010-09-03 18:12:14 +0200 (ven., 03 sept. 2010) | 3 lines Fix NEWS entry. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Fri Sep 3 18:14:14 2010 @@ -523,8 +523,9 @@ Build ----- -- Issue #3101: Helper functions _add_one_to_C() and _add_one_to_F() become - _Py_add_one_to_C() and _Py_add_one_to_F(), respectively. +- Issue #3101: Helper functions _add_one_to_index_C() and + _add_one_to_index_F() become _Py_add_one_to_index_C() and + _Py_add_one_to_index_F(), respectively. - Issue #9700: define HAVE_BROKEN_POSIX_SEMAPHORES under AIX 6.x. Patch by S?bastien Sabl?. From python-checkins at python.org Fri Sep 3 18:15:18 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 3 Sep 2010 18:15:18 +0200 (CEST) Subject: [Python-checkins] r84454 - in python/branches/release27-maint: Misc/NEWS Message-ID: <20100903161518.1FD12FC5E@mail.python.org> Author: antoine.pitrou Date: Fri Sep 3 18:15:17 2010 New Revision: 84454 Log: Merged revisions 84452 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84452 | antoine.pitrou | 2010-09-03 18:12:14 +0200 (ven., 03 sept. 2010) | 3 lines Fix NEWS entry. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Fri Sep 3 18:15:17 2010 @@ -266,8 +266,9 @@ Build ----- -- Issue #3101: Helper functions _add_one_to_C() and _add_one_to_F() become - _Py_add_one_to_C() and _Py_add_one_to_F(), respectively. +- Issue #3101: Helper functions _add_one_to_index_C() and + _add_one_to_index_F() become _Py_add_one_to_index_C() and + _Py_add_one_to_index_F(), respectively. - Issue #9700: define HAVE_BROKEN_POSIX_SEMAPHORES under AIX 6.x. Patch by S?bastien Sabl?. From python-checkins at python.org Fri Sep 3 18:18:00 2010 From: python-checkins at python.org (victor.stinner) Date: Fri, 3 Sep 2010 18:18:00 +0200 (CEST) Subject: [Python-checkins] r84455 - in python/branches/py3k: Include/unicodeobject.h Objects/unicodeobject.c Message-ID: <20100903161800.69160EE98F@mail.python.org> Author: victor.stinner Date: Fri Sep 3 18:18:00 2010 New Revision: 84455 Log: Rename PyUnicode_strdup() to PyUnicode_AsUnicodeCopy() Modified: python/branches/py3k/Include/unicodeobject.h python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Include/unicodeobject.h ============================================================================== --- python/branches/py3k/Include/unicodeobject.h (original) +++ python/branches/py3k/Include/unicodeobject.h Fri Sep 3 18:18:00 2010 @@ -1607,7 +1607,7 @@ and raise a MemoryError exception on memory allocation failure, otherwise return a new allocated buffer (use PyMem_Free() to free the buffer). */ -PyAPI_FUNC(Py_UNICODE*) PyUnicode_strdup( +PyAPI_FUNC(Py_UNICODE*) PyUnicode_AsUnicodeCopy( PyObject *unicode ); Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Fri Sep 3 18:18:00 2010 @@ -10015,7 +10015,7 @@ } Py_UNICODE* -PyUnicode_strdup(PyObject *object) +PyUnicode_AsUnicodeCopy(PyObject *object) { PyUnicodeObject *unicode = (PyUnicodeObject *)object; Py_UNICODE *copy; From python-checkins at python.org Fri Sep 3 18:23:29 2010 From: python-checkins at python.org (victor.stinner) Date: Fri, 3 Sep 2010 18:23:29 +0200 (CEST) Subject: [Python-checkins] r84456 - python/branches/py3k/Doc/c-api/unicode.rst Message-ID: <20100903162329.A386CEE9BC@mail.python.org> Author: victor.stinner Date: Fri Sep 3 18:23:29 2010 New Revision: 84456 Log: Document PyUnicode_AsUnicodeCopy() Modified: python/branches/py3k/Doc/c-api/unicode.rst Modified: python/branches/py3k/Doc/c-api/unicode.rst ============================================================================== --- python/branches/py3k/Doc/c-api/unicode.rst (original) +++ python/branches/py3k/Doc/c-api/unicode.rst Fri Sep 3 18:23:29 2010 @@ -335,6 +335,14 @@ buffer, *NULL* if *unicode* is not a Unicode object. +.. cfunction:: Py_UNICODE* PyUnicode_AsUnicodeCopy(PyObject *unicode) + + Create a copy of a unicode string ending with a nul character. Return *NULL* + and raise a :exc:`MemoryError` exception on memory allocation failure, + otherwise return a new allocated buffer (use :cfunc:`PyMem_Free` to free the + buffer). + + .. cfunction:: Py_ssize_t PyUnicode_GetSize(PyObject *unicode) Return the length of the Unicode object. From python-checkins at python.org Fri Sep 3 19:07:56 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 3 Sep 2010 19:07:56 +0200 (CEST) Subject: [Python-checkins] r84457 - peps/trunk/pep-3151.txt Message-ID: <20100903170756.32392EE9F6@mail.python.org> Author: antoine.pitrou Date: Fri Sep 3 19:07:56 2010 New Revision: 84457 Log: Add "Error" at the end of class names. Propose two new intermediate classes (ConnectionError and FileSystemError) Add a small diagram. Modified: peps/trunk/pep-3151.txt Modified: peps/trunk/pep-3151.txt ============================================================================== --- peps/trunk/pep-3151.txt (original) +++ peps/trunk/pep-3151.txt Fri Sep 3 19:07:56 2010 @@ -154,7 +154,7 @@ try: os.remove(filename) - except FileNotFound: + except FileNotFoundError: pass @@ -330,44 +330,76 @@ The following tentative list of subclasses, along with a description and the list of errnos mapped to them, is submitted to discussion: -* ``FileAlreadyExists``: trying to create a file or directory which already - exists (EEXIST) +* ``FileAlreadyExistsError``: trying to create a file or directory which + already exists (EEXIST) -* ``FileNotFound``: for all circumstances where a file and directory is +* ``FileNotFoundError``: for all circumstances where a file and directory is requested but doesn't exist (ENOENT) -* ``IsADirectory``: file-level operation (open(), os.remove()...) requested - on a directory (EISDIR) +* ``IsADirectoryError``: file-level operation (open(), os.remove()...) + requested on a directory (EISDIR) -* ``NotADirectory``: directory-level operation requested on something else - (ENOTDIR) +* ``NotADirectoryError``: directory-level operation requested on something + else (ENOTDIR) -* ``PermissionDenied``: trying to run an operation without the adequate access - rights - for example filesystem permissions (EACCESS, optionally EPERM) +* ``PermissionError``: trying to run an operation without the adequate access + rights - for example filesystem permissions (EACCESS, EPERM) * ``BlockingIOError``: an operation would block on an object (e.g. socket) set for non-blocking operation (EAGAIN, EALREADY, EWOULDBLOCK, EINPROGRESS); this is the existing ``io.BlockingIOError`` with an extended role -* ``BadFileDescriptor``: operation on an invalid file descriptor (EBADF); +* ``FileDescriptorError``: operation on an invalid file descriptor (EBADF); the default error message could point out that most causes are that an existing file descriptor has been closed -* ``ConnectionAborted``: connection attempt aborted by peer (ECONNABORTED) +* ``ConnectionAbortedError``: connection attempt aborted by peer (ECONNABORTED) -* ``ConnectionRefused``: connection reset by peer (ECONNREFUSED) +* ``ConnectionRefusedError``: connection reset by peer (ECONNREFUSED) -* ``ConnectionReset``: connection reset by peer (ECONNRESET) +* ``ConnectionResetError``: connection reset by peer (ECONNRESET) -* ``TimeoutError``: connection timed out (ECONNTIMEOUT); this could be re-cast +* ``TimeoutError``: connection timed out (ECONNTIMEOUT); this can be re-cast as a generic timeout exception, useful for other types of timeout (for example in Lock.acquire()) -This list assumes `Step 1`_ is accepted in full; the exception classes -described above would all derive from the now unified exception type -IOError. It will need reworking if a partial version of step 1 is accepted -instead (again, see appendix A for the current distribution of errnos -and exception types). +In addition, the following exception class are proposed for inclusion: + +* ``ConnectionError``: a base class for ``ConnectionAbortedError``, + ``ConnectionRefusedError`` and ``ConnectionResetError`` + +* ``FileSystemError``: a base class for ``FileAlreadyExistsError``, + ``FileNotFoundError``, ``IsADirectoryError`` and ``NotADirectoryError`` + +The following drawing tries to sum up the proposed additions, along with +the corresponding errno values (where applicable). The root of the +sub-hierarchy (IOError, assuming `Step 1`_ is accepted in full) is not +shown:: + + +-- BlockingIOError EAGAIN, EALREADY, EWOULDBLOCK, EINPROGRESS + +-- ConnectionError + +-- ConnectionAbortedError ECONNABORTED + +-- ConnectionRefusedError ECONNREFUSED + +-- ConnectionResetError ECONNRESET + +-- FileDescriptorError EBADF + +-- FileSystemError + +-- FileAlreadyExistsError EEXIST + +-- FileNotFoundError ENOENT + +-- IsADirectoryError EISDIR + +-- NotADirectoryError ENOTDIR + +-- PermissionError EACCESS, EPERM + +-- TimeoutError ECONNTIMEOUT + +Naming +------ + +Various naming controversies can arise. One of them is whether all +exception class names should end in "``Error``". In favour is consistency +with the rest of the exception hiearchy, against is concision (especially +with long names such as ``FileAlreadyExistsError``). + +Another cosmetic issue is whether ``FileSystemError`` should be spelled +``FilesystemError`` instead. Exception attributes -------------------- From python-checkins at python.org Fri Sep 3 20:30:30 2010 From: python-checkins at python.org (barry.warsaw) Date: Fri, 3 Sep 2010 20:30:30 +0200 (CEST) Subject: [Python-checkins] r84458 - in python/branches/py3k: Lib/distutils/tests/test_build_ext.py Makefile.pre.in Python/dynload_shlib.c configure configure.in pyconfig.h.in Message-ID: <20100903183030.B71ACEEA93@mail.python.org> Author: barry.warsaw Date: Fri Sep 3 20:30:30 2010 New Revision: 84458 Log: PEP 3149 is accepted. http://mail.python.org/pipermail/python-dev/2010-September/103408.html Modified: python/branches/py3k/Lib/distutils/tests/test_build_ext.py python/branches/py3k/Makefile.pre.in python/branches/py3k/Python/dynload_shlib.c python/branches/py3k/configure python/branches/py3k/configure.in python/branches/py3k/pyconfig.h.in Modified: python/branches/py3k/Lib/distutils/tests/test_build_ext.py ============================================================================== --- python/branches/py3k/Lib/distutils/tests/test_build_ext.py (original) +++ python/branches/py3k/Lib/distutils/tests/test_build_ext.py Fri Sep 3 20:30:30 2010 @@ -323,8 +323,8 @@ finally: os.chdir(old_wd) self.assertTrue(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], - sysconfig.get_config_var('SO')) + so_ext = sysconfig.get_config_var('SO') + self.assertTrue(so_file.endswith(so_ext)) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, other_tmp_dir) @@ -333,8 +333,7 @@ cmd.run() so_file = cmd.get_outputs()[0] self.assertTrue(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], - sysconfig.get_config_var('SO')) + self.assertTrue(so_file.endswith(so_ext)) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, cmd.build_lib) Modified: python/branches/py3k/Makefile.pre.in ============================================================================== --- python/branches/py3k/Makefile.pre.in (original) +++ python/branches/py3k/Makefile.pre.in Fri Sep 3 20:30:30 2010 @@ -35,6 +35,7 @@ AR= @AR@ RANLIB= @RANLIB@ SVNVERSION= @SVNVERSION@ +SOABI= @SOABI@ GNULD= @GNULD@ @@ -559,6 +560,11 @@ Modules/python.o: $(srcdir)/Modules/python.c $(MAINCC) -c $(PY_CORE_CFLAGS) -o $@ $(srcdir)/Modules/python.c +Python/dynload_shlib.o: $(srcdir)/Python/dynload_shlib.c Makefile + $(CC) -c $(PY_CORE_CFLAGS) \ + -DSOABI='"$(SOABI)"' \ + -o $@ $(srcdir)/Python/dynload_shlib.c + $(IO_OBJS): $(IO_H) # Use a stamp file to prevent make -j invoking pgen twice Modified: python/branches/py3k/Python/dynload_shlib.c ============================================================================== --- python/branches/py3k/Python/dynload_shlib.c (original) +++ python/branches/py3k/Python/dynload_shlib.c Fri Sep 3 20:30:30 2010 @@ -30,27 +30,34 @@ #define LEAD_UNDERSCORE "" #endif +/* The .so extension module ABI tag, supplied by the Makefile via + Makefile.pre.in and configure. This is used to discriminate between + incompatible .so files so that extensions for different Python builds can + live in the same directory. E.g. foomodule.cpython-32.so +*/ const struct filedescr _PyImport_DynLoadFiletab[] = { #ifdef __CYGWIN__ {".dll", "rb", C_EXTENSION}, {"module.dll", "rb", C_EXTENSION}, -#else +#else /* !__CYGWIN__ */ #if defined(PYOS_OS2) && defined(PYCC_GCC) {".pyd", "rb", C_EXTENSION}, {".dll", "rb", C_EXTENSION}, -#else +#else /* !(defined(PYOS_OS2) && defined(PYCC_GCC)) */ #ifdef __VMS {".exe", "rb", C_EXTENSION}, {".EXE", "rb", C_EXTENSION}, {"module.exe", "rb", C_EXTENSION}, {"MODULE.EXE", "rb", C_EXTENSION}, -#else +#else /* !__VMS */ + {"." SOABI ".so", "rb", C_EXTENSION}, {".so", "rb", C_EXTENSION}, + {"module." SOABI ".so", "rb", C_EXTENSION}, {"module.so", "rb", C_EXTENSION}, -#endif -#endif -#endif +#endif /* __VMS */ +#endif /* defined(PYOS_OS2) && defined(PYCC_GCC) */ +#endif /* __CYGWIN__ */ {0, 0} }; Modified: python/branches/py3k/configure ============================================================================== --- python/branches/py3k/configure (original) +++ python/branches/py3k/configure Fri Sep 3 20:30:30 2010 @@ -1,14 +1,14 @@ #! /bin/sh -# From configure.in Revision: 83986 . +# From configure.in Revision: 84379 . # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.65 for python 3.2. +# Generated by GNU Autoconf 2.67 for python 3.2. # # Report bugs to . # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software +# Foundation, Inc. # # # This configure script is free software; the Free Software Foundation @@ -320,7 +320,7 @@ test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p @@ -360,19 +360,19 @@ fi # as_fn_arith -# as_fn_error ERROR [LINENO LOG_FD] -# --------------------------------- +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with status $?, using 1 if that was 0. +# script with STATUS, using 1 if that was 0. as_fn_error () { - as_status=$?; test $as_status -eq 0 && as_status=1 - if test "$3"; then - as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi - $as_echo "$as_me: error: $1" >&2 + $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error @@ -534,7 +534,7 @@ exec 6>&1 # Name of the host. -# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` @@ -598,6 +598,7 @@ ac_subst_vars='LTLIBOBJS SRCDIRS THREADHEADERS +SOABI LIBC LIBM HAVE_GETHOSTBYNAME @@ -825,8 +826,9 @@ fi case $ac_option in - *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *) ac_optarg=yes ;; + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. @@ -871,7 +873,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error "invalid feature name: $ac_useropt" + as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -897,7 +899,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error "invalid feature name: $ac_useropt" + as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1101,7 +1103,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error "invalid package name: $ac_useropt" + as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1117,7 +1119,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error "invalid package name: $ac_useropt" + as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1147,8 +1149,8 @@ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; - -*) as_fn_error "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information." + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" ;; *=*) @@ -1156,7 +1158,7 @@ # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error "invalid variable name: \`$ac_envvar'" ;; + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; @@ -1174,13 +1176,13 @@ if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error "missing argument to $ac_option" + as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; - fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi @@ -1203,7 +1205,7 @@ [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac - as_fn_error "expected an absolute directory name for --$ac_var: $ac_val" + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' @@ -1217,8 +1219,8 @@ if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used." >&2 + $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1233,9 +1235,9 @@ ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error "working directory cannot be determined" + as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error "pwd does not report name of working directory" + as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. @@ -1274,11 +1276,11 @@ fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error "cannot find sources ($ac_unique_file) in $srcdir" + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg" + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then @@ -1318,7 +1320,7 @@ --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking...' messages + -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files @@ -1503,9 +1505,9 @@ if $ac_init_version; then cat <<\_ACEOF python configure 3.2 -generated by GNU Autoconf 2.65 +generated by GNU Autoconf 2.67 -Copyright (C) 2009 Free Software Foundation, Inc. +Copyright (C) 2010 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1575,7 +1577,7 @@ mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } >/dev/null && { + test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : @@ -1599,10 +1601,10 @@ ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + if eval "test \"\${$3+set}\"" = set; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : +if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 @@ -1638,7 +1640,7 @@ else ac_header_preproc=no fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } @@ -1661,17 +1663,15 @@ $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( cat <<\_ASBOX -## -------------------------------------- ## +( $as_echo "## -------------------------------------- ## ## Report this to http://bugs.python.org/ ## -## -------------------------------------- ## -_ASBOX +## -------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : +if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" @@ -1735,7 +1735,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : +if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -1812,7 +1812,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : +if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else eval "$3=no" @@ -1866,7 +1866,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 $as_echo_n "checking for uint$2_t... " >&6; } -if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : +if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else eval "$3=no" @@ -1896,8 +1896,7 @@ esac fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - eval as_val=\$$3 - if test "x$as_val" = x""no; then : + if eval test \"x\$"$3"\" = x"no"; then : else break @@ -1920,7 +1919,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 $as_echo_n "checking for int$2_t... " >&6; } -if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : +if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else eval "$3=no" @@ -1931,11 +1930,11 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default + enum { N = $2 / 2 - 1 }; int main () { -static int test_array [1 - 2 * !(enum { N = $2 / 2 - 1 }; - 0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; +static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; test_array [0] = 0 ; @@ -1946,11 +1945,11 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default + enum { N = $2 / 2 - 1 }; int main () { -static int test_array [1 - 2 * !(enum { N = $2 / 2 - 1 }; - ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) +static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; test_array [0] = 0 @@ -1971,8 +1970,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - eval as_val=\$$3 - if test "x$as_val" = x""no; then : + if eval test \"x\$"$3"\" = x"no"; then : else break @@ -2172,7 +2170,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : +if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2240,7 +2238,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 $as_echo_n "checking for $2.$3... " >&6; } -if { as_var=$4; eval "test \"\${$as_var+set}\" = set"; }; then : +if eval "test \"\${$4+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2288,15 +2286,18 @@ } # ac_fn_c_check_member -# ac_fn_c_check_decl LINENO SYMBOL VAR -# ------------------------------------ -# Tests whether SYMBOL is declared, setting cache variable VAR accordingly. +# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES +# --------------------------------------------- +# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR +# accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $2 is declared" >&5 -$as_echo_n "checking whether $2 is declared... " >&6; } -if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + as_decl_name=`echo $2|sed 's/ *(.*//'` + as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } +if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2305,8 +2306,12 @@ int main () { -#ifndef $2 - (void) $2; +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif #endif ; @@ -2331,7 +2336,7 @@ running configure, to aid debugging if configure makes a mistake. It was created by python $as_me 3.2, which was -generated by GNU Autoconf 2.65. Invocation command line was +generated by GNU Autoconf 2.67. Invocation command line was $ $0 $@ @@ -2441,11 +2446,9 @@ { echo - cat <<\_ASBOX -## ---------------- ## + $as_echo "## ---------------- ## ## Cache variables. ## -## ---------------- ## -_ASBOX +## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( @@ -2479,11 +2482,9 @@ ) echo - cat <<\_ASBOX -## ----------------- ## + $as_echo "## ----------------- ## ## Output variables. ## -## ----------------- ## -_ASBOX +## ----------------- ##" echo for ac_var in $ac_subst_vars do @@ -2496,11 +2497,9 @@ echo if test -n "$ac_subst_files"; then - cat <<\_ASBOX -## ------------------- ## + $as_echo "## ------------------- ## ## File substitutions. ## -## ------------------- ## -_ASBOX +## ------------------- ##" echo for ac_var in $ac_subst_files do @@ -2514,11 +2513,9 @@ fi if test -s confdefs.h; then - cat <<\_ASBOX -## ----------- ## + $as_echo "## ----------- ## ## confdefs.h. ## -## ----------- ## -_ASBOX +## ----------- ##" echo cat confdefs.h echo @@ -2573,7 +2570,12 @@ ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then - ac_site_file1=$CONFIG_SITE + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site @@ -2588,7 +2590,11 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5 ; } fi done @@ -2664,7 +2670,7 @@ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## @@ -2698,6 +2704,7 @@ VERSION=3.2 +# Version number or Python's own shared library file. SOVERSION=1.0 @@ -2764,7 +2771,7 @@ UNIVERSALSDK=$enableval if test ! -d "${UNIVERSALSDK}" then - as_fn_error "--enable-universalsdk specifies non-existing SDK: ${UNIVERSALSDK}" "$LINENO" 5 + as_fn_error $? "--enable-universalsdk specifies non-existing SDK: ${UNIVERSALSDK}" "$LINENO" 5 fi ;; esac @@ -3156,7 +3163,7 @@ # If the user switches compilers, we can't believe the cache if test ! -z "$ac_cv_prog_CC" -a ! -z "$CC" -a "$CC" != "$ac_cv_prog_CC" then - as_fn_error "cached CC is different -- throw away $cache_file + as_fn_error $? "cached CC is different -- throw away $cache_file (it is also a good idea to do 'make clean' before compiling)" "$LINENO" 5 fi @@ -3466,8 +3473,8 @@ test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "no acceptable C compiler found in \$PATH -See \`config.log' for more details." "$LINENO" 5; } +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5 ; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 @@ -3581,9 +3588,8 @@ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "C compiler cannot create executables -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5 ; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } @@ -3625,8 +3631,8 @@ else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." "$LINENO" 5; } +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5 ; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 @@ -3683,9 +3689,9 @@ else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "cannot run C compiled programs. +as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. -See \`config.log' for more details." "$LINENO" 5; } +See \`config.log' for more details" "$LINENO" 5 ; } fi fi fi @@ -3736,8 +3742,8 @@ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "cannot compute suffix of object files: cannot compile -See \`config.log' for more details." "$LINENO" 5; } +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5 ; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi @@ -4190,7 +4196,7 @@ # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -4206,11 +4212,11 @@ ac_preproc_ok=: break fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext +rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi @@ -4249,7 +4255,7 @@ # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -4265,18 +4271,18 @@ ac_preproc_ok=: break fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext +rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." "$LINENO" 5; } +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5 ; } fi ac_ext=c @@ -4337,7 +4343,7 @@ done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then - as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP @@ -4403,7 +4409,7 @@ done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then - as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP @@ -4535,8 +4541,7 @@ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " -eval as_val=\$$as_ac_Header - if test "x$as_val" = x""yes; then : +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -5140,16 +5145,22 @@ esac ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do - for ac_t in install-sh install.sh shtool; do - if test -f "$ac_dir/$ac_t"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/$ac_t -c" - break 2 - fi - done + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi done if test -z "$ac_aux_dir"; then - as_fn_error "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, @@ -5264,6 +5275,9 @@ esac fi +# For calculating the .so ABI tag. +SOABI_QUALIFIERS="" + # Check for --with-pydebug { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-pydebug" >&5 $as_echo_n "checking for --with-pydebug... " >&6; } @@ -5279,6 +5293,7 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; }; Py_DEBUG='true' + SOABI_QUALIFIERS="${SOABI_QUALIFIERS}d" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; }; Py_DEBUG='false' fi @@ -5482,7 +5497,7 @@ ARCH_RUN_32BIT="/usr/bin/arch -i386 -ppc" else - as_fn_error "proper usage is --with-universal-arch=32-bit|64-bit|all|intel|3-way" "$LINENO" 5 + as_fn_error $? "proper usage is --with-universal-arch=32-bit|64-bit|all|intel|3-way" "$LINENO" 5 fi @@ -6059,8 +6074,7 @@ do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -eval as_val=\$$as_ac_Header - if test "x$as_val" = x""yes; then : +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -6074,7 +6088,7 @@ as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 $as_echo_n "checking for $ac_hdr that defines DIR... " >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then : +if eval "test \"\${$as_ac_Header+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -6101,8 +6115,7 @@ eval ac_res=\$$as_ac_Header { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -eval as_val=\$$as_ac_Header - if test "x$as_val" = x""yes; then : +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 _ACEOF @@ -6622,9 +6635,8 @@ if test "$ac_cv_type_int" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (int) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (int) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_int=0 fi @@ -6656,9 +6668,8 @@ if test "$ac_cv_type_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (long) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (long) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_long=0 fi @@ -6690,9 +6701,8 @@ if test "$ac_cv_type_void_p" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (void *) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (void *) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_void_p=0 fi @@ -6724,9 +6734,8 @@ if test "$ac_cv_type_short" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (short) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (short) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_short=0 fi @@ -6758,9 +6767,8 @@ if test "$ac_cv_type_float" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (float) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (float) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_float=0 fi @@ -6792,9 +6800,8 @@ if test "$ac_cv_type_double" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (double) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (double) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_double=0 fi @@ -6826,9 +6833,8 @@ if test "$ac_cv_type_fpos_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (fpos_t) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (fpos_t) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_fpos_t=0 fi @@ -6860,9 +6866,8 @@ if test "$ac_cv_type_size_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (size_t) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (size_t) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_size_t=0 fi @@ -6894,9 +6899,8 @@ if test "$ac_cv_type_pid_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (pid_t) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (pid_t) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_pid_t=0 fi @@ -6955,9 +6959,8 @@ if test "$ac_cv_type_long_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (long long) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (long long) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_long_long=0 fi @@ -7017,9 +7020,8 @@ if test "$ac_cv_type_long_double" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (long double) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (long double) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_long_double=0 fi @@ -7080,9 +7082,8 @@ if test "$ac_cv_type__Bool" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (_Bool) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (_Bool) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof__Bool=0 fi @@ -7126,9 +7127,8 @@ if test "$ac_cv_type_uintptr_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (uintptr_t) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (uintptr_t) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_uintptr_t=0 fi @@ -7168,9 +7168,8 @@ if test "$ac_cv_type_off_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (off_t) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (off_t) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_off_t=0 fi @@ -7231,9 +7230,8 @@ if test "$ac_cv_type_time_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (time_t) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (time_t) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_time_t=0 fi @@ -7304,9 +7302,8 @@ if test "$ac_cv_type_pthread_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (pthread_t) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (pthread_t) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_pthread_t=0 fi @@ -7393,7 +7390,7 @@ MACOSX_DEFAULT_ARCH="ppc" ;; *) - as_fn_error "Unexpected output of 'arch' on OSX" "$LINENO" 5 + as_fn_error $? "Unexpected output of 'arch' on OSX" "$LINENO" 5 ;; esac else @@ -7405,7 +7402,7 @@ MACOSX_DEFAULT_ARCH="ppc64" ;; *) - as_fn_error "Unexpected output of 'arch' on OSX" "$LINENO" 5 + as_fn_error $? "Unexpected output of 'arch' on OSX" "$LINENO" 5 ;; esac @@ -7431,7 +7428,7 @@ $as_echo "yes" >&6; } if test $enable_shared = "yes" then - as_fn_error "Specifying both --enable-shared and --enable-framework is not supported, use only --enable-framework instead" "$LINENO" 5 + as_fn_error $? "Specifying both --enable-shared and --enable-framework is not supported, use only --enable-framework instead" "$LINENO" 5 fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 @@ -7461,36 +7458,6 @@ -# SO is the extension of shared libraries `(including the dot!) -# -- usually .so, .sl on HP-UX, .dll on Cygwin -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking SO" >&5 -$as_echo_n "checking SO... " >&6; } -if test -z "$SO" -then - case $ac_sys_system in - hp*|HP*) - case `uname -m` in - ia64) SO=.so;; - *) SO=.sl;; - esac - ;; - CYGWIN*) SO=.dll;; - *) SO=.so;; - esac -else - # this might also be a termcap variable, see #610332 - echo - echo '=====================================================================' - echo '+ +' - echo '+ WARNING: You have set SO in your environment. +' - echo '+ Do you really mean to change the extension for shared libraries? +' - echo '+ Continuing in 10 seconds to let you to ponder. +' - echo '+ +' - echo '=====================================================================' - sleep 10 -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SO" >&5 -$as_echo "$SO" >&6; } cat >>confdefs.h <<_ACEOF @@ -8239,12 +8206,12 @@ withval=$with_dbmliborder; if test x$with_dbmliborder = xyes then -as_fn_error "proper usage is --with-dbmliborder=db1:db2:..." "$LINENO" 5 +as_fn_error $? "proper usage is --with-dbmliborder=db1:db2:..." "$LINENO" 5 else for db in `echo $with_dbmliborder | sed 's/:/ /g'`; do if test x$db != xndbm && test x$db != xgdbm && test x$db != xbdb then - as_fn_error "proper usage is --with-dbmliborder=db1:db2:..." "$LINENO" 5 + as_fn_error $? "proper usage is --with-dbmliborder=db1:db2:..." "$LINENO" 5 fi done fi @@ -9210,7 +9177,9 @@ if test -z "$with_pymalloc" -then with_pymalloc="yes" +then + with_pymalloc="yes" + SOABI_QUALIFIERS="${SOABI_QUALIFIERS}m" fi if test "$with_pymalloc" != "no" then @@ -9241,7 +9210,7 @@ $as_echo "#define WITH_VALGRIND 1" >>confdefs.h else - as_fn_error "Valgrind support requested but headers not available" "$LINENO" 5 + as_fn_error $? "Valgrind support requested but headers not available" "$LINENO" 5 fi @@ -9358,8 +9327,7 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -eval as_val=\$$as_ac_var - if test "x$as_val" = x""yes; then : +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -10293,8 +10261,7 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -eval as_val=\$$as_ac_var - if test "x$as_val" = x""yes; then : +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -10303,25 +10270,44 @@ done -for ac_func in dup2 getcwd strdup -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -eval as_val=\$$as_ac_var - if test "x$as_val" = x""yes; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF +ac_fn_c_check_func "$LINENO" "dup2" "ac_cv_func_dup2" +if test "x$ac_cv_func_dup2" = x""yes; then : + $as_echo "#define HAVE_DUP2 1" >>confdefs.h else case " $LIBOBJS " in - *" $ac_func.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" + *" dup2.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS dup2.$ac_objext" + ;; +esac + +fi + +ac_fn_c_check_func "$LINENO" "getcwd" "ac_cv_func_getcwd" +if test "x$ac_cv_func_getcwd" = x""yes; then : + $as_echo "#define HAVE_GETCWD 1" >>confdefs.h + +else + case " $LIBOBJS " in + *" getcwd.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getcwd.$ac_objext" + ;; +esac + +fi + +ac_fn_c_check_func "$LINENO" "strdup" "ac_cv_func_strdup" +if test "x$ac_cv_func_strdup" = x""yes; then : + $as_echo "#define HAVE_STRDUP 1" >>confdefs.h + +else + case " $LIBOBJS " in + *" strdup.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS strdup.$ac_objext" ;; esac fi -done for ac_func in getpgrp @@ -11530,7 +11516,7 @@ then LIBM=$withval { $as_echo "$as_me:${as_lineno-$LINENO}: result: set LIBM=\"$withval\"" >&5 $as_echo "set LIBM=\"$withval\"" >&6; } -else as_fn_error "proper usage is --with-libm=STRING" "$LINENO" 5 +else as_fn_error $? "proper usage is --with-libm=STRING" "$LINENO" 5 fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: default LIBM=\"$LIBM\"" >&5 @@ -11554,7 +11540,7 @@ then LIBC=$withval { $as_echo "$as_me:${as_lineno-$LINENO}: result: set LIBC=\"$withval\"" >&5 $as_echo "set LIBC=\"$withval\"" >&6; } -else as_fn_error "proper usage is --with-libc=STRING" "$LINENO" 5 +else as_fn_error $? "proper usage is --with-libc=STRING" "$LINENO" 5 fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: default LIBC=\"$LIBC\"" >&5 @@ -11850,8 +11836,7 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -eval as_val=\$$as_ac_var - if test "x$as_val" = x""yes; then : +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -11863,8 +11848,7 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -eval as_val=\$$as_ac_var - if test "x$as_val" = x""yes; then : +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -12032,7 +12016,7 @@ 15|30) ;; *) - as_fn_error "bad value $enable_big_digits for --enable-big-digits; value should be 15 or 30" "$LINENO" 5 ;; + as_fn_error $? "bad value $enable_big_digits for --enable-big-digits; value should be 15 or 30" "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_big_digits" >&5 $as_echo "$enable_big_digits" >&6; } @@ -12083,9 +12067,8 @@ if test "$ac_cv_type_wchar_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (wchar_t) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (wchar_t) +See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_wchar_t=0 fi @@ -12187,7 +12170,7 @@ else case "$have_ucs4_tcl" in - yes) unicode_size="4" ;; + yes) unicode_size="4";; *) unicode_size="2" ;; esac @@ -12196,8 +12179,11 @@ case "$unicode_size" in - 4) $as_echo "#define Py_UNICODE_SIZE 4" >>confdefs.h - ;; + 4) + $as_echo "#define Py_UNICODE_SIZE 4" >>confdefs.h + + SOABI_QUALIFIERS="${SOABI_QUALIFIERS}u" + ;; *) $as_echo "#define Py_UNICODE_SIZE 2" >>confdefs.h ;; esac @@ -12451,11 +12437,64 @@ ;; #( *) - as_fn_error "unknown endianness - presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + as_fn_error $? "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac +# ABI version string for Python extension modules. This appears between the +# periods in shared library file names, e.g. foo..so. It is calculated +# from the following attributes which affect the ABI of this Python build (in +# this order): +# +# * The Python implementation (always 'cpython-' for us) +# * The major and minor version numbers +# * --with-pydebug (adds a 'd') +# * --with-pymalloc (adds a 'm') +# * --with-wide-unicode (adds a 'u') +# +# Thus for example, Python 3.2 built with wide unicode, pydebug, and pymalloc, +# would get a shared library ABI version tag of 'cpython-32udm' and shared +# libraries would be named 'foo.cpython-32udm.so'. + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking SOABI" >&5 +$as_echo_n "checking SOABI... " >&6; } +SOABI='cpython-'`echo $VERSION | tr -d .`${SOABI_QUALIFIERS} +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SOABI" >&5 +$as_echo "$SOABI" >&6; } + +# SO is the extension of shared libraries `(including the dot!) +# -- usually .so, .sl on HP-UX, .dll on Cygwin +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking SO" >&5 +$as_echo_n "checking SO... " >&6; } +if test -z "$SO" +then + case $ac_sys_system in + hp*|HP*) + case `uname -m` in + ia64) SO=.so;; + *) SO=.sl;; + esac + ;; + CYGWIN*) SO=.dll;; + Linux*) SO=.${SOABI}.so;; + *) SO=.so;; + esac +else + # this might also be a termcap variable, see #610332 + echo + echo '=====================================================================' + echo '+ +' + echo '+ WARNING: You have set SO in your environment. +' + echo '+ Do you really mean to change the extension for shared libraries? +' + echo '+ Continuing in 10 seconds to let you to ponder. +' + echo '+ +' + echo '=====================================================================' + sleep 10 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SO" >&5 +$as_echo "$SO" >&6; } + # Check whether right shifting a negative integer extends the sign bit # or fills with zeros (like the Cray J90, according to Tim Peters). { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether right shift extends the sign bit" >&5 @@ -12649,7 +12688,7 @@ have_readline=no fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext if test $have_readline = yes then cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -12823,7 +12862,7 @@ have_readline=no fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext if test $have_readline = yes then cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -13633,7 +13672,7 @@ case $ac_sys_system in - OSF*) as_fn_error "OSF* systems are deprecated unless somebody volunteers. Check http://bugs.python.org/issue8606" "$LINENO" 5 ;; + OSF*) as_fn_error $? "OSF* systems are deprecated unless somebody volunteers. Check http://bugs.python.org/issue8606" "$LINENO" 5 ;; esac @@ -13743,6 +13782,7 @@ ac_libobjs= ac_ltlibobjs= +U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' @@ -13905,19 +13945,19 @@ (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# as_fn_error ERROR [LINENO LOG_FD] -# --------------------------------- +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with status $?, using 1 if that was 0. +# script with STATUS, using 1 if that was 0. as_fn_error () { - as_status=$?; test $as_status -eq 0 && as_status=1 - if test "$3"; then - as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi - $as_echo "$as_me: error: $1" >&2 + $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error @@ -14113,7 +14153,7 @@ test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p @@ -14167,7 +14207,7 @@ # values after options handling. ac_log=" This file was extended by python $as_me 3.2, which was -generated by GNU Autoconf 2.65. Invocation command line was +generated by GNU Autoconf 2.67. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -14191,8 +14231,8 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. -config_files="`echo $ac_config_files`" -config_headers="`echo $ac_config_headers`" +config_files="$ac_config_files" +config_headers="$ac_config_headers" _ACEOF @@ -14229,10 +14269,10 @@ ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ python config.status 3.2 -configured by $0, generated by GNU Autoconf 2.65, +configured by $0, generated by GNU Autoconf 2.67, with options \\"\$ac_cs_config\\" -Copyright (C) 2009 Free Software Foundation, Inc. +Copyright (C) 2010 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -14248,11 +14288,16 @@ while test $# != 0 do case $1 in - --*=*) + --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; *) ac_option=$1 ac_optarg=$2 @@ -14274,6 +14319,7 @@ $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; @@ -14286,7 +14332,7 @@ ac_need_defaults=false;; --he | --h) # Conflict between --help and --header - as_fn_error "ambiguous option: \`$1' + as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; @@ -14295,7 +14341,7 @@ ac_cs_silent=: ;; # This is an error. - -*) as_fn_error "unrecognized option: \`$1' + -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" @@ -14353,7 +14399,7 @@ "Modules/Setup.config") CONFIG_FILES="$CONFIG_FILES Modules/Setup.config" ;; "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; - *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5 ;; esac done @@ -14390,7 +14436,7 @@ { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") -} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5 +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. @@ -14407,7 +14453,7 @@ fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\r' + ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi @@ -14421,18 +14467,18 @@ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || - as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || - as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then - as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi @@ -14521,20 +14567,28 @@ else cat fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ - || as_fn_error "could not setup config files machinery" "$LINENO" 5 + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF -# VPATH may cause trouble with some makes, so we remove $(srcdir), -# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=/{ -s/:*\$(srcdir):*/:/ -s/:*\${srcdir}:*/:/ -s/:*@srcdir@:*/:/ -s/^\([^=]*=[ ]*\):*/\1/ + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// s/^[^=]*=[ ]*$// }' fi @@ -14562,7 +14616,7 @@ if test -z "$ac_t"; then break elif $ac_last_try; then - as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5 + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi @@ -14647,7 +14701,7 @@ _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error "could not setup config headers machinery" "$LINENO" 5 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" @@ -14660,7 +14714,7 @@ esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5 ;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac @@ -14688,7 +14742,7 @@ [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || - as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;; + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5 ;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" @@ -14715,7 +14769,7 @@ case $ac_tag in *:-:* | *:-) cat >"$tmp/stdin" \ - || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac @@ -14846,22 +14900,22 @@ $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ - || as_fn_error "could not create $ac_file" "$LINENO" 5 + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined." >&5 +which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined." >&2;} +which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$tmp/stdin" case $ac_file in -) cat "$tmp/out" && rm -f "$tmp/out";; *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; esac \ - || as_fn_error "could not create $ac_file" "$LINENO" 5 + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # @@ -14872,19 +14926,19 @@ $as_echo "/* $configure_input */" \ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" } >"$tmp/config.h" \ - || as_fn_error "could not create $ac_file" "$LINENO" 5 + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$tmp/config.h" "$ac_file" \ - || as_fn_error "could not create $ac_file" "$LINENO" 5 + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error "could not create -" "$LINENO" 5 + || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; @@ -14899,7 +14953,7 @@ ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || - as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5 + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. @@ -14920,7 +14974,7 @@ exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit $? + $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 Modified: python/branches/py3k/configure.in ============================================================================== --- python/branches/py3k/configure.in (original) +++ python/branches/py3k/configure.in Fri Sep 3 20:30:30 2010 @@ -12,7 +12,7 @@ [], [m4_fatal([Autoconf version $1 is required for Python], 63)]) ]) -version_required(2.65) +AC_PREREQ(2.65) AC_REVISION($Revision$) AC_INIT(python, PYTHON_VERSION, http://bugs.python.org/) @@ -52,6 +52,7 @@ AC_SUBST(VERSION) VERSION=PYTHON_VERSION +# Version number or Python's own shared library file. AC_SUBST(SOVERSION) SOVERSION=1.0 @@ -817,6 +818,9 @@ esac fi +# For calculating the .so ABI tag. +SOABI_QUALIFIERS="" + # Check for --with-pydebug AC_MSG_CHECKING(for --with-pydebug) AC_ARG_WITH(pydebug, @@ -828,6 +832,7 @@ [Define if you want to build an interpreter with many run-time checks.]) AC_MSG_RESULT(yes); Py_DEBUG='true' + SOABI_QUALIFIERS="${SOABI_QUALIFIERS}d" else AC_MSG_RESULT(no); Py_DEBUG='false' fi], [AC_MSG_RESULT(no)]) @@ -1649,34 +1654,6 @@ AC_SUBST(BLDSHARED) AC_SUBST(CCSHARED) AC_SUBST(LINKFORSHARED) -# SO is the extension of shared libraries `(including the dot!) -# -- usually .so, .sl on HP-UX, .dll on Cygwin -AC_MSG_CHECKING(SO) -if test -z "$SO" -then - case $ac_sys_system in - hp*|HP*) - case `uname -m` in - ia64) SO=.so;; - *) SO=.sl;; - esac - ;; - CYGWIN*) SO=.dll;; - *) SO=.so;; - esac -else - # this might also be a termcap variable, see #610332 - echo - echo '=====================================================================' - echo '+ +' - echo '+ WARNING: You have set SO in your environment. +' - echo '+ Do you really mean to change the extension for shared libraries? +' - echo '+ Continuing in 10 seconds to let you to ponder. +' - echo '+ +' - echo '=====================================================================' - sleep 10 -fi -AC_MSG_RESULT($SO) AC_DEFINE_UNQUOTED(SHLIB_EXT, "$SO", [Define this to be extension of shared libraries (including the dot!).]) # LDSHARED is the ld *command* used to create shared library @@ -2487,7 +2464,9 @@ AS_HELP_STRING([--with(out)-pymalloc], [disable/enable specialized mallocs])) if test -z "$with_pymalloc" -then with_pymalloc="yes" +then + with_pymalloc="yes" + SOABI_QUALIFIERS="${SOABI_QUALIFIERS}m" fi if test "$with_pymalloc" != "no" then @@ -3595,7 +3574,7 @@ ], [ case "$have_ucs4_tcl" in - yes) unicode_size="4" ;; + yes) unicode_size="4";; *) unicode_size="2" ;; esac ]) @@ -3603,7 +3582,10 @@ AH_TEMPLATE(Py_UNICODE_SIZE, [Define as the size of the unicode type.]) case "$unicode_size" in - 4) AC_DEFINE(Py_UNICODE_SIZE, 4) ;; + 4) + AC_DEFINE(Py_UNICODE_SIZE, 4) + SOABI_QUALIFIERS="${SOABI_QUALIFIERS}u" + ;; *) AC_DEFINE(Py_UNICODE_SIZE, 2) ;; esac @@ -3636,6 +3618,55 @@ # check for endianness AC_C_BIGENDIAN +# ABI version string for Python extension modules. This appears between the +# periods in shared library file names, e.g. foo..so. It is calculated +# from the following attributes which affect the ABI of this Python build (in +# this order): +# +# * The Python implementation (always 'cpython-' for us) +# * The major and minor version numbers +# * --with-pydebug (adds a 'd') +# * --with-pymalloc (adds a 'm') +# * --with-wide-unicode (adds a 'u') +# +# Thus for example, Python 3.2 built with wide unicode, pydebug, and pymalloc, +# would get a shared library ABI version tag of 'cpython-32udm' and shared +# libraries would be named 'foo.cpython-32udm.so'. +AC_SUBST(SOABI) +AC_MSG_CHECKING(SOABI) +SOABI='cpython-'`echo $VERSION | tr -d .`${SOABI_QUALIFIERS} +AC_MSG_RESULT($SOABI) + +# SO is the extension of shared libraries `(including the dot!) +# -- usually .so, .sl on HP-UX, .dll on Cygwin +AC_MSG_CHECKING(SO) +if test -z "$SO" +then + case $ac_sys_system in + hp*|HP*) + case `uname -m` in + ia64) SO=.so;; + *) SO=.sl;; + esac + ;; + CYGWIN*) SO=.dll;; + Linux*) SO=.${SOABI}.so;; + *) SO=.so;; + esac +else + # this might also be a termcap variable, see #610332 + echo + echo '=====================================================================' + echo '+ +' + echo '+ WARNING: You have set SO in your environment. +' + echo '+ Do you really mean to change the extension for shared libraries? +' + echo '+ Continuing in 10 seconds to let you to ponder. +' + echo '+ +' + echo '=====================================================================' + sleep 10 +fi +AC_MSG_RESULT($SO) + # Check whether right shifting a negative integer extends the sign bit # or fills with zeros (like the Cray J90, according to Tim Peters). AC_MSG_CHECKING(whether right shift extends the sign bit) Modified: python/branches/py3k/pyconfig.h.in ============================================================================== --- python/branches/py3k/pyconfig.h.in (original) +++ python/branches/py3k/pyconfig.h.in Fri Sep 3 20:30:30 2010 @@ -1040,7 +1040,7 @@ /* Define to 1 if your declares `struct tm'. */ #undef TM_IN_SYS_TIME -/* Define to 0 if you don't want to use computed gotos in ceval.c. */ +/* Define if you want to use computed gotos in ceval.c. */ #undef USE_COMPUTED_GOTOS /* Define if the compiler supports the inline keyword */ From python-checkins at python.org Fri Sep 3 20:31:07 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Fri, 3 Sep 2010 20:31:07 +0200 (CEST) Subject: [Python-checkins] r84459 - python/branches/py3k/Doc/c-api/unicode.rst Message-ID: <20100903183107.B0CF7EEA4D@mail.python.org> Author: daniel.stutzbach Date: Fri Sep 3 20:31:07 2010 New Revision: 84459 Log: Doc fix: unicode() is now str() Modified: python/branches/py3k/Doc/c-api/unicode.rst Modified: python/branches/py3k/Doc/c-api/unicode.rst ============================================================================== --- python/branches/py3k/Doc/c-api/unicode.rst (original) +++ python/branches/py3k/Doc/c-api/unicode.rst Fri Sep 3 20:31:07 2010 @@ -470,7 +470,7 @@ Many of the following APIs take two arguments encoding and errors. These parameters encoding and errors have the same semantics as the ones of the -built-in :func:`unicode` Unicode object constructor. +built-in :func:`str` string object constructor. Setting encoding to *NULL* causes the default encoding to be used which is ASCII. The file system calls should use From python-checkins at python.org Fri Sep 3 20:31:47 2010 From: python-checkins at python.org (barry.warsaw) Date: Fri, 3 Sep 2010 20:31:47 +0200 (CEST) Subject: [Python-checkins] r84460 - peps/trunk/pep-3149.txt Message-ID: <20100903183147.A0AC6EEA1C@mail.python.org> Author: barry.warsaw Date: Fri Sep 3 20:31:47 2010 New Revision: 84460 Log: PEP 3149 has been accepted. Modified: peps/trunk/pep-3149.txt Modified: peps/trunk/pep-3149.txt ============================================================================== --- peps/trunk/pep-3149.txt (original) +++ peps/trunk/pep-3149.txt Fri Sep 3 20:31:47 2010 @@ -3,13 +3,13 @@ Version: $Revision$ Last-Modified: $Date$ Author: Barry Warsaw -Status: Draft +Status: Final Type: Standards Track Content-Type: text/x-rst Created: 2010-07-09 Python-Version: 3.2 Post-History: 2010-07-14, 2010-07-22 -Resolution: TBD +Resolution: http://mail.python.org/pipermail/python-dev/2010-September/103408.html Abstract From python-checkins at python.org Fri Sep 3 20:32:23 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 3 Sep 2010 20:32:23 +0200 (CEST) Subject: [Python-checkins] r84461 - peps/trunk/pep-3151.txt Message-ID: <20100903183223.51915FCB4@mail.python.org> Author: antoine.pitrou Date: Fri Sep 3 20:32:23 2010 New Revision: 84461 Log: Replaced `FileAlreadyExistsError` with the shorter `FileExistsError` (thanks Georg) Modified: peps/trunk/pep-3151.txt Modified: peps/trunk/pep-3151.txt ============================================================================== --- peps/trunk/pep-3151.txt (original) +++ peps/trunk/pep-3151.txt Fri Sep 3 20:32:23 2010 @@ -330,8 +330,8 @@ The following tentative list of subclasses, along with a description and the list of errnos mapped to them, is submitted to discussion: -* ``FileAlreadyExistsError``: trying to create a file or directory which - already exists (EEXIST) +* ``FileExistsError``: trying to create a file or directory which already + exists (EEXIST) * ``FileNotFoundError``: for all circumstances where a file and directory is requested but doesn't exist (ENOENT) @@ -368,7 +368,7 @@ * ``ConnectionError``: a base class for ``ConnectionAbortedError``, ``ConnectionRefusedError`` and ``ConnectionResetError`` -* ``FileSystemError``: a base class for ``FileAlreadyExistsError``, +* ``FileSystemError``: a base class for ``FileExistsError``, ``FileNotFoundError``, ``IsADirectoryError`` and ``NotADirectoryError`` The following drawing tries to sum up the proposed additions, along with @@ -383,7 +383,7 @@ +-- ConnectionResetError ECONNRESET +-- FileDescriptorError EBADF +-- FileSystemError - +-- FileAlreadyExistsError EEXIST + +-- FileExistsError EEXIST +-- FileNotFoundError ENOENT +-- IsADirectoryError EISDIR +-- NotADirectoryError ENOTDIR @@ -396,7 +396,7 @@ Various naming controversies can arise. One of them is whether all exception class names should end in "``Error``". In favour is consistency with the rest of the exception hiearchy, against is concision (especially -with long names such as ``FileAlreadyExistsError``). +with long names such as ``ConnectionAbortedError``). Another cosmetic issue is whether ``FileSystemError`` should be spelled ``FilesystemError`` instead. From python-checkins at python.org Fri Sep 3 20:36:11 2010 From: python-checkins at python.org (barry.warsaw) Date: Fri, 3 Sep 2010 20:36:11 +0200 (CEST) Subject: [Python-checkins] r84462 - python/branches/py3k/Misc/NEWS Message-ID: <20100903183611.93861EEAC2@mail.python.org> Author: barry.warsaw Date: Fri Sep 3 20:36:11 2010 New Revision: 84462 Log: NEWS for PEP 3149, and clean up a few other entries. Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Sep 3 20:36:11 2010 @@ -36,8 +36,8 @@ - Issue #9684: Added a definition for SIZEOF_WCHAR_T to PC/pyconfig.h, to match the pyconfig.h generated by configure on other systems. -- Issue #9666: Only catch AttributeError in hasattr(). All other exceptions that - occur during attribute lookup are now propagated to the caller. +- Issue #9666: Only catch AttributeError in hasattr(). All other exceptions + that occur during attribute lookup are now propagated to the caller. - Issue #8622: Add PYTHONFSENCODING environment variable to override the filesystem encoding. @@ -190,9 +190,9 @@ - Issue #9129: smtpd.py is vulnerable to DoS attacks deriving from missing error handling when accepting a new connection. -- Issue #9601: ftplib now provides a workaround for non-compliant - implementations such as IIS shipped with Windows server 2003 returning invalid - response codes for MKD and PWD commands. +- Issue #9601: ftplib now provides a workaround for non-compliant + implementations such as IIS shipped with Windows server 2003 returning + invalid response codes for MKD and PWD commands. - Issue #658749: asyncore's connect() method now correctly interprets winsock errors. @@ -215,8 +215,8 @@ - Issue #3488: Provide convenient shorthand functions ``gzip.compress`` and ``gzip.decompress``. Original patch by Anand B. Pillai. -- Issue #8807: poplib.POP3_SSL class now accepts a context parameter, which is a - ssl.SSLContext object allowing bundling SSL configuration options, +- Issue #8807: poplib.POP3_SSL class now accepts a context parameter, which is + a ssl.SSLContext object allowing bundling SSL configuration options, certificates and private keys into a single (potentially long-lived) structure. @@ -378,6 +378,8 @@ Build ----- +- Issue #9193: PEP 3149 is accepted. + - Issue #3101: Helper functions _add_one_to_index_C() and _add_one_to_index_F() become _Py_add_one_to_index_C() and _Py_add_one_to_index_F(), respectively. From python-checkins at python.org Fri Sep 3 20:37:34 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Fri, 3 Sep 2010 20:37:34 +0200 (CEST) Subject: [Python-checkins] r84463 - in python/branches/release31-maint: Doc/c-api/unicode.rst Message-ID: <20100903183734.8F1AEF0B1@mail.python.org> Author: daniel.stutzbach Date: Fri Sep 3 20:37:34 2010 New Revision: 84463 Log: Merged revisions 84459 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84459 | daniel.stutzbach | 2010-09-03 13:31:07 -0500 (Fri, 03 Sep 2010) | 1 line Doc fix: unicode() is now str() ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/c-api/unicode.rst Modified: python/branches/release31-maint/Doc/c-api/unicode.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/unicode.rst (original) +++ python/branches/release31-maint/Doc/c-api/unicode.rst Fri Sep 3 20:37:34 2010 @@ -420,7 +420,7 @@ Many of the following APIs take two arguments encoding and errors. These parameters encoding and errors have the same semantics as the ones of the -built-in :func:`unicode` Unicode object constructor. +built-in :func:`str` string object constructor. Setting encoding to *NULL* causes the default encoding to be used which is ASCII. The file system calls should use From python-checkins at python.org Fri Sep 3 20:38:18 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 3 Sep 2010 20:38:18 +0200 (CEST) Subject: [Python-checkins] r84464 - in python/branches/py3k: Lib/ssl.py Modules/_ssl.c Message-ID: <20100903183818.050C0EEAAA@mail.python.org> Author: antoine.pitrou Date: Fri Sep 3 20:38:17 2010 New Revision: 84464 Log: Issue #3805: clean up implementation of the _read method in _ssl.c. Modified: python/branches/py3k/Lib/ssl.py python/branches/py3k/Modules/_ssl.c Modified: python/branches/py3k/Lib/ssl.py ============================================================================== --- python/branches/py3k/Lib/ssl.py (original) +++ python/branches/py3k/Lib/ssl.py Fri Sep 3 20:38:17 2010 @@ -199,14 +199,14 @@ self._checkClosed() try: - if buffer: - v = self._sslobj.read(buffer, len) + if buffer is not None: + v = self._sslobj.read(len, buffer) else: v = self._sslobj.read(len or 1024) return v except SSLError as x: if x.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs: - if buffer: + if buffer is not None: return 0 else: return b'' Modified: python/branches/py3k/Modules/_ssl.c ============================================================================== --- python/branches/py3k/Modules/_ssl.c (original) +++ python/branches/py3k/Modules/_ssl.c Fri Sep 3 20:38:17 2010 @@ -1156,11 +1156,9 @@ { PyObject *dest = NULL; Py_buffer buf; - int buf_passed = 0; - int count = -1; char *mem; - /* XXX this should use Py_ssize_t */ - int len = 1024; + int len, count; + int buf_passed = 0; int sockstate; int err; int nonblocking; @@ -1174,26 +1172,28 @@ } Py_INCREF(sock); - if (!PyArg_ParseTuple(args, "|Oi:read", &dest, &count)) + buf.obj = NULL; + buf.buf = NULL; + if (!PyArg_ParseTuple(args, "i|w*:read", &len, &buf)) goto error; - if ((dest == NULL) || (dest == Py_None)) { - if (!(dest = PyByteArray_FromStringAndSize((char *) 0, len))) - goto error; - mem = PyByteArray_AS_STRING(dest); - } else if (PyLong_Check(dest)) { - len = PyLong_AS_LONG(dest); - if (!(dest = PyByteArray_FromStringAndSize((char *) 0, len))) + if ((buf.buf == NULL) && (buf.obj == NULL)) { + dest = PyBytes_FromStringAndSize(NULL, len); + if (dest == NULL) goto error; - mem = PyByteArray_AS_STRING(dest); - } else { - if (PyObject_GetBuffer(dest, &buf, PyBUF_CONTIG) < 0) - goto error; - mem = buf.buf; - len = buf.len; - if ((count > 0) && (count <= len)) - len = count; + mem = PyBytes_AS_STRING(dest); + } + else { buf_passed = 1; + mem = buf.buf; + if (len <= 0 || len > buf.len) { + len = (int) buf.len; + if (buf.len != len) { + PyErr_SetString(PyExc_OverflowError, + "maximum length can't fit in a C 'int'"); + goto error; + } + } } /* just in case the blocking state of the socket has been changed */ @@ -1254,23 +1254,24 @@ PySSL_SetError(self, count, __FILE__, __LINE__); goto error; } - done: + +done: Py_DECREF(sock); if (!buf_passed) { - PyObject *res = PyBytes_FromStringAndSize(mem, count); - Py_DECREF(dest); - return res; - } else { + _PyBytes_Resize(&dest, count); + return dest; + } + else { PyBuffer_Release(&buf); return PyLong_FromLong(count); } - error: + +error: Py_DECREF(sock); - if (!buf_passed) { + if (!buf_passed) Py_XDECREF(dest); - } else { + else PyBuffer_Release(&buf); - } return NULL; } From python-checkins at python.org Fri Sep 3 20:39:47 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 3 Sep 2010 20:39:47 +0200 (CEST) Subject: [Python-checkins] r84465 - in python/branches/release31-maint: Lib/ssl.py Modules/_ssl.c Message-ID: <20100903183947.5838FEEA90@mail.python.org> Author: antoine.pitrou Date: Fri Sep 3 20:39:47 2010 New Revision: 84465 Log: Merged revisions 84464 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84464 | antoine.pitrou | 2010-09-03 20:38:17 +0200 (ven., 03 sept. 2010) | 3 lines Issue #3805: clean up implementation of the _read method in _ssl.c. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/ssl.py python/branches/release31-maint/Modules/_ssl.c Modified: python/branches/release31-maint/Lib/ssl.py ============================================================================== --- python/branches/release31-maint/Lib/ssl.py (original) +++ python/branches/release31-maint/Lib/ssl.py Fri Sep 3 20:39:47 2010 @@ -162,14 +162,14 @@ self._checkClosed() try: - if buffer: - v = self._sslobj.read(buffer, len) + if buffer is not None: + v = self._sslobj.read(len, buffer) else: v = self._sslobj.read(len or 1024) return v except SSLError as x: if x.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs: - if buffer: + if buffer is not None: return 0 else: return b'' Modified: python/branches/release31-maint/Modules/_ssl.c ============================================================================== --- python/branches/release31-maint/Modules/_ssl.c (original) +++ python/branches/release31-maint/Modules/_ssl.c Fri Sep 3 20:39:47 2010 @@ -1277,11 +1277,9 @@ { PyObject *dest = NULL; Py_buffer buf; - int buf_passed = 0; - int count = -1; char *mem; - /* XXX this should use Py_ssize_t */ - int len = 1024; + int len, count; + int buf_passed = 0; int sockstate; int err; int nonblocking; @@ -1295,26 +1293,28 @@ } Py_INCREF(sock); - if (!PyArg_ParseTuple(args, "|Oi:read", &dest, &count)) + buf.obj = NULL; + buf.buf = NULL; + if (!PyArg_ParseTuple(args, "i|w*:read", &len, &buf)) goto error; - if ((dest == NULL) || (dest == Py_None)) { - if (!(dest = PyByteArray_FromStringAndSize((char *) 0, len))) - goto error; - mem = PyByteArray_AS_STRING(dest); - } else if (PyLong_Check(dest)) { - len = PyLong_AS_LONG(dest); - if (!(dest = PyByteArray_FromStringAndSize((char *) 0, len))) - goto error; - mem = PyByteArray_AS_STRING(dest); - } else { - if (PyObject_GetBuffer(dest, &buf, PyBUF_CONTIG) < 0) + if ((buf.buf == NULL) && (buf.obj == NULL)) { + dest = PyBytes_FromStringAndSize(NULL, len); + if (dest == NULL) goto error; - mem = buf.buf; - len = buf.len; - if ((count > 0) && (count <= len)) - len = count; + mem = PyBytes_AS_STRING(dest); + } + else { buf_passed = 1; + mem = buf.buf; + if (len <= 0 || len > buf.len) { + len = (int) buf.len; + if (buf.len != len) { + PyErr_SetString(PyExc_OverflowError, + "maximum length can't fit in a C 'int'"); + goto error; + } + } } /* just in case the blocking state of the socket has been changed */ @@ -1375,23 +1375,24 @@ PySSL_SetError(self, count, __FILE__, __LINE__); goto error; } - done: + +done: Py_DECREF(sock); if (!buf_passed) { - PyObject *res = PyBytes_FromStringAndSize(mem, count); - Py_DECREF(dest); - return res; - } else { + _PyBytes_Resize(&dest, count); + return dest; + } + else { PyBuffer_Release(&buf); return PyLong_FromLong(count); } - error: + +error: Py_DECREF(sock); - if (!buf_passed) { + if (!buf_passed) Py_XDECREF(dest); - } else { + else PyBuffer_Release(&buf); - } return NULL; } From python-checkins at python.org Fri Sep 3 21:08:36 2010 From: python-checkins at python.org (florent.xicluna) Date: Fri, 3 Sep 2010 21:08:36 +0200 (CEST) Subject: [Python-checkins] r84466 - peps/trunk/pep-0001.txt Message-ID: <20100903190836.7F6B9EEA15@mail.python.org> Author: florent.xicluna Date: Fri Sep 3 21:08:36 2010 New Revision: 84466 Log: Remove reference to python-3000 mailing list in PEP1 Modified: peps/trunk/pep-0001.txt Modified: peps/trunk/pep-0001.txt ============================================================================== --- peps/trunk/pep-0001.txt (original) +++ peps/trunk/pep-0001.txt Fri Sep 3 21:08:36 2010 @@ -439,7 +439,7 @@ properly. * Send email back to the PEP author with next steps (post to - python-list & -dev/-3000). + python-list & -dev). Updates to existing PEPs also come in to peps at python.org. Many PEP authors are not SVN committers yet, so we do the commits for them. From python-checkins at python.org Fri Sep 3 21:52:04 2010 From: python-checkins at python.org (florent.xicluna) Date: Fri, 3 Sep 2010 21:52:04 +0200 (CEST) Subject: [Python-checkins] r84467 - in python/branches/py3k/Lib: keyword.py pdb.py platform.py turtle.py Message-ID: <20100903195204.36E17F682@mail.python.org> Author: florent.xicluna Date: Fri Sep 3 21:52:03 2010 New Revision: 84467 Log: Use a context manager for some file objects. Modified: python/branches/py3k/Lib/keyword.py python/branches/py3k/Lib/pdb.py python/branches/py3k/Lib/platform.py python/branches/py3k/Lib/turtle.py Modified: python/branches/py3k/Lib/keyword.py ============================================================================== --- python/branches/py3k/Lib/keyword.py (original) +++ python/branches/py3k/Lib/keyword.py Fri Sep 3 21:52:03 2010 @@ -61,21 +61,19 @@ else: optfile = "Lib/keyword.py" # scan the source file for keywords - fp = open(iptfile) - strprog = re.compile('"([^"]+)"') - lines = [] - for line in fp: - if '{1, "' in line: - match = strprog.search(line) - if match: - lines.append(" '" + match.group(1) + "',\n") - fp.close() + with open(iptfile) as fp: + strprog = re.compile('"([^"]+)"') + lines = [] + for line in fp: + if '{1, "' in line: + match = strprog.search(line) + if match: + lines.append(" '" + match.group(1) + "',\n") lines.sort() # load the output skeleton from the target - fp = open(optfile) - format = fp.readlines() - fp.close() + with open(optfile) as fp: + format = fp.readlines() # insert the lines of keywords try: Modified: python/branches/py3k/Lib/pdb.py ============================================================================== --- python/branches/py3k/Lib/pdb.py (original) +++ python/branches/py3k/Lib/pdb.py Fri Sep 3 21:52:03 2010 @@ -155,21 +155,15 @@ if 'HOME' in os.environ: envHome = os.environ['HOME'] try: - rcFile = open(os.path.join(envHome, ".pdbrc")) + with open(os.path.join(envHome, ".pdbrc")) as rcFile: + self.rcLines.extend(rcFile) except IOError: pass - else: - for line in rcFile.readlines(): - self.rcLines.append(line) - rcFile.close() try: - rcFile = open(".pdbrc") + with open(".pdbrc") as rcFile: + self.rcLines.extend(rcFile) except IOError: pass - else: - for line in rcFile.readlines(): - self.rcLines.append(line) - rcFile.close() self.commands = {} # associates a command list to breakpoint numbers self.commands_doprompt = {} # for each bp num, tells if the prompt Modified: python/branches/py3k/Lib/platform.py ============================================================================== --- python/branches/py3k/Lib/platform.py (original) +++ python/branches/py3k/Lib/platform.py Fri Sep 3 21:52:03 2010 @@ -200,9 +200,8 @@ """ if os.path.exists('/var/adm/inst-log/info'): # SuSE Linux stores distribution information in that file - info = open('/var/adm/inst-log/info').readlines() distname = 'SuSE' - for line in info: + for line in open('/var/adm/inst-log/info'): tv = line.split() if len(tv) == 2: tag,value = tv @@ -217,8 +216,7 @@ if os.path.exists('/etc/.installed'): # Caldera OpenLinux has some infos in that file (thanks to Colin Kong) - info = open('/etc/.installed').readlines() - for line in info: + for line in open('/etc/.installed'): pkg = line.split('-') if len(pkg) >= 2 and pkg[0] == 'OpenLinux': # XXX does Caldera support non Intel platforms ? If yes, @@ -327,9 +325,8 @@ return _dist_try_harder(distname,version,id) # Read the first line - f = open('/etc/'+file, 'r') - firstline = f.readline() - f.close() + with open('/etc/'+file, 'r') as f: + firstline = f.readline() _distname, _version, _id = _parse_release_file(firstline) if _distname and full_distribution_name: Modified: python/branches/py3k/Lib/turtle.py ============================================================================== --- python/branches/py3k/Lib/turtle.py (original) +++ python/branches/py3k/Lib/turtle.py Fri Sep 3 21:52:03 2010 @@ -169,9 +169,8 @@ def config_dict(filename): """Convert content of config-file into dictionary.""" - f = open(filename, "r") - cfglines = f.readlines() - f.close() + with open(filename, "r") as f: + cfglines = f.readlines() cfgdict = {} for line in cfglines: line = line.strip() From python-checkins at python.org Fri Sep 3 21:54:03 2010 From: python-checkins at python.org (florent.xicluna) Date: Fri, 3 Sep 2010 21:54:03 +0200 (CEST) Subject: [Python-checkins] r84468 - python/branches/py3k/Lib/tokenize.py Message-ID: <20100903195403.06379F0B1@mail.python.org> Author: florent.xicluna Date: Fri Sep 3 21:54:02 2010 New Revision: 84468 Log: Remove unused import, fix typo and rewrap docstrings. Modified: python/branches/py3k/Lib/tokenize.py Modified: python/branches/py3k/Lib/tokenize.py ============================================================================== --- python/branches/py3k/Lib/tokenize.py (original) +++ python/branches/py3k/Lib/tokenize.py Fri Sep 3 21:54:02 2010 @@ -1,12 +1,12 @@ """Tokenization help for Python programs. -tokenize(readline) is a generator that breaks a stream of -bytes into Python tokens. It decodes the bytes according to -PEP-0263 for determining source file encoding. - -It accepts a readline-like method which is called -repeatedly to get the next line of input (or b"" for EOF). It generates -5-tuples with these members: +tokenize(readline) is a generator that breaks a stream of bytes into +Python tokens. It decodes the bytes according to PEP-0263 for +determining source file encoding. + +It accepts a readline-like method which is called repeatedly to get the +next line of input (or b"" for EOF). It generates 5-tuples with these +members: the token type (see token.py) the token (a string) @@ -16,14 +16,16 @@ It is designed to match the working of the Python tokenizer exactly, except that it produces COMMENT tokens for comments and gives type OP for all -operators. Aditionally, all token lists start with an ENCODING token -which tells you which encoding was used to decode the bytes stream.""" +operators. Additionally, all token lists start with an ENCODING token +which tells you which encoding was used to decode the bytes stream. +""" __author__ = 'Ka-Ping Yee ' __credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, ' 'Skip Montanaro, Raymond Hettinger, Trent Nelson, ' 'Michael Foord') -import re, string, sys +import re +import sys from token import * from codecs import lookup, BOM_UTF8 cookie_re = re.compile("coding[:=]\s*([-\w.]+)") @@ -298,17 +300,16 @@ def detect_encoding(readline): """ The detect_encoding() function is used to detect the encoding that should - be used to decode a Python source file. It requires one argment, readline, + be used to decode a Python source file. It requires one argment, readline, in the same way as the tokenize() generator. It will call readline a maximum of twice, and return the encoding used - (as a string) and a list of any lines (left as bytes) it has read - in. + (as a string) and a list of any lines (left as bytes) it has read in. It detects the encoding from the presence of a utf-8 bom or an encoding - cookie as specified in pep-0263. If both a bom and a cookie are present, but - disagree, a SyntaxError will be raised. If the encoding cookie is an invalid - charset, raise a SyntaxError. Note that if a utf-8 bom is found, + cookie as specified in pep-0263. If both a bom and a cookie are present, + but disagree, a SyntaxError will be raised. If the encoding cookie is an + invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, 'utf-8-sig' is returned. If no encoding is specified, then the default of 'utf-8' will be returned. @@ -372,7 +373,7 @@ """ The tokenize() generator requires one argment, readline, which must be a callable object which provides the same interface as the - readline() method of built-in file objects. Each call to the function + readline() method of built-in file objects. Each call to the function should return one line of input as bytes. Alternately, readline can be a callable function terminating with StopIteration: readline = open(myfile, 'rb').__next__ # Example of alternate readline @@ -381,7 +382,7 @@ token string; a 2-tuple (srow, scol) of ints specifying the row and column where the token begins in the source; a 2-tuple (erow, ecol) of ints specifying the row and column where the token ends in the source; - and the line on which the token was found. The line passed is the + and the line on which the token was found. The line passed is the logical line; continuation lines are included. The first token sequence will always be an ENCODING token From python-checkins at python.org Fri Sep 3 21:55:26 2010 From: python-checkins at python.org (florent.xicluna) Date: Fri, 3 Sep 2010 21:55:26 +0200 (CEST) Subject: [Python-checkins] r84469 - python/branches/py3k/Lib/importlib/_bootstrap.py Message-ID: <20100903195526.44830EEA30@mail.python.org> Author: florent.xicluna Date: Fri Sep 3 21:55:26 2010 New Revision: 84469 Log: Remove redundant context manager. Modified: python/branches/py3k/Lib/importlib/_bootstrap.py Modified: python/branches/py3k/Lib/importlib/_bootstrap.py ============================================================================== --- python/branches/py3k/Lib/importlib/_bootstrap.py (original) +++ python/branches/py3k/Lib/importlib/_bootstrap.py Fri Sep 3 21:55:26 2010 @@ -80,20 +80,6 @@ return _path_join(_os.getcwd(), path) -class _closing: - - """Simple replacement for contextlib.closing.""" - - def __init__(self, obj): - self.obj = obj - - def __enter__(self): - return self.obj - - def __exit__(self, *args): - self.obj.close() - - def _wrap(new, old): """Simple substitute for functools.wraps.""" for replace in ['__module__', '__name__', '__doc__']: @@ -468,7 +454,7 @@ def get_data(self, path): """Return the data from path as raw bytes.""" - with _closing(_io.FileIO(path, 'r')) as file: + with _io.FileIO(path, 'r') as file: return file.read() From python-checkins at python.org Fri Sep 3 22:00:37 2010 From: python-checkins at python.org (florent.xicluna) Date: Fri, 3 Sep 2010 22:00:37 +0200 (CEST) Subject: [Python-checkins] r84470 - python/branches/py3k/Lib/test/test_bytes.py Message-ID: <20100903200037.38A7FF4A9@mail.python.org> Author: florent.xicluna Date: Fri Sep 3 22:00:37 2010 New Revision: 84470 Log: Strengthen BytesWarning tests. Modified: python/branches/py3k/Lib/test/test_bytes.py Modified: python/branches/py3k/Lib/test/test_bytes.py ============================================================================== --- python/branches/py3k/Lib/test/test_bytes.py (original) +++ python/branches/py3k/Lib/test/test_bytes.py Fri Sep 3 22:00:37 2010 @@ -9,15 +9,28 @@ import re import sys import copy -import operator +import functools import pickle import tempfile import unittest -import warnings import test.support import test.string_tests import test.buffer_tests + +if sys.flags.bytes_warning: + def check_bytes_warnings(func): + @functools.wraps(func) + def wrapper(*args, **kw): + with test.support.check_warnings(('', BytesWarning)): + return func(*args, **kw) + return wrapper +else: + # no-op + def check_bytes_warnings(func): + return func + + class Indexable: def __init__(self, value=0): self.value = value @@ -121,20 +134,19 @@ self.assertFalse(b3 < b2) self.assertFalse(b3 <= b2) + @check_bytes_warnings def test_compare_to_str(self): - with test.support.check_warnings(): - warnings.simplefilter('ignore', BytesWarning) - # Byte comparisons with unicode should always fail! - # Test this for all expected byte orders and Unicode character - # sizes. - self.assertEqual(self.type2test(b"\0a\0b\0c") == "abc", False) - self.assertEqual(self.type2test(b"\0\0\0a\0\0\0b\0\0\0c") == "abc", - False) - self.assertEqual(self.type2test(b"a\0b\0c\0") == "abc", False) - self.assertEqual(self.type2test(b"a\0\0\0b\0\0\0c\0\0\0") == "abc", - False) - self.assertEqual(self.type2test() == str(), False) - self.assertEqual(self.type2test() != str(), True) + # Byte comparisons with unicode should always fail! + # Test this for all expected byte orders and Unicode character + # sizes. + self.assertEqual(self.type2test(b"\0a\0b\0c") == "abc", False) + self.assertEqual(self.type2test(b"\0\0\0a\0\0\0b\0\0\0c") == "abc", + False) + self.assertEqual(self.type2test(b"a\0b\0c\0") == "abc", False) + self.assertEqual(self.type2test(b"a\0\0\0b\0\0\0c\0\0\0") == "abc", + False) + self.assertEqual(self.type2test() == str(), False) + self.assertEqual(self.type2test() != str(), True) def test_reversed(self): input = list(map(ord, "Hello")) @@ -823,17 +835,16 @@ # Test various combinations of bytes and bytearray # + @check_bytes_warnings def test_repr_str(self): - with test.support.check_warnings(): - warnings.simplefilter('ignore', BytesWarning) - for f in str, repr: - self.assertEqual(f(bytearray()), "bytearray(b'')") - self.assertEqual(f(bytearray([0])), "bytearray(b'\\x00')") - self.assertEqual(f(bytearray([0, 1, 254, 255])), - "bytearray(b'\\x00\\x01\\xfe\\xff')") - self.assertEqual(f(b"abc"), "b'abc'") - self.assertEqual(f(b"'"), '''b"'"''') # ''' - self.assertEqual(f(b"'\""), r"""b'\'"'""") # ' + for f in str, repr: + self.assertEqual(f(bytearray()), "bytearray(b'')") + self.assertEqual(f(bytearray([0])), "bytearray(b'\\x00')") + self.assertEqual(f(bytearray([0, 1, 254, 255])), + "bytearray(b'\\x00\\x01\\xfe\\xff')") + self.assertEqual(f(b"abc"), "b'abc'") + self.assertEqual(f(b"'"), '''b"'"''') # ''' + self.assertEqual(f(b"'\""), r"""b'\'"'""") # ' def test_compare_bytes_to_bytearray(self): self.assertEqual(b"abc" == bytes(b"abc"), True) @@ -876,15 +887,14 @@ b = bytearray(buf) self.assertEqual(b, bytearray(sample)) + @check_bytes_warnings def test_to_str(self): - with test.support.check_warnings(): - warnings.simplefilter('ignore', BytesWarning) - self.assertEqual(str(b''), "b''") - self.assertEqual(str(b'x'), "b'x'") - self.assertEqual(str(b'\x80'), "b'\\x80'") - self.assertEqual(str(bytearray(b'')), "bytearray(b'')") - self.assertEqual(str(bytearray(b'x')), "bytearray(b'x')") - self.assertEqual(str(bytearray(b'\x80')), "bytearray(b'\\x80')") + self.assertEqual(str(b''), "b''") + self.assertEqual(str(b'x'), "b'x'") + self.assertEqual(str(b'\x80'), "b'\\x80'") + self.assertEqual(str(bytearray(b'')), "bytearray(b'')") + self.assertEqual(str(bytearray(b'x')), "bytearray(b'x')") + self.assertEqual(str(bytearray(b'\x80')), "bytearray(b'\\x80')") def test_literal(self): tests = [ @@ -930,19 +940,18 @@ def test_compare(self): if sys.flags.bytes_warning: - with test.support.check_warnings(): - warnings.simplefilter('error', BytesWarning) - with self.assertRaises(BytesWarning): - b'' == '' - with self.assertRaises(BytesWarning): - b'' != '' - with self.assertRaises(BytesWarning): - bytearray(b'') == '' - with self.assertRaises(BytesWarning): - bytearray(b'') != '' + def bytes_warning(): + return test.support.check_warnings(('', BytesWarning)) + with bytes_warning(): + b'' == '' + with bytes_warning(): + b'' != '' + with bytes_warning(): + bytearray(b'') == '' + with bytes_warning(): + bytearray(b'') != '' else: - # self.skipTest("BytesWarning is needed for this test: use -bb option") - pass + self.skipTest("BytesWarning is needed for this test: use -bb option") # Optimizations: # __iter__? (optimization) From python-checkins at python.org Fri Sep 3 22:23:40 2010 From: python-checkins at python.org (florent.xicluna) Date: Fri, 3 Sep 2010 22:23:40 +0200 (CEST) Subject: [Python-checkins] r84471 - python/branches/py3k/Doc/reference/simple_stmts.rst Message-ID: <20100903202340.8324AF1F2@mail.python.org> Author: florent.xicluna Date: Fri Sep 3 22:23:40 2010 New Revision: 84471 Log: Typo Modified: python/branches/py3k/Doc/reference/simple_stmts.rst Modified: python/branches/py3k/Doc/reference/simple_stmts.rst ============================================================================== --- python/branches/py3k/Doc/reference/simple_stmts.rst (original) +++ python/branches/py3k/Doc/reference/simple_stmts.rst Fri Sep 3 22:23:40 2010 @@ -823,7 +823,7 @@ exists. Two dots means up one package level. Three dots is up two levels, etc. So if you execute ``from . import mod`` from a module in the ``pkg`` package then you will end up importing ``pkg.mod``. If you execute ``from ..subpkg2 -imprt mod`` from within ``pkg.subpkg1`` you will import ``pkg.subpkg2.mod``. +import mod`` from within ``pkg.subpkg1`` you will import ``pkg.subpkg2.mod``. The specification for relative imports is contained within :pep:`328`. :func:`importlib.import_module` is provided to support applications that From python-checkins at python.org Sat Sep 4 00:03:11 2010 From: python-checkins at python.org (eric.araujo) Date: Sat, 4 Sep 2010 00:03:11 +0200 (CEST) Subject: [Python-checkins] r84472 - python/branches/py3k/Modules/_heapqmodule.c Message-ID: <20100903220311.162C9F7F4@mail.python.org> Author: eric.araujo Date: Sat Sep 4 00:03:10 2010 New Revision: 84472 Log: Fix invalid bytes for UTF-8 Modified: python/branches/py3k/Modules/_heapqmodule.c Modified: python/branches/py3k/Modules/_heapqmodule.c ============================================================================== --- python/branches/py3k/Modules/_heapqmodule.c (original) +++ python/branches/py3k/Modules/_heapqmodule.c Sat Sep 4 00:03:10 2010 @@ -2,7 +2,7 @@ C implementation derived directly from heapq.py in Py2.3 which was written by Kevin O'Connor, augmented by Tim Peters, -annotated by Fran?ois Pinard, and converted to C by Raymond Hettinger. +annotated by Fran??ois Pinard, and converted to C by Raymond Hettinger. */ From python-checkins at python.org Sat Sep 4 00:05:17 2010 From: python-checkins at python.org (eric.araujo) Date: Sat, 4 Sep 2010 00:05:17 +0200 (CEST) Subject: [Python-checkins] r84473 - in python/branches/release31-maint: Modules/_heapqmodule.c Message-ID: <20100903220517.5B4A1F269@mail.python.org> Author: eric.araujo Date: Sat Sep 4 00:05:17 2010 New Revision: 84473 Log: Merged revisions 84472 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84472 | eric.araujo | 2010-09-04 00:03:10 +0200 (sam., 04 sept. 2010) | 2 lines Fix invalid bytes for UTF-8 ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Modules/_heapqmodule.c Modified: python/branches/release31-maint/Modules/_heapqmodule.c ============================================================================== --- python/branches/release31-maint/Modules/_heapqmodule.c (original) +++ python/branches/release31-maint/Modules/_heapqmodule.c Sat Sep 4 00:05:17 2010 @@ -2,7 +2,7 @@ C implementation derived directly from heapq.py in Py2.3 which was written by Kevin O'Connor, augmented by Tim Peters, -annotated by Fran?ois Pinard, and converted to C by Raymond Hettinger. +annotated by Fran??ois Pinard, and converted to C by Raymond Hettinger. */ From python-checkins at python.org Sat Sep 4 00:06:31 2010 From: python-checkins at python.org (eric.araujo) Date: Sat, 4 Sep 2010 00:06:31 +0200 (CEST) Subject: [Python-checkins] r84474 - in python/branches/release27-maint: Modules/_heapqmodule.c Message-ID: <20100903220631.3BF18F46E@mail.python.org> Author: eric.araujo Date: Sat Sep 4 00:06:31 2010 New Revision: 84474 Log: Merged revisions 84472 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84472 | eric.araujo | 2010-09-04 00:03:10 +0200 (sam., 04 sept. 2010) | 2 lines Fix invalid bytes for UTF-8 ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Modules/_heapqmodule.c Modified: python/branches/release27-maint/Modules/_heapqmodule.c ============================================================================== --- python/branches/release27-maint/Modules/_heapqmodule.c (original) +++ python/branches/release27-maint/Modules/_heapqmodule.c Sat Sep 4 00:06:31 2010 @@ -2,7 +2,7 @@ C implementation derived directly from heapq.py in Py2.3 which was written by Kevin O'Connor, augmented by Tim Peters, -annotated by Fran?ois Pinard, and converted to C by Raymond Hettinger. +annotated by Fran??ois Pinard, and converted to C by Raymond Hettinger. */ From python-checkins at python.org Sat Sep 4 00:13:42 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 4 Sep 2010 00:13:42 +0200 (CEST) Subject: [Python-checkins] r84475 - python/branches/py3k/Doc/c-api/unicode.rst Message-ID: <20100903221342.A1D8DC621@mail.python.org> Author: victor.stinner Date: Sat Sep 4 00:13:42 2010 New Revision: 84475 Log: Add versionadded tag to PyUnicode_AsUnicodeCopy's doc Modified: python/branches/py3k/Doc/c-api/unicode.rst Modified: python/branches/py3k/Doc/c-api/unicode.rst ============================================================================== --- python/branches/py3k/Doc/c-api/unicode.rst (original) +++ python/branches/py3k/Doc/c-api/unicode.rst Sat Sep 4 00:13:42 2010 @@ -342,6 +342,8 @@ otherwise return a new allocated buffer (use :cfunc:`PyMem_Free` to free the buffer). + .. versionadded:: 3.2 + .. cfunction:: Py_ssize_t PyUnicode_GetSize(PyObject *unicode) From python-checkins at python.org Sat Sep 4 00:14:52 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 4 Sep 2010 00:14:52 +0200 (CEST) Subject: [Python-checkins] r84476 - python/branches/py3k/Makefile.pre.in Message-ID: <20100903221452.DB294EAD1@mail.python.org> Author: georg.brandl Date: Sat Sep 4 00:14:52 2010 New Revision: 84476 Log: Use tabs consistently. Modified: python/branches/py3k/Makefile.pre.in Modified: python/branches/py3k/Makefile.pre.in ============================================================================== --- python/branches/py3k/Makefile.pre.in (original) +++ python/branches/py3k/Makefile.pre.in Sat Sep 4 00:14:52 2010 @@ -35,9 +35,9 @@ AR= @AR@ RANLIB= @RANLIB@ SVNVERSION= @SVNVERSION@ -SOABI= @SOABI@ +SOABI= @SOABI@ -GNULD= @GNULD@ +GNULD= @GNULD@ # Shell used by make (some versions default to the login shell, which is bad) SHELL= /bin/sh From python-checkins at python.org Sat Sep 4 00:19:08 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 4 Sep 2010 00:19:08 +0200 (CEST) Subject: [Python-checkins] r84477 - python/branches/py3k/configure.in Message-ID: <20100903221908.0B0F3F569@mail.python.org> Author: georg.brandl Date: Sat Sep 4 00:19:07 2010 New Revision: 84477 Log: Fix typos and use tabs consistently. Modified: python/branches/py3k/configure.in Modified: python/branches/py3k/configure.in ============================================================================== --- python/branches/py3k/configure.in (original) +++ python/branches/py3k/configure.in Sat Sep 4 00:19:07 2010 @@ -52,7 +52,7 @@ AC_SUBST(VERSION) VERSION=PYTHON_VERSION -# Version number or Python's own shared library file. +# Version number of Python's own shared library file. AC_SUBST(SOVERSION) SOVERSION=1.0 @@ -3630,8 +3630,8 @@ # * --with-wide-unicode (adds a 'u') # # Thus for example, Python 3.2 built with wide unicode, pydebug, and pymalloc, -# would get a shared library ABI version tag of 'cpython-32udm' and shared -# libraries would be named 'foo.cpython-32udm.so'. +# would get a shared library ABI version tag of 'cpython-32dmu' and shared +# libraries would be named 'foo.cpython-32dmu.so'. AC_SUBST(SOABI) AC_MSG_CHECKING(SOABI) SOABI='cpython-'`echo $VERSION | tr -d .`${SOABI_QUALIFIERS} @@ -3650,19 +3650,19 @@ esac ;; CYGWIN*) SO=.dll;; - Linux*) SO=.${SOABI}.so;; + Linux*) SO=.${SOABI}.so;; *) SO=.so;; esac else # this might also be a termcap variable, see #610332 - echo - echo '=====================================================================' - echo '+ +' + echo + echo '=====================================================================' + echo '+ +' echo '+ WARNING: You have set SO in your environment. +' - echo '+ Do you really mean to change the extension for shared libraries? +' - echo '+ Continuing in 10 seconds to let you to ponder. +' - echo '+ +' - echo '=====================================================================' + echo '+ Do you really mean to change the extension for shared libraries? +' + echo '+ Continuing in 10 seconds to let you to ponder. +' + echo '+ +' + echo '=====================================================================' sleep 10 fi AC_MSG_RESULT($SO) From python-checkins at python.org Sat Sep 4 00:20:58 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 4 Sep 2010 00:20:58 +0200 (CEST) Subject: [Python-checkins] r84478 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100903222058.EA688C4CE@mail.python.org> Author: georg.brandl Date: Sat Sep 4 00:20:58 2010 New Revision: 84478 Log: Make PEP section a bit less ugly and at least list implemented PEPs. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sat Sep 4 00:20:58 2010 @@ -51,8 +51,13 @@ This article explains the new features in Python 3.2, compared to 3.1. -PEP XXX: Stub -============= +PEPs +==== + +Implemented PEPs: + +* :pep:`3147` +* :pep:`3149` Other Language Changes From python-checkins at python.org Sat Sep 4 00:29:08 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 4 Sep 2010 00:29:08 +0200 (CEST) Subject: [Python-checkins] r84479 - python/branches/py3k/pyconfig.h.in Message-ID: <20100903222908.99C1BEBC8@mail.python.org> Author: georg.brandl Date: Sat Sep 4 00:29:08 2010 New Revision: 84479 Log: Revert unintended change from r84458. Modified: python/branches/py3k/pyconfig.h.in Modified: python/branches/py3k/pyconfig.h.in ============================================================================== --- python/branches/py3k/pyconfig.h.in (original) +++ python/branches/py3k/pyconfig.h.in Sat Sep 4 00:29:08 2010 @@ -1040,7 +1040,8 @@ /* Define to 1 if your declares `struct tm'. */ #undef TM_IN_SYS_TIME -/* Define if you want to use computed gotos in ceval.c. */ +/* Define to 0 if you don't want to use computed gotos in ceval.c. + (The default is to use them if HAVE_COMPUTED_GOTOS is defined.) */ #undef USE_COMPUTED_GOTOS /* Define if the compiler supports the inline keyword */ From python-checkins at python.org Sat Sep 4 00:33:27 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 4 Sep 2010 00:33:27 +0200 (CEST) Subject: [Python-checkins] r84480 - in python/branches/py3k/Doc: conf.py tools/sphinxext/indexcontent.html using/index.rst Message-ID: <20100903223327.3B5D8F513@mail.python.org> Author: georg.brandl Date: Sat Sep 4 00:33:27 2010 New Revision: 84480 Log: More inclusive title. Modified: python/branches/py3k/Doc/conf.py python/branches/py3k/Doc/tools/sphinxext/indexcontent.html python/branches/py3k/Doc/using/index.rst Modified: python/branches/py3k/Doc/conf.py ============================================================================== --- python/branches/py3k/Doc/conf.py (original) +++ python/branches/py3k/Doc/conf.py Sat Sep 4 00:33:27 2010 @@ -131,7 +131,7 @@ ('tutorial/index', 'tutorial.tex', 'Python Tutorial', _stdauthor, 'manual'), ('using/index', 'using.tex', - 'Python Setup', _stdauthor, 'manual'), + 'Python Setup and Usage', _stdauthor, 'manual'), ('whatsnew/' + version, 'whatsnew.tex', 'What\'s New in Python', 'A. M. Kuchling', 'howto'), ] Modified: python/branches/py3k/Doc/tools/sphinxext/indexcontent.html ============================================================================== --- python/branches/py3k/Doc/tools/sphinxext/indexcontent.html (original) +++ python/branches/py3k/Doc/tools/sphinxext/indexcontent.html Sat Sep 4 00:33:27 2010 @@ -11,7 +11,7 @@ keep this under your pillow

- Modified: python/branches/py3k/Doc/using/index.rst ============================================================================== --- python/branches/py3k/Doc/using/index.rst (original) +++ python/branches/py3k/Doc/using/index.rst Sat Sep 4 00:33:27 2010 @@ -1,8 +1,8 @@ .. _using-index: -################ - Python Setup -################ +########################## + Python Setup and Usage +########################## This part of the documentation is devoted to general information on the setup From python-checkins at python.org Sat Sep 4 00:36:23 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 4 Sep 2010 00:36:23 +0200 (CEST) Subject: [Python-checkins] r84481 - python/branches/py3k/Doc/library/json.rst Message-ID: <20100903223623.19ABDEB74@mail.python.org> Author: georg.brandl Date: Sat Sep 4 00:36:22 2010 New Revision: 84481 Log: #9767: doctest run over json docs. Modified: python/branches/py3k/Doc/library/json.rst Modified: python/branches/py3k/Doc/library/json.rst ============================================================================== --- python/branches/py3k/Doc/library/json.rst (original) +++ python/branches/py3k/Doc/library/json.rst Sat Sep 4 00:36:22 2010 @@ -82,12 +82,12 @@ ... return [obj.real, obj.imag] ... return json.JSONEncoder.default(self, obj) ... - >>> dumps(2 + 1j, cls=ComplexEncoder) + >>> json.dumps(2 + 1j, cls=ComplexEncoder) '[2.0, 1.0]' >>> ComplexEncoder().encode(2 + 1j) '[2.0, 1.0]' >>> list(ComplexEncoder().iterencode(2 + 1j)) - ['[', '2.0', ', ', '1.0', ']'] + ['[2.0', ', 1.0', ']'] .. highlight:: none @@ -373,7 +373,7 @@ pass else: return list(iterable) - return JSONEncoder.default(self, o) + return json.JSONEncoder.default(self, o) .. method:: encode(o) @@ -381,7 +381,7 @@ Return a JSON string representation of a Python data structure, *o*. For example:: - >>> JSONEncoder().encode({"foo": ["bar", "baz"]}) + >>> json.JSONEncoder().encode({"foo": ["bar", "baz"]}) '{"foo": ["bar", "baz"]}' @@ -390,5 +390,5 @@ Encode the given object, *o*, and yield each string representation as available. For example:: - for chunk in JSONEncoder().iterencode(bigobject): + for chunk in json.JSONEncoder().iterencode(bigobject): mysocket.write(chunk) From python-checkins at python.org Sat Sep 4 00:40:02 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 4 Sep 2010 00:40:02 +0200 (CEST) Subject: [Python-checkins] r84482 - python/branches/py3k/Doc/reference/compound_stmts.rst Message-ID: <20100903224002.8D0FEDA87@mail.python.org> Author: georg.brandl Date: Sat Sep 4 00:40:02 2010 New Revision: 84482 Log: #9760: clarify what context expression is. Modified: python/branches/py3k/Doc/reference/compound_stmts.rst Modified: python/branches/py3k/Doc/reference/compound_stmts.rst ============================================================================== --- python/branches/py3k/Doc/reference/compound_stmts.rst (original) +++ python/branches/py3k/Doc/reference/compound_stmts.rst Sat Sep 4 00:40:02 2010 @@ -350,7 +350,8 @@ The execution of the :keyword:`with` statement with one "item" proceeds as follows: -#. The context expression is evaluated to obtain a context manager. +#. The context expression (the expression given in the :token:`with_item`) is + evaluated to obtain a context manager. #. The context manager's :meth:`__exit__` is loaded for later use. From python-checkins at python.org Sat Sep 4 00:43:08 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Sat, 4 Sep 2010 00:43:08 +0200 (CEST) Subject: [Python-checkins] r84483 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100903224308.BFC8ADA87@mail.python.org> Author: amaury.forgeotdarc Date: Sat Sep 4 00:43:08 2010 New Revision: 84483 Log: Add an entry in whatsnew about the PyCObject -> PyCapsule move. (It seems that I am the first SWIG user to try python 3.2...) Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sat Sep 4 00:43:08 2010 @@ -282,3 +282,7 @@ * "t#" format has been removed: use "s#" or "s*" instead * "w" and "w#" formats has been removed: use "w*" instead +* The :ctype:`PyCObject` type, deprecated in 3.1, has been removed. To wrap + opaque C pointers in Python objects, the :ctype:`PyCapsule` API should be + used instead; the new type has a well defined interface for passing typing + safety information and a less complicated signature for calling a destructor. From python-checkins at python.org Sat Sep 4 00:49:27 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 4 Sep 2010 00:49:27 +0200 (CEST) Subject: [Python-checkins] r84484 - python/branches/py3k/Doc/library/multiprocessing.rst Message-ID: <20100903224927.F256BF513@mail.python.org> Author: georg.brandl Date: Sat Sep 4 00:49:27 2010 New Revision: 84484 Log: Fix missing word. Modified: python/branches/py3k/Doc/library/multiprocessing.rst Modified: python/branches/py3k/Doc/library/multiprocessing.rst ============================================================================== --- python/branches/py3k/Doc/library/multiprocessing.rst (original) +++ python/branches/py3k/Doc/library/multiprocessing.rst Sat Sep 4 00:49:27 2010 @@ -2221,8 +2221,8 @@ .. literalinclude:: ../includes/mp_synchronize.py -An showing how to use queues to feed tasks to a collection of worker process and -collect the results: +An example showing how to use queues to feed tasks to a collection of worker +process and collect the results: .. literalinclude:: ../includes/mp_workers.py From python-checkins at python.org Sat Sep 4 01:47:32 2010 From: python-checkins at python.org (florent.xicluna) Date: Sat, 4 Sep 2010 01:47:32 +0200 (CEST) Subject: [Python-checkins] r84485 - in python/branches/py3k: Modules/_sqlite/cache.c Modules/_sqlite/cache.h Modules/_sqlite/connection.c Modules/_sqlite/connection.h Modules/_sqlite/cursor.h Modules/_sqlite/module.c Modules/_sqlite/module.h Modules/_sqlite/prepare_protocol.c Modules/_sqlite/prepare_protocol.h Modules/_sqlite/row.c Modules/_sqlite/row.h Modules/_sqlite/sqlitecompat.h Modules/_sqlite/statement.c Modules/_sqlite/statement.h Modules/_sqlite/util.c Modules/_sqlite/util.h Modules/unicodedata.c PC/_msi.c Message-ID: <20100903234732.532F9D2A5@mail.python.org> Author: florent.xicluna Date: Sat Sep 4 01:47:32 2010 New Revision: 84485 Log: Welcome in the UTF-8 world. Modified: python/branches/py3k/Modules/_sqlite/cache.c python/branches/py3k/Modules/_sqlite/cache.h python/branches/py3k/Modules/_sqlite/connection.c python/branches/py3k/Modules/_sqlite/connection.h python/branches/py3k/Modules/_sqlite/cursor.h python/branches/py3k/Modules/_sqlite/module.c python/branches/py3k/Modules/_sqlite/module.h python/branches/py3k/Modules/_sqlite/prepare_protocol.c python/branches/py3k/Modules/_sqlite/prepare_protocol.h python/branches/py3k/Modules/_sqlite/row.c python/branches/py3k/Modules/_sqlite/row.h python/branches/py3k/Modules/_sqlite/sqlitecompat.h python/branches/py3k/Modules/_sqlite/statement.c python/branches/py3k/Modules/_sqlite/statement.h python/branches/py3k/Modules/_sqlite/util.c python/branches/py3k/Modules/_sqlite/util.h python/branches/py3k/Modules/unicodedata.c python/branches/py3k/PC/_msi.c Modified: python/branches/py3k/Modules/_sqlite/cache.c ============================================================================== --- python/branches/py3k/Modules/_sqlite/cache.c (original) +++ python/branches/py3k/Modules/_sqlite/cache.c Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* cache .c - a LRU cache * - * Copyright (C) 2004-2010 Gerhard H?ring + * Copyright (C) 2004-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/cache.h ============================================================================== --- python/branches/py3k/Modules/_sqlite/cache.h (original) +++ python/branches/py3k/Modules/_sqlite/cache.h Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* cache.h - definitions for the LRU cache * - * Copyright (C) 2004-2010 Gerhard H?ring + * Copyright (C) 2004-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/connection.c ============================================================================== --- python/branches/py3k/Modules/_sqlite/connection.c (original) +++ python/branches/py3k/Modules/_sqlite/connection.c Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* connection.c - the connection type * - * Copyright (C) 2004-2010 Gerhard H?ring + * Copyright (C) 2004-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/connection.h ============================================================================== --- python/branches/py3k/Modules/_sqlite/connection.h (original) +++ python/branches/py3k/Modules/_sqlite/connection.h Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* connection.h - definitions for the connection type * - * Copyright (C) 2004-2010 Gerhard H?ring + * Copyright (C) 2004-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/cursor.h ============================================================================== --- python/branches/py3k/Modules/_sqlite/cursor.h (original) +++ python/branches/py3k/Modules/_sqlite/cursor.h Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* cursor.h - definitions for the cursor type * - * Copyright (C) 2004-2010 Gerhard H?ring + * Copyright (C) 2004-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/module.c ============================================================================== --- python/branches/py3k/Modules/_sqlite/module.c (original) +++ python/branches/py3k/Modules/_sqlite/module.c Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* module.c - the module itself * - * Copyright (C) 2004-2010 Gerhard H?ring + * Copyright (C) 2004-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/module.h ============================================================================== --- python/branches/py3k/Modules/_sqlite/module.h (original) +++ python/branches/py3k/Modules/_sqlite/module.h Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* module.h - definitions for the module * - * Copyright (C) 2004-2010 Gerhard H?ring + * Copyright (C) 2004-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/prepare_protocol.c ============================================================================== --- python/branches/py3k/Modules/_sqlite/prepare_protocol.c (original) +++ python/branches/py3k/Modules/_sqlite/prepare_protocol.c Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* prepare_protocol.c - the protocol for preparing values for SQLite * - * Copyright (C) 2005-2010 Gerhard H?ring + * Copyright (C) 2005-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/prepare_protocol.h ============================================================================== --- python/branches/py3k/Modules/_sqlite/prepare_protocol.h (original) +++ python/branches/py3k/Modules/_sqlite/prepare_protocol.h Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* prepare_protocol.h - the protocol for preparing values for SQLite * - * Copyright (C) 2005-2010 Gerhard H?ring + * Copyright (C) 2005-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/row.c ============================================================================== --- python/branches/py3k/Modules/_sqlite/row.c (original) +++ python/branches/py3k/Modules/_sqlite/row.c Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* row.c - an enhanced tuple for database rows * - * Copyright (C) 2005-2010 Gerhard H?ring + * Copyright (C) 2005-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/row.h ============================================================================== --- python/branches/py3k/Modules/_sqlite/row.h (original) +++ python/branches/py3k/Modules/_sqlite/row.h Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* row.h - an enhanced tuple for database rows * - * Copyright (C) 2005-2010 Gerhard H?ring + * Copyright (C) 2005-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/sqlitecompat.h ============================================================================== --- python/branches/py3k/Modules/_sqlite/sqlitecompat.h (original) +++ python/branches/py3k/Modules/_sqlite/sqlitecompat.h Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* sqlitecompat.h - compatibility macros * - * Copyright (C) 2006-2010 Gerhard H?ring + * Copyright (C) 2006-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/statement.c ============================================================================== --- python/branches/py3k/Modules/_sqlite/statement.c (original) +++ python/branches/py3k/Modules/_sqlite/statement.c Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* statement.c - the statement type * - * Copyright (C) 2005-2010 Gerhard H?ring + * Copyright (C) 2005-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/statement.h ============================================================================== --- python/branches/py3k/Modules/_sqlite/statement.h (original) +++ python/branches/py3k/Modules/_sqlite/statement.h Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* statement.h - definitions for the statement type * - * Copyright (C) 2005-2010 Gerhard H?ring + * Copyright (C) 2005-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/util.c ============================================================================== --- python/branches/py3k/Modules/_sqlite/util.c (original) +++ python/branches/py3k/Modules/_sqlite/util.c Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* util.c - various utility functions * - * Copyright (C) 2005-2010 Gerhard H?ring + * Copyright (C) 2005-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/_sqlite/util.h ============================================================================== --- python/branches/py3k/Modules/_sqlite/util.h (original) +++ python/branches/py3k/Modules/_sqlite/util.h Sat Sep 4 01:47:32 2010 @@ -1,6 +1,6 @@ /* util.h - various utility functions * - * Copyright (C) 2005-2010 Gerhard H?ring + * Copyright (C) 2005-2010 Gerhard H??ring * * This file is part of pysqlite. * Modified: python/branches/py3k/Modules/unicodedata.c ============================================================================== --- python/branches/py3k/Modules/unicodedata.c (original) +++ python/branches/py3k/Modules/unicodedata.c Sat Sep 4 01:47:32 2010 @@ -6,7 +6,7 @@ Written by Marc-Andre Lemburg (mal at lemburg.com). Modified for Python 2.0 by Fredrik Lundh (fredrik at pythonware.com) - Modified by Martin v. L?wis (martin at v.loewis.de) + Modified by Martin v. L??wis (martin at v.loewis.de) Copyright (c) Corporation for National Research Initiatives. Modified: python/branches/py3k/PC/_msi.c ============================================================================== --- python/branches/py3k/PC/_msi.c (original) +++ python/branches/py3k/PC/_msi.c Sat Sep 4 01:47:32 2010 @@ -1,5 +1,5 @@ /* Helper library for MSI creation with Python. - * Copyright (C) 2005 Martin v. L?wis + * Copyright (C) 2005 Martin v. L??wis * Licensed to PSF under a contributor agreement. */ From python-checkins at python.org Sat Sep 4 01:56:06 2010 From: python-checkins at python.org (florent.xicluna) Date: Sat, 4 Sep 2010 01:56:06 +0200 (CEST) Subject: [Python-checkins] r84485 - svn:log Message-ID: <20100903235606.A70E1EFB5@mail.python.org> Author: florent.xicluna Revision: 84485 Property Name: svn:log Action: modified Property diff: --- old property value +++ new property value @@ -1 +1 @@ -Welcome in the UTF-8 world. \ No newline at end of file +Welcome to the UTF-8 world. From solipsis at pitrou.net Sat Sep 4 05:01:29 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 04 Sep 2010 05:01:29 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84485): sum=0 Message-ID: py3k results for svn r84485 (hg cset 1fbac42d64e5) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogrxB6b0', '-x'] From python-checkins at python.org Sat Sep 4 06:35:35 2010 From: python-checkins at python.org (fred.drake) Date: Sat, 4 Sep 2010 06:35:35 +0200 (CEST) Subject: [Python-checkins] r84486 - in python/branches/py3k: Doc/library/configparser.rst Lib/configparser.py Lib/test/test_cfgparser.py Misc/NEWS Message-ID: <20100904043535.2AEE5EB74@mail.python.org> Author: fred.drake Date: Sat Sep 4 06:35:34 2010 New Revision: 84486 Log: add consistent support for the vars and default arguments on all configuration parser classes (http://bugs.python.org/issue9421) Modified: python/branches/py3k/Doc/library/configparser.rst python/branches/py3k/Lib/configparser.py python/branches/py3k/Lib/test/test_cfgparser.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/configparser.rst ============================================================================== --- python/branches/py3k/Doc/library/configparser.rst (original) +++ python/branches/py3k/Doc/library/configparser.rst Sat Sep 4 06:35:34 2010 @@ -135,7 +135,7 @@ *empty_lines_in_values* were added. -.. class:: SafeConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=('#', ';'), strict=False, empty_lines_in_values=True) +.. class:: SafeConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, strict=False, empty_lines_in_values=True) Derived class of :class:`ConfigParser` that implements a sane variant of the magical interpolation feature. This implementation is more predictable as it @@ -155,7 +155,7 @@ *empty_lines_in_values* were added. -.. class:: ConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=('#', ';'), strict=False, empty_lines_in_values=True) +.. class:: ConfigParser(defaults=None, dict_type=collections.OrderedDict, allow_no_value=False, delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, strict=False, empty_lines_in_values=True) Derived class of :class:`RawConfigParser` that implements the magical interpolation feature and adds optional arguments to the :meth:`get` and @@ -366,39 +366,44 @@ Load configuration from a dictionary. Keys are section names, values are dictionaries with keys and values that should be present in the section. If the used dictionary type preserves order, sections and their keys will be - added in order. + added in order. Values are automatically converted to strings. Optional argument *source* specifies a context-specific name of the dictionary passed. If not given, ```` is used. .. versionadded:: 3.2 +.. method:: RawConfigParser.get(section, option, [vars, default]) -.. method:: RawConfigParser.get(section, option) - - Get an *option* value for the named *section*. + Get an *option* value for the named *section*. If *vars* is provided, it + must be a dictionary. The *option* is looked up in *vars* (if provided), + *section*, and in *DEFAULTSECT* in that order. If the key is not found and + *default* is provided, it is used as a fallback value. ``None`` can be + provided as a *default* value. -.. method:: RawConfigParser.getint(section, option) +.. method:: RawConfigParser.getint(section, option, [vars, default]) - A convenience method which coerces the *option* in the specified *section* to an - integer. + A convenience method which coerces the *option* in the specified *section* to + an integer. See :meth:`get` for explanation of *vars* and *default*. -.. method:: RawConfigParser.getfloat(section, option) +.. method:: RawConfigParser.getfloat(section, option, [vars, default]) - A convenience method which coerces the *option* in the specified *section* to a - floating point number. + A convenience method which coerces the *option* in the specified *section* to + a floating point number. See :meth:`get` for explanation of *vars* and + *default*. -.. method:: RawConfigParser.getboolean(section, option) +.. method:: RawConfigParser.getboolean(section, option, [vars, default]) - A convenience method which coerces the *option* in the specified *section* to a - Boolean value. Note that the accepted values for the option are ``"1"``, - ``"yes"``, ``"true"``, and ``"on"``, which cause this method to return ``True``, - and ``"0"``, ``"no"``, ``"false"``, and ``"off"``, which cause it to return - ``False``. These string values are checked in a case-insensitive manner. Any - other value will cause it to raise :exc:`ValueError`. + A convenience method which coerces the *option* in the specified *section* + to a Boolean value. Note that the accepted values for the option are + ``"1"``, ``"yes"``, ``"true"``, and ``"on"``, which cause this method to + return ``True``, and ``"0"``, ``"no"``, ``"false"``, and ``"off"``, which + cause it to return ``False``. These string values are checked in + a case-insensitive manner. Any other value will cause it to raise + :exc:`ValueError`. See :meth:`get` for explanation of *vars* and *default*. .. method:: RawConfigParser.items(section) @@ -475,17 +480,44 @@ for the interpolation. -.. method:: ConfigParser.get(section, option, raw=False, vars=None) +.. method:: ConfigParser.get(section, option, raw=False, [vars, default]) Get an *option* value for the named *section*. If *vars* is provided, it must be a dictionary. The *option* is looked up in *vars* (if provided), - *section*, and in *defaults* in that order. + *section*, and in *DEFAULTSECT* in that order. If the key is not found and + *default* is provided, it is used as a fallback value. ``None`` can be + provided as a *default* value. All the ``'%'`` interpolations are expanded in the return values, unless the *raw* argument is true. Values for interpolation keys are looked up in the same manner as the option. +.. method:: ConfigParser.getint(section, option, raw=False, [vars, default]) + + A convenience method which coerces the *option* in the specified *section* to + an integer. See :meth:`get` for explanation of *raw*, *vars* and *default*. + + +.. method:: ConfigParser.getfloat(section, option, raw=False, [vars, default]) + + A convenience method which coerces the *option* in the specified *section* to + a floating point number. See :meth:`get` for explanation of *raw*, *vars* + and *default*. + + +.. method:: ConfigParser.getboolean(section, option, raw=False, [vars, default]) + + A convenience method which coerces the *option* in the specified *section* + to a Boolean value. Note that the accepted values for the option are + ``"1"``, ``"yes"``, ``"true"``, and ``"on"``, which cause this method to + return ``True``, and ``"0"``, ``"no"``, ``"false"``, and ``"off"``, which + cause it to return ``False``. These string values are checked in + a case-insensitive manner. Any other value will cause it to raise + :exc:`ValueError`. See :meth:`get` for explanation of *raw*, *vars* and + *default*. + + .. method:: ConfigParser.items(section, raw=False, vars=None) Return a list of ``(name, value)`` pairs for each option in the given Modified: python/branches/py3k/Lib/configparser.py ============================================================================== --- python/branches/py3k/Lib/configparser.py (original) +++ python/branches/py3k/Lib/configparser.py Sat Sep 4 06:35:34 2010 @@ -24,9 +24,9 @@ methods: - __init__(defaults=None, dict_type=_default_dict, - delimiters=('=', ':'), comment_prefixes=('#', ';'), - strict=False, empty_lines_in_values=True, allow_no_value=False): + __init__(defaults=None, dict_type=_default_dict, allow_no_value=False, + delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, + strict=False, empty_lines_in_values=True): Create the parser. When `defaults' is given, it is initialized into the dictionary or intrinsic defaults. The keys must be strings, the values must be appropriate for %()s string interpolation. Note that `__name__' @@ -82,22 +82,24 @@ Read configuration from a dictionary. Keys are section names, values are dictionaries with keys and values that should be present in the section. If the used dictionary type preserves order, sections - and their keys will be added in order. + and their keys will be added in order. Values are automatically + converted to strings. - get(section, option, raw=False, vars=None) + get(section, option, raw=False, vars=None, default=_UNSET) Return a string value for the named option. All % interpolations are expanded in the return values, based on the defaults passed into the constructor and the DEFAULT section. Additional substitutions may be provided using the `vars' argument, which must be a dictionary whose - contents override any pre-existing defaults. + contents override any pre-existing defaults. If `option' is a key in + `vars', the value from `vars' is used. - getint(section, options) + getint(section, options, raw=False, vars=None, default=_UNSET) Like get(), but convert value to an integer. - getfloat(section, options) + getfloat(section, options, raw=False, vars=None, default=_UNSET) Like get(), but convert value to a float. - getboolean(section, options) + getboolean(section, options, raw=False, vars=None, default=_UNSET) Like get(), but convert value to a boolean (currently case insensitively defined as 0, false, no, off for False, and 1, true, yes, on for True). Returns False or True. @@ -353,6 +355,17 @@ self.args = (filename, lineno, line) +# Used in parsers to denote selecting a backwards-compatible inline comment +# character behavior (; and # are comments at the start of a line, but ; only +# inline) +_COMPATIBLE = object() + +# Used in parser getters to indicate the default behaviour when a specific +# option is not found it to raise an exception. Created to enable `None' as +# a valid fallback value. +_UNSET = object() + + class RawConfigParser: """ConfigParser that does not do interpolation.""" @@ -389,9 +402,9 @@ OPTCRE_NV = re.compile(_OPT_NV_TMPL.format(delim="=|:"), re.VERBOSE) # Compiled regular expression for matching leading whitespace in a line NONSPACECRE = re.compile(r"\S") - # Select backwards-compatible inline comment character behavior - # (; and # are comments at the start of a line, but ; only inline) - _COMPATIBLE = object() + # Possible boolean values in the configuration. + BOOLEAN_STATES = {'1': True, 'yes': True, 'true': True, 'on': True, + '0': False, 'no': False, 'false': False, 'off': False} def __init__(self, defaults=None, dict_type=_default_dict, allow_no_value=False, *, delimiters=('=', ':'), @@ -414,7 +427,7 @@ else: self._optcre = re.compile(self._OPT_TMPL.format(delim=d), re.VERBOSE) - if comment_prefixes is self._COMPATIBLE: + if comment_prefixes is _COMPATIBLE: self._startonly_comment_prefixes = ('#',) self._comment_prefixes = (';',) else: @@ -528,6 +541,8 @@ elements_added.add(section) for key, value in keys.items(): key = self.optionxform(key) + if value is not None: + value = str(value) if self._strict and (section, key) in elements_added: raise DuplicateOptionError(section, key, source) elements_added.add((section, key)) @@ -542,21 +557,29 @@ ) self.read_file(fp, source=filename) - def get(self, section, option): - opt = self.optionxform(option) - if section not in self._sections: - if section != DEFAULTSECT: - raise NoSectionError(section) - if opt in self._defaults: - return self._defaults[opt] + def get(self, section, option, vars=None, default=_UNSET): + """Get an option value for a given section. + + If `vars' is provided, it must be a dictionary. The option is looked up + in `vars' (if provided), `section', and in `DEFAULTSECT' in that order. + If the key is not found and `default' is provided, it is used as + a fallback value. `None' can be provided as a `default' value. + """ + try: + d = self._unify_values(section, vars) + except NoSectionError: + if default is _UNSET: + raise else: + return default + option = self.optionxform(option) + try: + return d[option] + except KeyError: + if default is _UNSET: raise NoOptionError(option, section) - elif opt in self._sections[section]: - return self._sections[section][opt] - elif opt in self._defaults: - return self._defaults[opt] - else: - raise NoOptionError(option, section) + else: + return default def items(self, section): try: @@ -571,23 +594,35 @@ del d["__name__"] return d.items() - def _get(self, section, conv, option): - return conv(self.get(section, option)) + def _get(self, section, conv, option, *args, **kwargs): + return conv(self.get(section, option, *args, **kwargs)) - def getint(self, section, option): - return self._get(section, int, option) + def getint(self, section, option, vars=None, default=_UNSET): + try: + return self._get(section, int, option, vars) + except (NoSectionError, NoOptionError): + if default is _UNSET: + raise + else: + return default - def getfloat(self, section, option): - return self._get(section, float, option) + def getfloat(self, section, option, vars=None, default=_UNSET): + try: + return self._get(section, float, option, vars) + except (NoSectionError, NoOptionError): + if default is _UNSET: + raise + else: + return default - _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True, - '0': False, 'no': False, 'false': False, 'off': False} - - def getboolean(self, section, option): - v = self.get(section, option) - if v.lower() not in self._boolean_states: - raise ValueError('Not a boolean: %s' % v) - return self._boolean_states[v.lower()] + def getboolean(self, section, option, vars=None, default=_UNSET): + try: + return self._get(section, self._convert_to_boolean, option, vars) + except (NoSectionError, NoOptionError): + if default is _UNSET: + raise + else: + return default def optionxform(self, optionstr): return optionstr.lower() @@ -797,15 +832,43 @@ exc.append(lineno, repr(line)) return exc + def _unify_values(self, section, vars): + """Create a copy of the DEFAULTSECT with values from a specific + `section' and the `vars' dictionary. If provided, values in `vars' + take precendence. + """ + d = self._defaults.copy() + try: + d.update(self._sections[section]) + except KeyError: + if section != DEFAULTSECT: + raise NoSectionError(section) + # Update with the entry specific variables + if vars: + for key, value in vars.items(): + if value is not None: + value = str(value) + d[self.optionxform(key)] = value + return d + + def _convert_to_boolean(self, value): + """Return a boolean value translating from other types if necessary. + """ + if value.lower() not in self.BOOLEAN_STATES: + raise ValueError('Not a boolean: %s' % value) + return self.BOOLEAN_STATES[value.lower()] + class ConfigParser(RawConfigParser): """ConfigParser implementing interpolation.""" - def get(self, section, option, raw=False, vars=None): + def get(self, section, option, raw=False, vars=None, default=_UNSET): """Get an option value for a given section. If `vars' is provided, it must be a dictionary. The option is looked up - in `vars' (if provided), `section', and in `defaults' in that order. + in `vars' (if provided), `section', and in `DEFAULTSECT' in that order. + If the key is not found and `default' is provided, it is used as + a fallback value. `None' can be provided as a `default' value. All % interpolations are expanded in the return values, unless the optional argument `raw' is true. Values for interpolation keys are @@ -813,27 +876,56 @@ The section DEFAULT is special. """ - d = self._defaults.copy() try: - d.update(self._sections[section]) - except KeyError: - if section != DEFAULTSECT: - raise NoSectionError(section) - # Update with the entry specific variables - if vars: - for key, value in vars.items(): - d[self.optionxform(key)] = value + d = self._unify_values(section, vars) + except NoSectionError: + if default is _UNSET: + raise + else: + return default option = self.optionxform(option) try: value = d[option] except KeyError: - raise NoOptionError(option, section) + if default is _UNSET: + raise NoOptionError(option, section) + else: + return default if raw or value is None: return value else: return self._interpolate(section, option, value, d) + def getint(self, section, option, raw=False, vars=None, default=_UNSET): + try: + return self._get(section, int, option, raw, vars) + except (NoSectionError, NoOptionError): + if default is _UNSET: + raise + else: + return default + + def getfloat(self, section, option, raw=False, vars=None, default=_UNSET): + try: + return self._get(section, float, option, raw, vars) + except (NoSectionError, NoOptionError): + if default is _UNSET: + raise + else: + return default + + def getboolean(self, section, option, raw=False, vars=None, + default=_UNSET): + try: + return self._get(section, self._convert_to_boolean, option, raw, + vars) + except (NoSectionError, NoOptionError): + if default is _UNSET: + raise + else: + return default + def items(self, section, raw=False, vars=None): """Return a list of (name, value) tuples for each option in a section. Modified: python/branches/py3k/Lib/test/test_cfgparser.py ============================================================================== --- python/branches/py3k/Lib/test/test_cfgparser.py (original) +++ python/branches/py3k/Lib/test/test_cfgparser.py Sat Sep 4 06:35:34 2010 @@ -62,9 +62,10 @@ 'Spaces', 'Spacey Bar', 'Spacey Bar From The Beginning', + 'Types', ] if self.allow_no_value: - E.append(r'NoValue') + E.append('NoValue') E.sort() eq = self.assertEqual eq(L, E) @@ -80,9 +81,43 @@ eq(cf.get('Commented Bar', 'baz'), 'qwe') eq(cf.get('Spaces', 'key with spaces'), 'value') eq(cf.get('Spaces', 'another with spaces'), 'splat!') + eq(cf.getint('Types', 'int'), 42) + eq(cf.get('Types', 'int'), "42") + self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44) + eq(cf.get('Types', 'float'), "0.44") + eq(cf.getboolean('Types', 'boolean'), False) if self.allow_no_value: eq(cf.get('NoValue', 'option-without-value'), None) + # test vars= and default= + eq(cf.get('Foo Bar', 'foo', default='baz'), 'bar') + eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz') + with self.assertRaises(configparser.NoSectionError): + cf.get('No Such Foo Bar', 'foo') + with self.assertRaises(configparser.NoOptionError): + cf.get('Foo Bar', 'no-such-foo') + eq(cf.get('No Such Foo Bar', 'foo', default='baz'), 'baz') + eq(cf.get('Foo Bar', 'no-such-foo', default='baz'), 'baz') + eq(cf.get('Spacey Bar', 'foo', default=None), 'bar') + eq(cf.get('No Such Spacey Bar', 'foo', default=None), None) + eq(cf.getint('Types', 'int', default=18), 42) + eq(cf.getint('Types', 'no-such-int', default=18), 18) + eq(cf.getint('Types', 'no-such-int', default="18"), "18") # sic! + self.assertAlmostEqual(cf.getfloat('Types', 'float', + default=0.0), 0.44) + self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float', + default=0.0), 0.0) + eq(cf.getfloat('Types', 'no-such-float', default="0.0"), "0.0") # sic! + eq(cf.getboolean('Types', 'boolean', default=True), False) + eq(cf.getboolean('Types', 'no-such-boolean', default="yes"), + "yes") # sic! + eq(cf.getboolean('Types', 'no-such-boolean', default=True), True) + eq(cf.getboolean('No Such Types', 'boolean', default=True), True) + if self.allow_no_value: + eq(cf.get('NoValue', 'option-without-value', default=False), None) + eq(cf.get('NoValue', 'no-such-option-without-value', + default=False), False) + self.assertNotIn('__name__', cf.options("Foo Bar"), '__name__ "option" should not be exposed by the API!') @@ -127,6 +162,10 @@ [Spaces] key with spaces {0[1]} value another with spaces {0[0]} splat! +[Types] +int {0[1]} 42 +float {0[0]} 0.44 +boolean {0[0]} NO """.format(self.delimiters, self.comment_prefixes) if self.allow_no_value: config_string += ( @@ -194,7 +233,12 @@ "Spaces": { "key with spaces": "value", "another with spaces": "splat!", - } + }, + "Types": { + "int": 42, + "float": 0.44, + "boolean": False, + }, } if self.allow_no_value: config.update({ @@ -732,8 +776,11 @@ 'no values here', 'tricky interpolation', 'more interpolation']) - #self.assertEqual(cf.getint('DEFAULT', 'go', vars={'interpolate': '-1'}), - # -1) + self.assertEqual(cf.getint('DEFAULT', 'go', + vars={'interpolate': '-1'}), -1) + with self.assertRaises(ValueError): + # no interpolation will happen + cf.getint('DEFAULT', 'go', raw=True, vars={'interpolate': '-1'}) self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4) self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10) longname = 'yeah, sections can be indented as well' @@ -808,7 +855,7 @@ class CompatibleTestCase(CfgParserTestCaseClass): config_class = configparser.RawConfigParser - comment_prefixes = configparser.RawConfigParser._COMPATIBLE + comment_prefixes = configparser._COMPATIBLE def test_comment_handling(self): config_string = textwrap.dedent("""\ Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 4 06:35:34 2010 @@ -158,6 +158,9 @@ - Issue #9753: Fixed socket.dup, which did not always work correctly on Windows. +- Issue #9421: Made the get methods consistently accept the vars + and default arguments on all parser classes. + - Issue #7005: Fixed output of None values for RawConfigParser.write and ConfigParser.write. From python-checkins at python.org Sat Sep 4 16:38:09 2010 From: python-checkins at python.org (martin.v.loewis) Date: Sat, 4 Sep 2010 16:38:09 +0200 (CEST) Subject: [Python-checkins] r84487 - in python/branches/py3k: Misc/NEWS Tools/msi/msi.py Message-ID: <20100904143809.8AA4BDFFD@mail.python.org> Author: martin.v.loewis Date: Sat Sep 4 16:38:09 2010 New Revision: 84487 Log: Issue #1303434: Include PDBs in release. Patch by James Lee and Daniel Stutzbach. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Tools/msi/msi.py Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 4 16:38:09 2010 @@ -381,6 +381,8 @@ Build ----- +- Issue #1303434: Generate ZIP file containing all PDBs. + - Issue #9193: PEP 3149 is accepted. - Issue #3101: Helper functions _add_one_to_index_C() and Modified: python/branches/py3k/Tools/msi/msi.py ============================================================================== --- python/branches/py3k/Tools/msi/msi.py (original) +++ python/branches/py3k/Tools/msi/msi.py Sat Sep 4 16:38:09 2010 @@ -1,7 +1,7 @@ # Python MSI Generator # (C) 2003 Martin v. Loewis # See "FOO" in comments refers to MSDN sections with the title FOO. -import msilib, schema, sequence, os, glob, time, re, shutil +import msilib, schema, sequence, os, glob, time, re, shutil, zipfile from msilib import Feature, CAB, Directory, Dialog, Binary, add_data import uisample from win32com.client import constants @@ -31,6 +31,8 @@ MSVCR = "90" # Name of certificate in default store to sign MSI with certname = None +# Make a zip file containing the PDB files for this build? +pdbzip = True try: from config import * @@ -1316,6 +1318,16 @@ ]) db.Commit() +def build_pdbzip(): + pdbexclude = ['kill_python.pdb', 'make_buildinfo.pdb', + 'make_versioninfo.pdb'] + path = "python-%s%s-pdb.zip" % (full_current_version, msilib.arch_ext) + pdbzip = zipfile.ZipFile(path, 'w') + for f in glob.glob1(os.path.join(srcdir, PCBUILD), "*.pdb"): + if f not in pdbexclude and not f.endswith('_d.pdb'): + pdbzip.write(os.path.join(srcdir, PCBUILD, f), f) + pdbzip.close() + db,msiname = build_database() try: add_features(db) @@ -1398,3 +1410,6 @@ # the certificate subject, e.g. "Python Software Foundation" if certname: os.system('signtool sign /n "%s" /t http://timestamp.verisign.com/scripts/timestamp.dll %s' % (certname, msiname)) + +if pdbzip: + build_pdbzip() From python-checkins at python.org Sat Sep 4 18:28:04 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 18:28:04 +0200 (CEST) Subject: [Python-checkins] r84488 - python/branches/py3k/setup.py Message-ID: <20100904162804.4FDE1F7C1@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 18:28:00 2010 New Revision: 84488 Log: Workaround PEP 3149 build problems. Modified: python/branches/py3k/setup.py Modified: python/branches/py3k/setup.py ============================================================================== --- python/branches/py3k/setup.py (original) +++ python/branches/py3k/setup.py Sat Sep 4 18:28:00 2010 @@ -152,7 +152,13 @@ def build_extensions(self): # Detect which modules should be compiled - missing = self.detect_modules() + old_so = self.compiler.shared_lib_extension + # Workaround PEP 3149 stuff + self.compiler.shared_lib_extension = os.environ.get("SO", ".so") + try: + missing = self.detect_modules() + finally: + self.compiler.shared_lib_extension = old_so # Remove modules that are present on the disabled list extensions = [ext for ext in self.extensions From python-checkins at python.org Sat Sep 4 19:21:57 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:21:57 +0200 (CEST) Subject: [Python-checkins] r84489 - in python/branches/py3k: Misc/NEWS Modules/posixmodule.c Message-ID: <20100904172157.47801F9B5@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:21:57 2010 New Revision: 84489 Log: Issue #7736: Release the GIL around calls to opendir() and closedir() in the posix module. Patch by Marcin Bachry. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/posixmodule.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 4 19:21:57 2010 @@ -93,6 +93,9 @@ Extensions ---------- +- Issue #7736: Release the GIL around calls to opendir() and closedir() + in the posix module. Patch by Marcin Bachry. + - Issue #4835: make PyLong_FromSocket_t() and PyLong_AsSocket_t() private to the socket module, and fix the width of socket descriptors to be correctly detected under 64-bit Windows. Modified: python/branches/py3k/Modules/posixmodule.c ============================================================================== --- python/branches/py3k/Modules/posixmodule.c (original) +++ python/branches/py3k/Modules/posixmodule.c Sat Sep 4 19:21:57 2010 @@ -2580,11 +2580,16 @@ oname = PyBytes_FromString("."); } name = PyBytes_AsString(oname); - if ((dirp = opendir(name)) == NULL) { + Py_BEGIN_ALLOW_THREADS + dirp = opendir(name); + Py_END_ALLOW_THREADS + if (dirp == NULL) { return posix_error_with_allocated_filename(oname); } if ((d = PyList_New(0)) == NULL) { + Py_BEGIN_ALLOW_THREADS closedir(dirp); + Py_END_ALLOW_THREADS Py_DECREF(oname); return NULL; } @@ -2597,7 +2602,9 @@ if (errno == 0) { break; } else { + Py_BEGIN_ALLOW_THREADS closedir(dirp); + Py_END_ALLOW_THREADS Py_DECREF(d); return posix_error_with_allocated_filename(oname); } @@ -2621,7 +2628,9 @@ } Py_DECREF(v); } + Py_BEGIN_ALLOW_THREADS closedir(dirp); + Py_END_ALLOW_THREADS Py_DECREF(oname); return d; From python-checkins at python.org Sat Sep 4 19:26:02 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:26:02 +0200 (CEST) Subject: [Python-checkins] r84490 - in python/branches/release31-maint: Misc/NEWS Modules/posixmodule.c Message-ID: <20100904172602.1F30AF6FF@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:26:01 2010 New Revision: 84490 Log: Merged revisions 84489 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84489 | antoine.pitrou | 2010-09-04 19:21:57 +0200 (sam., 04 sept. 2010) | 4 lines Issue #7736: Release the GIL around calls to opendir() and closedir() in the posix module. Patch by Marcin Bachry. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/posixmodule.c Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sat Sep 4 19:26:01 2010 @@ -474,6 +474,9 @@ Extension Modules ----------------- +- Issue #7736: Release the GIL around calls to opendir() and closedir() + in the posix module. Patch by Marcin Bachry. + - Issue #4835: make PyLong_FromSocket_t() and PyLong_AsSocket_t() private to the socket module, and fix the width of socket descriptors to be correctly detected under 64-bit Windows. Modified: python/branches/release31-maint/Modules/posixmodule.c ============================================================================== --- python/branches/release31-maint/Modules/posixmodule.c (original) +++ python/branches/release31-maint/Modules/posixmodule.c Sat Sep 4 19:26:01 2010 @@ -2493,11 +2493,16 @@ if (!PyArg_ParseTuple(args, "O&:listdir", PyUnicode_FSConverter, &oname)) return NULL; name = bytes2str(oname, 1); - if ((dirp = opendir(name)) == NULL) { + Py_BEGIN_ALLOW_THREADS + dirp = opendir(name); + Py_END_ALLOW_THREADS + if (dirp == NULL) { return posix_error_with_allocated_filename(oname); } if ((d = PyList_New(0)) == NULL) { + Py_BEGIN_ALLOW_THREADS closedir(dirp); + Py_END_ALLOW_THREADS release_bytes(oname); return NULL; } @@ -2510,7 +2515,9 @@ if (errno == 0) { break; } else { + Py_BEGIN_ALLOW_THREADS closedir(dirp); + Py_END_ALLOW_THREADS Py_DECREF(d); return posix_error_with_allocated_filename(oname); } @@ -2550,7 +2557,9 @@ } Py_DECREF(v); } + Py_BEGIN_ALLOW_THREADS closedir(dirp); + Py_END_ALLOW_THREADS release_bytes(oname); return d; From python-checkins at python.org Sat Sep 4 19:27:10 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:27:10 +0200 (CEST) Subject: [Python-checkins] r84491 - in python/branches/release27-maint: Misc/NEWS Modules/posixmodule.c Message-ID: <20100904172710.5D36FF6FF@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:27:10 2010 New Revision: 84491 Log: Merged revisions 84489 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84489 | antoine.pitrou | 2010-09-04 19:21:57 +0200 (sam., 04 sept. 2010) | 4 lines Issue #7736: Release the GIL around calls to opendir() and closedir() in the posix module. Patch by Marcin Bachry. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/posixmodule.c Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sat Sep 4 19:27:10 2010 @@ -225,6 +225,9 @@ Extension Modules ----------------- +- Issue #7736: Release the GIL around calls to opendir() and closedir() + in the posix module. Patch by Marcin Bachry. + - As a result of issue #2521, the _weakref module is now compiled into the interpreter by default. Modified: python/branches/release27-maint/Modules/posixmodule.c ============================================================================== --- python/branches/release27-maint/Modules/posixmodule.c (original) +++ python/branches/release27-maint/Modules/posixmodule.c Sat Sep 4 19:27:10 2010 @@ -2333,11 +2333,16 @@ } if (!PyArg_ParseTuple(args, "et:listdir", Py_FileSystemDefaultEncoding, &name)) return NULL; - if ((dirp = opendir(name)) == NULL) { + Py_BEGIN_ALLOW_THREADS + dirp = opendir(name); + Py_END_ALLOW_THREADS + if (dirp == NULL) { return posix_error_with_allocated_filename(name); } if ((d = PyList_New(0)) == NULL) { + Py_BEGIN_ALLOW_THREADS closedir(dirp); + Py_END_ALLOW_THREADS PyMem_Free(name); return NULL; } @@ -2350,7 +2355,9 @@ if (errno == 0) { break; } else { + Py_BEGIN_ALLOW_THREADS closedir(dirp); + Py_END_ALLOW_THREADS Py_DECREF(d); return posix_error_with_allocated_filename(name); } @@ -2391,7 +2398,9 @@ } Py_DECREF(v); } + Py_BEGIN_ALLOW_THREADS closedir(dirp); + Py_END_ALLOW_THREADS PyMem_Free(name); return d; From python-checkins at python.org Sat Sep 4 19:32:06 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:32:06 +0200 (CEST) Subject: [Python-checkins] r84492 - python/branches/py3k/Lib/test/test_posix.py Message-ID: <20100904173206.D9F14FB7F@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:32:06 2010 New Revision: 84492 Log: Issue #9581: Fix non-working PosixGroupsTester test case (it only runs as root, which is why nobody bothered about the failure) Modified: python/branches/py3k/Lib/test/test_posix.py Modified: python/branches/py3k/Lib/test/test_posix.py ============================================================================== --- python/branches/py3k/Lib/test/test_posix.py (original) +++ python/branches/py3k/Lib/test/test_posix.py Sat Sep 4 19:32:06 2010 @@ -409,13 +409,7 @@ def test_initgroups(self): # find missing group - groups = sorted(self.saved_groups) - for g1,g2 in zip(groups[:-1], groups[1:]): - g = g1 + 1 - if g < g2: - break - else: - g = g2 + 1 + g = max(self.saved_groups) + 1 name = pwd.getpwuid(posix.getuid()).pw_name posix.initgroups(name, g) self.assertIn(g, posix.getgroups()) @@ -423,7 +417,7 @@ @unittest.skipUnless(hasattr(posix, 'setgroups'), "test needs posix.setgroups()") def test_setgroups(self): - for groups in [[0], range(16)]: + for groups in [[0], list(range(16))]: posix.setgroups(groups) self.assertListEqual(groups, posix.getgroups()) From python-checkins at python.org Sat Sep 4 19:33:06 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:33:06 +0200 (CEST) Subject: [Python-checkins] r84493 - in python/branches/release31-maint: Lib/test/test_posix.py Message-ID: <20100904173306.A1B82F4CB@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:33:06 2010 New Revision: 84493 Log: Merged revisions 84492 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84492 | antoine.pitrou | 2010-09-04 19:32:06 +0200 (sam., 04 sept. 2010) | 4 lines Issue #9581: Fix non-working PosixGroupsTester test case (it only runs as root, which is why nobody bothered about the failure) ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_posix.py Modified: python/branches/release31-maint/Lib/test/test_posix.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_posix.py (original) +++ python/branches/release31-maint/Lib/test/test_posix.py Sat Sep 4 19:33:06 2010 @@ -310,13 +310,7 @@ def test_initgroups(self): # find missing group - groups = sorted(self.saved_groups) - for g1,g2 in zip(groups[:-1], groups[1:]): - g = g1 + 1 - if g < g2: - break - else: - g = g2 + 1 + g = max(self.saved_groups) + 1 name = pwd.getpwuid(posix.getuid()).pw_name posix.initgroups(name, g) self.assertIn(g, posix.getgroups()) @@ -324,7 +318,7 @@ @unittest.skipUnless(hasattr(posix, 'setgroups'), "test needs posix.setgroups()") def test_setgroups(self): - for groups in [[0], range(16)]: + for groups in [[0], list(range(16))]: posix.setgroups(groups) self.assertListEqual(groups, posix.getgroups()) From python-checkins at python.org Sat Sep 4 19:34:12 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:34:12 +0200 (CEST) Subject: [Python-checkins] r84494 - in python/branches/release27-maint: Lib/test/test_posix.py Message-ID: <20100904173412.EFEF5F4CB@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:34:12 2010 New Revision: 84494 Log: Merged revisions 84492 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84492 | antoine.pitrou | 2010-09-04 19:32:06 +0200 (sam., 04 sept. 2010) | 4 lines Issue #9581: Fix non-working PosixGroupsTester test case (it only runs as root, which is why nobody bothered about the failure) ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_posix.py Modified: python/branches/release27-maint/Lib/test/test_posix.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_posix.py (original) +++ python/branches/release27-maint/Lib/test/test_posix.py Sat Sep 4 19:34:12 2010 @@ -400,13 +400,7 @@ def test_initgroups(self): # find missing group - groups = sorted(self.saved_groups) - for g1,g2 in zip(groups[:-1], groups[1:]): - g = g1 + 1 - if g < g2: - break - else: - g = g2 + 1 + g = max(self.saved_groups) + 1 name = pwd.getpwuid(posix.getuid()).pw_name posix.initgroups(name, g) self.assertIn(g, posix.getgroups()) From python-checkins at python.org Sat Sep 4 19:40:21 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:40:21 +0200 (CEST) Subject: [Python-checkins] r84495 - in python/branches/py3k: Lib/copy.py Lib/test/test_copy.py Misc/NEWS Message-ID: <20100904174021.9D7FFF8F0@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:40:21 2010 New Revision: 84495 Log: Issue #1100562: Fix deep-copying of objects derived from the list and dict types. Patch by Michele Orr? and Bj?rn Lindqvist. Modified: python/branches/py3k/Lib/copy.py python/branches/py3k/Lib/test/test_copy.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/copy.py ============================================================================== --- python/branches/py3k/Lib/copy.py (original) +++ python/branches/py3k/Lib/copy.py Sat Sep 4 19:40:21 2010 @@ -283,17 +283,7 @@ args = deepcopy(args, memo) y = callable(*args) memo[id(x)] = y - if listiter is not None: - for item in listiter: - if deep: - item = deepcopy(item, memo) - y.append(item) - if dictiter is not None: - for key, value in dictiter: - if deep: - key = deepcopy(key, memo) - value = deepcopy(value, memo) - y[key] = value + if state: if deep: state = deepcopy(state, memo) @@ -309,6 +299,18 @@ if slotstate is not None: for key, value in slotstate.items(): setattr(y, key, value) + + if listiter is not None: + for item in listiter: + if deep: + item = deepcopy(item, memo) + y.append(item) + if dictiter is not None: + for key, value in dictiter: + if deep: + key = deepcopy(key, memo) + value = deepcopy(value, memo) + y[key] = value return y del d @@ -370,6 +372,16 @@ print(map(reprlib.repr, l1)) print(map(reprlib.repr, l2)) print(map(reprlib.repr, l3)) + class odict(dict): + def __init__(self, d = {}): + self.a = 99 + dict.__init__(self, d) + def __setitem__(self, k, i): + dict.__setitem__(self, k, i) + self.a + o = odict({"A" : "B"}) + x = deepcopy(o) + print(o, x) if __name__ == '__main__': _test() Modified: python/branches/py3k/Lib/test/test_copy.py ============================================================================== --- python/branches/py3k/Lib/test/test_copy.py (original) +++ python/branches/py3k/Lib/test/test_copy.py Sat Sep 4 19:40:21 2010 @@ -532,6 +532,26 @@ self.assertEqual(x.foo, y.foo) self.assertTrue(x.foo is not y.foo) + def test_deepcopy_dict_subclass(self): + class C(dict): + def __init__(self, d=None): + if not d: + d = {} + self._keys = list(d.keys()) + super().__init__(d) + def __setitem__(self, key, item): + super().__setitem__(key, item) + if key not in self._keys: + self._keys.append(key) + x = C(d={'foo':0}) + y = copy.deepcopy(x) + self.assertEqual(x, y) + self.assertEqual(x._keys, y._keys) + self.assertTrue(x is not y) + x['bar'] = 1 + self.assertNotEqual(x, y) + self.assertNotEqual(x._keys, y._keys) + def test_copy_list_subclass(self): class C(list): pass Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 4 19:40:21 2010 @@ -158,6 +158,9 @@ Library ------- +- Issue #1100562: Fix deep-copying of objects derived from the list and + dict types. Patch by Michele Orr? and Bj?rn Lindqvist. + - Issue #9753: Fixed socket.dup, which did not always work correctly on Windows. From python-checkins at python.org Sat Sep 4 19:40:51 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:40:51 +0200 (CEST) Subject: [Python-checkins] r84496 - python/branches/py3k/Misc/ACKS Message-ID: <20100904174051.84A38FAEF@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:40:51 2010 New Revision: 84496 Log: Fix Bj?rn's name in ACKS. Modified: python/branches/py3k/Misc/ACKS Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Sat Sep 4 19:40:51 2010 @@ -494,7 +494,7 @@ Martin Ligr Grant Limberg Christopher Lindblad -Bjorn Lindqvist +Bj?rn Lindqvist Per Lindqvist Eric Lindvall Gregor Lingl From python-checkins at python.org Sat Sep 4 19:46:44 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:46:44 +0200 (CEST) Subject: [Python-checkins] r84497 - python/branches/py3k/Lib/copy.py Message-ID: <20100904174644.F191DF924@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:46:44 2010 New Revision: 84497 Log: Fix running the copy module from the command-line (however use{ful,less} it may be). Modified: python/branches/py3k/Lib/copy.py Modified: python/branches/py3k/Lib/copy.py ============================================================================== --- python/branches/py3k/Lib/copy.py (original) +++ python/branches/py3k/Lib/copy.py Sat Sep 4 19:46:44 2010 @@ -51,6 +51,7 @@ import types import weakref from copyreg import dispatch_table +import builtins class Error(Exception): pass @@ -109,7 +110,7 @@ if t is not None: d[t] = _copy_immutable for name in ("complex", "unicode"): - t = globals()['__builtins__'].get(name) + t = getattr(builtins, name, None) if t is not None: d[t] = _copy_immutable From python-checkins at python.org Sat Sep 4 19:49:13 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:49:13 +0200 (CEST) Subject: [Python-checkins] r84498 - in python/branches/release31-maint: Lib/copy.py Lib/test/test_copy.py Misc/ACKS Misc/NEWS Message-ID: <20100904174913.BC84EFA84@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:49:13 2010 New Revision: 84498 Log: Merged revisions 84495-84497 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84495 | antoine.pitrou | 2010-09-04 19:40:21 +0200 (sam., 04 sept. 2010) | 4 lines Issue #1100562: Fix deep-copying of objects derived from the list and dict types. Patch by Michele Orr? and Bj?rn Lindqvist. ........ r84496 | antoine.pitrou | 2010-09-04 19:40:51 +0200 (sam., 04 sept. 2010) | 3 lines Fix Bj?rn's name in ACKS. ........ r84497 | antoine.pitrou | 2010-09-04 19:46:44 +0200 (sam., 04 sept. 2010) | 3 lines Fix running the copy module from the command-line (however use{ful,less} it may be). ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/copy.py python/branches/release31-maint/Lib/test/test_copy.py python/branches/release31-maint/Misc/ACKS python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/copy.py ============================================================================== --- python/branches/release31-maint/Lib/copy.py (original) +++ python/branches/release31-maint/Lib/copy.py Sat Sep 4 19:49:13 2010 @@ -51,6 +51,7 @@ import types import weakref from copyreg import dispatch_table +import builtins class Error(Exception): pass @@ -109,7 +110,7 @@ if t is not None: d[t] = _copy_immutable for name in ("complex", "unicode"): - t = globals()['__builtins__'].get(name) + t = getattr(builtins, name, None) if t is not None: d[t] = _copy_immutable @@ -279,17 +280,7 @@ args = deepcopy(args, memo) y = callable(*args) memo[id(x)] = y - if listiter is not None: - for item in listiter: - if deep: - item = deepcopy(item, memo) - y.append(item) - if dictiter is not None: - for key, value in dictiter: - if deep: - key = deepcopy(key, memo) - value = deepcopy(value, memo) - y[key] = value + if state: if deep: state = deepcopy(state, memo) @@ -305,6 +296,18 @@ if slotstate is not None: for key, value in slotstate.items(): setattr(y, key, value) + + if listiter is not None: + for item in listiter: + if deep: + item = deepcopy(item, memo) + y.append(item) + if dictiter is not None: + for key, value in dictiter: + if deep: + key = deepcopy(key, memo) + value = deepcopy(value, memo) + y[key] = value return y del d @@ -366,6 +369,16 @@ print(map(reprlib.repr, l1)) print(map(reprlib.repr, l2)) print(map(reprlib.repr, l3)) + class odict(dict): + def __init__(self, d = {}): + self.a = 99 + dict.__init__(self, d) + def __setitem__(self, k, i): + dict.__setitem__(self, k, i) + self.a + o = odict({"A" : "B"}) + x = deepcopy(o) + print(o, x) if __name__ == '__main__': _test() Modified: python/branches/release31-maint/Lib/test/test_copy.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_copy.py (original) +++ python/branches/release31-maint/Lib/test/test_copy.py Sat Sep 4 19:49:13 2010 @@ -532,6 +532,26 @@ self.assertEqual(x.foo, y.foo) self.assertTrue(x.foo is not y.foo) + def test_deepcopy_dict_subclass(self): + class C(dict): + def __init__(self, d=None): + if not d: + d = {} + self._keys = list(d.keys()) + super().__init__(d) + def __setitem__(self, key, item): + super().__setitem__(key, item) + if key not in self._keys: + self._keys.append(key) + x = C(d={'foo':0}) + y = copy.deepcopy(x) + self.assertEqual(x, y) + self.assertEqual(x._keys, y._keys) + self.assertTrue(x is not y) + x['bar'] = 1 + self.assertNotEqual(x, y) + self.assertNotEqual(x._keys, y._keys) + def test_copy_list_subclass(self): class C(list): pass Modified: python/branches/release31-maint/Misc/ACKS ============================================================================== --- python/branches/release31-maint/Misc/ACKS (original) +++ python/branches/release31-maint/Misc/ACKS Sat Sep 4 19:49:13 2010 @@ -469,7 +469,7 @@ Martin Ligr Grant Limberg Christopher Lindblad -Bjorn Lindqvist +Bj?rn Lindqvist Per Lindqvist Eric Lindvall Gregor Lingl Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sat Sep 4 19:49:13 2010 @@ -105,6 +105,9 @@ Library ------- +- Issue #1100562: Fix deep-copying of objects derived from the list and + dict types. Patch by Michele Orr? and Bj?rn Lindqvist. + - Issue #9753: Fixed socket.dup, which did not always work correctly on Windows. From python-checkins at python.org Sat Sep 4 19:52:26 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 19:52:26 +0200 (CEST) Subject: [Python-checkins] r84499 - in python/branches/release27-maint: Lib/copy.py Lib/test/test_copy.py Misc/ACKS Misc/NEWS Message-ID: <20100904175226.630F1E381@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 19:52:26 2010 New Revision: 84499 Log: Merged revisions 84495-84496 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84495 | antoine.pitrou | 2010-09-04 19:40:21 +0200 (sam., 04 sept. 2010) | 4 lines Issue #1100562: Fix deep-copying of objects derived from the list and dict types. Patch by Michele Orr? and Bj?rn Lindqvist. ........ r84496 | antoine.pitrou | 2010-09-04 19:40:51 +0200 (sam., 04 sept. 2010) | 3 lines Fix Bj?rn's name in ACKS. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/copy.py python/branches/release27-maint/Lib/test/test_copy.py python/branches/release27-maint/Misc/ACKS python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/copy.py ============================================================================== --- python/branches/release27-maint/Lib/copy.py (original) +++ python/branches/release27-maint/Lib/copy.py Sat Sep 4 19:52:26 2010 @@ -328,17 +328,7 @@ args = deepcopy(args, memo) y = callable(*args) memo[id(x)] = y - if listiter is not None: - for item in listiter: - if deep: - item = deepcopy(item, memo) - y.append(item) - if dictiter is not None: - for key, value in dictiter: - if deep: - key = deepcopy(key, memo) - value = deepcopy(value, memo) - y[key] = value + if state: if deep: state = deepcopy(state, memo) @@ -354,6 +344,18 @@ if slotstate is not None: for key, value in slotstate.iteritems(): setattr(y, key, value) + + if listiter is not None: + for item in listiter: + if deep: + item = deepcopy(item, memo) + y.append(item) + if dictiter is not None: + for key, value in dictiter: + if deep: + key = deepcopy(key, memo) + value = deepcopy(value, memo) + y[key] = value return y del d @@ -416,6 +418,16 @@ print map(repr.repr, l1) print map(repr.repr, l2) print map(repr.repr, l3) + class odict(dict): + def __init__(self, d = {}): + self.a = 99 + dict.__init__(self, d) + def __setitem__(self, k, i): + dict.__setitem__(self, k, i) + self.a + o = odict({"A" : "B"}) + x = deepcopy(o) + print(o, x) if __name__ == '__main__': _test() Modified: python/branches/release27-maint/Lib/test/test_copy.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_copy.py (original) +++ python/branches/release27-maint/Lib/test/test_copy.py Sat Sep 4 19:52:26 2010 @@ -526,6 +526,26 @@ self.assertEqual(x.foo, y.foo) self.assertTrue(x.foo is not y.foo) + def test_deepcopy_dict_subclass(self): + class C(dict): + def __init__(self, d=None): + if not d: + d = {} + self._keys = list(d.keys()) + dict.__init__(self, d) + def __setitem__(self, key, item): + dict.__setitem__(self, key, item) + if key not in self._keys: + self._keys.append(key) + x = C(d={'foo':0}) + y = copy.deepcopy(x) + self.assertEqual(x, y) + self.assertEqual(x._keys, y._keys) + self.assertTrue(x is not y) + x['bar'] = 1 + self.assertNotEqual(x, y) + self.assertNotEqual(x._keys, y._keys) + def test_copy_list_subclass(self): class C(list): pass Modified: python/branches/release27-maint/Misc/ACKS ============================================================================== --- python/branches/release27-maint/Misc/ACKS (original) +++ python/branches/release27-maint/Misc/ACKS Sat Sep 4 19:52:26 2010 @@ -472,7 +472,7 @@ Shawn Ligocki Martin Ligr Christopher Lindblad -Bjorn Lindqvist +Bj?rn Lindqvist Per Lindqvist Eric Lindvall Gregor Lingl Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sat Sep 4 19:52:26 2010 @@ -36,6 +36,9 @@ Library ------- +- Issue #1100562: Fix deep-copying of objects derived from the list and + dict types. Patch by Michele Orr? and Bj?rn Lindqvist. + - Issue #7005: Fixed output of None values for RawConfigParser.write and ConfigParser.write. From python-checkins at python.org Sat Sep 4 20:24:04 2010 From: python-checkins at python.org (brett.cannon) Date: Sat, 4 Sep 2010 20:24:04 +0200 (CEST) Subject: [Python-checkins] r84500 - in python/branches/py3k: Lib/warnings.py Misc/NEWS Python/_warnings.c Message-ID: <20100904182404.A352DF4A9@mail.python.org> Author: brett.cannon Date: Sat Sep 4 20:24:04 2010 New Revision: 84500 Log: _warnings exposed two variables with the name 'default_action' and 'once_registry'. This is bad as the warnings module had variables named 'defaultaction' and 'onceregistry' which are what people should be looking at (technically those variables shouldn't be mucked with as they are undocumented, but we all know better than to believe that isn't happening). So the variables from _warnings have been renamed to come off as private and to avoid confusion over what variable should be used. Closes issue #9766. Thanks to Antoine Pitrou for the discovery. Modified: python/branches/py3k/Lib/warnings.py python/branches/py3k/Misc/NEWS python/branches/py3k/Python/_warnings.c Modified: python/branches/py3k/Lib/warnings.py ============================================================================== --- python/branches/py3k/Lib/warnings.py (original) +++ python/branches/py3k/Lib/warnings.py Sat Sep 4 20:24:04 2010 @@ -357,10 +357,10 @@ # If either if the compiled regexs are None, match anything. _warnings_defaults = False try: - from _warnings import (filters, default_action, once_registry, + from _warnings import (filters, _defaultaction, _onceregistry, warn, warn_explicit) - defaultaction = default_action - onceregistry = once_registry + defaultaction = _defaultaction + onceregistry = _onceregistry _warnings_defaults = True except ImportError: filters = [] Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 4 20:24:04 2010 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #9766: Rename poorly named variables exposed by _warnings to prevent + confusion with the proper variables names from 'warnings' itself. + - Issue #9212: dict_keys and dict_items now provide the isdisjoint() method, to conform to the Set ABC. Patch by Daniel Urban. Modified: python/branches/py3k/Python/_warnings.c ============================================================================== --- python/branches/py3k/Python/_warnings.c (original) +++ python/branches/py3k/Python/_warnings.c Sat Sep 4 20:24:04 2010 @@ -945,13 +945,13 @@ if (_once_registry == NULL) return NULL; Py_INCREF(_once_registry); - if (PyModule_AddObject(m, "once_registry", _once_registry) < 0) + if (PyModule_AddObject(m, "_onceregistry", _once_registry) < 0) return NULL; _default_action = PyUnicode_FromString("default"); if (_default_action == NULL) return NULL; - if (PyModule_AddObject(m, "default_action", _default_action) < 0) + if (PyModule_AddObject(m, "_defaultaction", _default_action) < 0) return NULL; return m; } From python-checkins at python.org Sat Sep 4 20:43:53 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 20:43:53 +0200 (CEST) Subject: [Python-checkins] r84501 - in python/branches/py3k: Doc/library/dis.rst Include/opcode.h Lib/opcode.py Misc/ACKS Misc/NEWS Python/ceval.c Python/compile.c Python/import.c Python/opcode_targets.h Message-ID: <20100904184353.308B6F943@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 20:43:52 2010 New Revision: 84501 Log: Issue #9225: Remove the ROT_FOUR and DUP_TOPX opcode, the latter replaced by the new (and simpler) DUP_TOP_TWO. Performance isn't changed, but our bytecode is a bit simplified. Patch by Demur Rumed. Modified: python/branches/py3k/Doc/library/dis.rst python/branches/py3k/Include/opcode.h python/branches/py3k/Lib/opcode.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS python/branches/py3k/Python/ceval.c python/branches/py3k/Python/compile.c python/branches/py3k/Python/import.c python/branches/py3k/Python/opcode_targets.h Modified: python/branches/py3k/Doc/library/dis.rst ============================================================================== --- python/branches/py3k/Doc/library/dis.rst (original) +++ python/branches/py3k/Doc/library/dis.rst Sat Sep 4 20:43:52 2010 @@ -184,15 +184,15 @@ three. -.. opcode:: ROT_FOUR +.. opcode:: DUP_TOP - Lifts second, third and forth stack item one position up, moves top down to - position four. + Duplicates the reference on top of the stack. -.. opcode:: DUP_TOP +.. opcode:: DUP_TOP_TWO - Duplicates the reference on top of the stack. + Duplicates the two references on top of the stack, leaving them in the + same order. **Unary operations** @@ -531,12 +531,6 @@ are put onto the stack right-to-left. -.. opcode:: DUP_TOPX (count) - - Duplicate *count* items, keeping them in the same order. Due to implementation - limits, *count* should be between 1 and 5 inclusive. - - .. opcode:: STORE_ATTR (namei) Implements ``TOS.name = TOS1``, where *namei* is the index of name in Modified: python/branches/py3k/Include/opcode.h ============================================================================== --- python/branches/py3k/Include/opcode.h (original) +++ python/branches/py3k/Include/opcode.h Sat Sep 4 20:43:52 2010 @@ -12,7 +12,7 @@ #define ROT_TWO 2 #define ROT_THREE 3 #define DUP_TOP 4 -#define ROT_FOUR 5 +#define DUP_TOP_TWO 5 #define NOP 9 #define UNARY_POSITIVE 10 @@ -83,7 +83,7 @@ #define DELETE_ATTR 96 /* "" */ #define STORE_GLOBAL 97 /* "" */ #define DELETE_GLOBAL 98 /* "" */ -#define DUP_TOPX 99 /* number of items to duplicate */ + #define LOAD_CONST 100 /* Index in const list */ #define LOAD_NAME 101 /* Index in name list */ #define BUILD_TUPLE 102 /* Number of tuple items */ Modified: python/branches/py3k/Lib/opcode.py ============================================================================== --- python/branches/py3k/Lib/opcode.py (original) +++ python/branches/py3k/Lib/opcode.py Sat Sep 4 20:43:52 2010 @@ -48,7 +48,7 @@ def_op('ROT_TWO', 2) def_op('ROT_THREE', 3) def_op('DUP_TOP', 4) -def_op('ROT_FOUR', 5) +def_op('DUP_TOP_TWO', 5) def_op('NOP', 9) def_op('UNARY_POSITIVE', 10) @@ -116,7 +116,6 @@ name_op('DELETE_ATTR', 96) # "" name_op('STORE_GLOBAL', 97) # "" name_op('DELETE_GLOBAL', 98) # "" -def_op('DUP_TOPX', 99) # number of items to duplicate def_op('LOAD_CONST', 100) # Index in const list hasconst.append(100) name_op('LOAD_NAME', 101) # Index in name list Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Sat Sep 4 20:43:52 2010 @@ -700,6 +700,7 @@ Clinton Roy Paul Rubin Sam Ruby +Demur Rumed Audun S. Runde Rauli Ruohonen Jeff Rush Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 4 20:43:52 2010 @@ -12,6 +12,10 @@ Core and Builtins ----------------- +- Issue #9225: Remove the ROT_FOUR and DUP_TOPX opcode, the latter replaced + by the new (and simpler) DUP_TOP_TWO. Performance isn't changed, but + our bytecode is a bit simplified. Patch by Demur Rumed. + - Issue #9766: Rename poorly named variables exposed by _warnings to prevent confusion with the proper variables names from 'warnings' itself. Modified: python/branches/py3k/Python/ceval.c ============================================================================== --- python/branches/py3k/Python/ceval.c (original) +++ python/branches/py3k/Python/ceval.c Sat Sep 4 20:43:52 2010 @@ -1420,50 +1420,21 @@ SET_THIRD(v); FAST_DISPATCH(); - TARGET(ROT_FOUR) - u = TOP(); - v = SECOND(); - w = THIRD(); - x = FOURTH(); - SET_TOP(v); - SET_SECOND(w); - SET_THIRD(x); - SET_FOURTH(u); - FAST_DISPATCH(); - TARGET(DUP_TOP) v = TOP(); Py_INCREF(v); PUSH(v); FAST_DISPATCH(); - TARGET(DUP_TOPX) - if (oparg == 2) { - x = TOP(); - Py_INCREF(x); - w = SECOND(); - Py_INCREF(w); - STACKADJ(2); - SET_TOP(x); - SET_SECOND(w); - FAST_DISPATCH(); - } else if (oparg == 3) { - x = TOP(); - Py_INCREF(x); - w = SECOND(); - Py_INCREF(w); - v = THIRD(); - Py_INCREF(v); - STACKADJ(3); - SET_TOP(x); - SET_SECOND(w); - SET_THIRD(v); - FAST_DISPATCH(); - } - Py_FatalError("invalid argument to DUP_TOPX" - " (bytecode corruption?)"); - /* Never returns, so don't bother to set why. */ - break; + TARGET(DUP_TOP_TWO) + x = TOP(); + Py_INCREF(x); + w = SECOND(); + Py_INCREF(w); + STACKADJ(2); + SET_TOP(x); + SET_SECOND(w); + FAST_DISPATCH(); TARGET(UNARY_POSITIVE) v = TOP(); Modified: python/branches/py3k/Python/compile.c ============================================================================== --- python/branches/py3k/Python/compile.c (original) +++ python/branches/py3k/Python/compile.c Sat Sep 4 20:43:52 2010 @@ -680,8 +680,8 @@ return 0; case DUP_TOP: return 1; - case ROT_FOUR: - return 0; + case DUP_TOP_TWO: + return 2; case UNARY_POSITIVE: case UNARY_NEGATIVE: @@ -782,8 +782,6 @@ return -1; case DELETE_GLOBAL: return 0; - case DUP_TOPX: - return oparg; case LOAD_CONST: return 1; case LOAD_NAME: @@ -3404,7 +3402,7 @@ return 0; } if (ctx == AugLoad) { - ADDOP_I(c, DUP_TOPX, 2); + ADDOP(c, DUP_TOP_TWO); } else if (ctx == AugStore) { ADDOP(c, ROT_THREE); Modified: python/branches/py3k/Python/import.c ============================================================================== --- python/branches/py3k/Python/import.c (original) +++ python/branches/py3k/Python/import.c Sat Sep 4 20:43:52 2010 @@ -101,12 +101,14 @@ introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE) Python 3.2a0: 3160 (add SETUP_WITH) tag: cpython-32 + Python 3.2a1: 3170 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR) + tag: cpython-32 */ /* If you change MAGIC, you must change TAG and you must insert the old value into _PyMagicNumberTags below. */ -#define MAGIC (3160 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (3170 | ((long)'\r'<<16) | ((long)'\n'<<24)) #define TAG "cpython-32" #define CACHEDIR "__pycache__" /* Current magic word and string tag as globals. */ Modified: python/branches/py3k/Python/opcode_targets.h ============================================================================== --- python/branches/py3k/Python/opcode_targets.h (original) +++ python/branches/py3k/Python/opcode_targets.h Sat Sep 4 20:43:52 2010 @@ -4,7 +4,7 @@ &&TARGET_ROT_TWO, &&TARGET_ROT_THREE, &&TARGET_DUP_TOP, - &&TARGET_ROT_FOUR, + &&TARGET_DUP_TOP_TWO, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, @@ -98,7 +98,7 @@ &&TARGET_DELETE_ATTR, &&TARGET_STORE_GLOBAL, &&TARGET_DELETE_GLOBAL, - &&TARGET_DUP_TOPX, + &&_unknown_opcode, &&TARGET_LOAD_CONST, &&TARGET_LOAD_NAME, &&TARGET_BUILD_TUPLE, From python-checkins at python.org Sat Sep 4 20:45:37 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 20:45:37 +0200 (CEST) Subject: [Python-checkins] r84502 - python/branches/py3k/Modules/_pickle.c Message-ID: <20100904184537.33570FA6F@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 20:45:37 2010 New Revision: 84502 Log: Fix typos in error messages (thanks Arfrever). Modified: python/branches/py3k/Modules/_pickle.c Modified: python/branches/py3k/Modules/_pickle.c ============================================================================== --- python/branches/py3k/Modules/_pickle.c (original) +++ python/branches/py3k/Modules/_pickle.c Sat Sep 4 20:45:37 2010 @@ -497,7 +497,7 @@ /* XXX: Should bytearray be supported too? */ if (!PyBytes_Check(data)) { PyErr_SetString(PyExc_ValueError, - "read() from the underlying stream did not" + "read() from the underlying stream did not " "return bytes"); Py_DECREF(data); return -1; @@ -530,7 +530,7 @@ /* XXX: Should bytearray be supported too? */ if (!PyBytes_Check(data)) { PyErr_SetString(PyExc_ValueError, - "readline() from the underlying stream did not" + "readline() from the underlying stream did not " "return bytes"); return -1; } From python-checkins at python.org Sat Sep 4 20:46:56 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 20:46:56 +0200 (CEST) Subject: [Python-checkins] r84503 - in python/branches/release31-maint: Modules/_pickle.c Message-ID: <20100904184656.C7AB1FA9F@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 20:46:56 2010 New Revision: 84503 Log: Merged revisions 84502 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84502 | antoine.pitrou | 2010-09-04 20:45:37 +0200 (sam., 04 sept. 2010) | 3 lines Fix typos in error messages (thanks Arfrever). ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Modules/_pickle.c Modified: python/branches/release31-maint/Modules/_pickle.c ============================================================================== --- python/branches/release31-maint/Modules/_pickle.c (original) +++ python/branches/release31-maint/Modules/_pickle.c Sat Sep 4 20:46:56 2010 @@ -497,7 +497,7 @@ /* XXX: Should bytearray be supported too? */ if (!PyBytes_Check(data)) { PyErr_SetString(PyExc_ValueError, - "read() from the underlying stream did not" + "read() from the underlying stream did not " "return bytes"); Py_DECREF(data); return -1; @@ -530,7 +530,7 @@ /* XXX: Should bytearray be supported too? */ if (!PyBytes_Check(data)) { PyErr_SetString(PyExc_ValueError, - "readline() from the underlying stream did not" + "readline() from the underlying stream did not " "return bytes"); return -1; } From python-checkins at python.org Sat Sep 4 20:50:35 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 20:50:35 +0200 (CEST) Subject: [Python-checkins] r84504 - python/branches/py3k/Lib/test/test_socket.py Message-ID: <20100904185035.67729F99B@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 20:50:35 2010 New Revision: 84504 Log: Issue #9777: test_idna requires the "network" resource Modified: python/branches/py3k/Lib/test/test_socket.py Modified: python/branches/py3k/Lib/test/test_socket.py ============================================================================== --- python/branches/py3k/Lib/test/test_socket.py (original) +++ python/branches/py3k/Lib/test/test_socket.py Sat Sep 4 20:50:35 2010 @@ -642,6 +642,7 @@ self.assertRaises(socket.error, socket.getnameinfo, ('mail.python.org',0), 0) def test_idna(self): + support.requires('network') # these should all be successful socket.gethostbyname('?????????.python.org') socket.gethostbyname_ex('?????????.python.org') From python-checkins at python.org Sat Sep 4 22:16:54 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 22:16:54 +0200 (CEST) Subject: [Python-checkins] r84505 - in python/branches/py3k: Lib/json/decoder.py Lib/json/scanner.py Lib/json/tests/test_decode.py Misc/NEWS Modules/_json.c Message-ID: <20100904201654.2F7AEF924@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 22:16:53 2010 New Revision: 84505 Log: Issue #7451: Improve decoding performance of JSON objects, and reduce the memory consumption of said decoded objects when they use the same strings as keys. Modified: python/branches/py3k/Lib/json/decoder.py python/branches/py3k/Lib/json/scanner.py python/branches/py3k/Lib/json/tests/test_decode.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/_json.c Modified: python/branches/py3k/Lib/json/decoder.py ============================================================================== --- python/branches/py3k/Lib/json/decoder.py (original) +++ python/branches/py3k/Lib/json/decoder.py Sat Sep 4 22:16:53 2010 @@ -147,10 +147,14 @@ def JSONObject(s_and_end, strict, scan_once, object_hook, object_pairs_hook, - _w=WHITESPACE.match, _ws=WHITESPACE_STR): + memo=None, _w=WHITESPACE.match, _ws=WHITESPACE_STR): s, end = s_and_end pairs = [] pairs_append = pairs.append + # Backwards compatibility + if memo is None: + memo = {} + memo_get = memo.setdefault # Use a slice to prevent IndexError from being raised, the following # check will raise a more specific ValueError if the string is empty nextchar = s[end:end + 1] @@ -167,6 +171,7 @@ end += 1 while True: key, end = scanstring(s, end, strict) + key = memo_get(key, key) # To skip some function call overhead we optimize the fast paths where # the JSON key separator is ": " or just ":". if s[end:end + 1] != ':': @@ -214,7 +219,7 @@ pairs = object_hook(pairs) return pairs, end -def JSONArray(s_and_end, scan_once, context, _w=WHITESPACE.match): +def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR): s, end = s_and_end values = [] nextchar = s[end:end + 1] @@ -314,6 +319,7 @@ self.parse_object = JSONObject self.parse_array = JSONArray self.parse_string = scanstring + self.memo = {} self.scan_once = make_scanner(self) Modified: python/branches/py3k/Lib/json/scanner.py ============================================================================== --- python/branches/py3k/Lib/json/scanner.py (original) +++ python/branches/py3k/Lib/json/scanner.py Sat Sep 4 22:16:53 2010 @@ -22,6 +22,8 @@ parse_int = context.parse_int parse_constant = context.parse_constant object_hook = context.object_hook + object_pairs_hook = context.object_pairs_hook + memo = context.memo def _scan_once(string, idx): try: @@ -33,7 +35,7 @@ return parse_string(string, idx + 1, strict) elif nextchar == '{': return parse_object((string, idx + 1), strict, - _scan_once, object_hook, object_pairs_hook) + _scan_once, object_hook, object_pairs_hook, memo) elif nextchar == '[': return parse_array((string, idx + 1), _scan_once) elif nextchar == 'n' and string[idx:idx + 4] == 'null': @@ -60,6 +62,12 @@ else: raise StopIteration + def scan_once(string, idx): + try: + return _scan_once(string, idx) + finally: + memo.clear() + return _scan_once make_scanner = c_make_scanner or py_make_scanner Modified: python/branches/py3k/Lib/json/tests/test_decode.py ============================================================================== --- python/branches/py3k/Lib/json/tests/test_decode.py (original) +++ python/branches/py3k/Lib/json/tests/test_decode.py Sat Sep 4 22:16:53 2010 @@ -1,10 +1,25 @@ import decimal from unittest import TestCase from io import StringIO +from contextlib import contextmanager import json +import json.decoder +import json.scanner from collections import OrderedDict + + at contextmanager +def use_python_scanner(): + py_scanner = json.scanner.py_make_scanner + old_scanner = json.decoder.make_scanner + json.decoder.make_scanner = py_scanner + try: + yield + finally: + json.decoder.make_scanner = old_scanner + + class TestDecode(TestCase): def test_decimal(self): rval = json.loads('1.1', parse_float=decimal.Decimal) @@ -39,3 +54,16 @@ # exercise the uncommon cases. The array cases are already covered. rval = json.loads('{ "key" : "value" , "k":"v" }') self.assertEquals(rval, {"key":"value", "k":"v"}) + + def check_keys_reuse(self, source, loads): + rval = loads(source) + (a, b), (c, d) = sorted(rval[0]), sorted(rval[1]) + self.assertIs(a, c) + self.assertIs(b, d) + + def test_keys_reuse(self): + s = '[{"a_key": 1, "b_\xe9": 2}, {"a_key": 3, "b_\xe9": 4}]' + self.check_keys_reuse(s, json.loads) + # Disabled: the pure Python version of json simply doesn't work + with use_python_scanner(): + self.check_keys_reuse(s, json.decoder.JSONDecoder().decode) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 4 22:16:53 2010 @@ -165,6 +165,10 @@ Library ------- +- Issue #7451: Improve decoding performance of JSON objects, and reduce + the memory consumption of said decoded objects when they use the same + strings as keys. + - Issue #1100562: Fix deep-copying of objects derived from the list and dict types. Patch by Michele Orr? and Bj?rn Lindqvist. Modified: python/branches/py3k/Modules/_json.c ============================================================================== --- python/branches/py3k/Modules/_json.c (original) +++ python/branches/py3k/Modules/_json.c Sat Sep 4 22:16:53 2010 @@ -36,6 +36,7 @@ PyObject *parse_float; PyObject *parse_int; PyObject *parse_constant; + PyObject *memo; } PyScannerObject; static PyMemberDef scanner_members[] = { @@ -305,6 +306,21 @@ return tpl; } +#define APPEND_OLD_CHUNK \ + if (chunk != NULL) { \ + if (chunks == NULL) { \ + chunks = PyList_New(0); \ + if (chunks == NULL) { \ + goto bail; \ + } \ + } \ + if (PyList_Append(chunks, chunk)) { \ + Py_DECREF(chunk); \ + goto bail; \ + } \ + Py_CLEAR(chunk); \ + } + static PyObject * scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next_end_ptr) { @@ -316,15 +332,14 @@ Return value is a new PyUnicode */ - PyObject *rval; + PyObject *rval = NULL; Py_ssize_t len = PyUnicode_GET_SIZE(pystr); Py_ssize_t begin = end - 1; Py_ssize_t next = begin; const Py_UNICODE *buf = PyUnicode_AS_UNICODE(pystr); - PyObject *chunks = PyList_New(0); - if (chunks == NULL) { - goto bail; - } + PyObject *chunks = NULL; + PyObject *chunk = NULL; + if (end < 0 || len <= end) { PyErr_SetString(PyExc_ValueError, "end is out of bounds"); goto bail; @@ -332,7 +347,6 @@ while (1) { /* Find the end of the string or the next escape */ Py_UNICODE c = 0; - PyObject *chunk = NULL; for (next = end; next < len; next++) { c = buf[next]; if (c == '"' || c == '\\') { @@ -349,15 +363,11 @@ } /* Pick up this chunk if it's not zero length */ if (next != end) { + APPEND_OLD_CHUNK chunk = PyUnicode_FromUnicode(&buf[end], next - end); if (chunk == NULL) { goto bail; } - if (PyList_Append(chunks, chunk)) { - Py_DECREF(chunk); - goto bail; - } - Py_DECREF(chunk); } next++; if (c == '"') { @@ -459,27 +469,34 @@ } #endif } + APPEND_OLD_CHUNK chunk = PyUnicode_FromUnicode(&c, 1); if (chunk == NULL) { goto bail; } - if (PyList_Append(chunks, chunk)) { - Py_DECREF(chunk); + } + + if (chunks == NULL) { + if (chunk != NULL) + rval = chunk; + else + rval = PyUnicode_FromStringAndSize("", 0); + } + else { + APPEND_OLD_CHUNK + rval = join_list_unicode(chunks); + if (rval == NULL) { goto bail; } - Py_DECREF(chunk); + Py_CLEAR(chunks); } - rval = join_list_unicode(chunks); - if (rval == NULL) { - goto bail; - } - Py_DECREF(chunks); *next_end_ptr = end; return rval; bail: *next_end_ptr = -1; Py_XDECREF(chunks); + Py_XDECREF(chunk); return NULL; } @@ -578,6 +595,7 @@ Py_CLEAR(s->parse_float); Py_CLEAR(s->parse_int); Py_CLEAR(s->parse_constant); + Py_CLEAR(s->memo); return 0; } @@ -593,10 +611,16 @@ Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr); Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1; PyObject *val = NULL; - PyObject *rval = PyList_New(0); + PyObject *rval = NULL; PyObject *key = NULL; int strict = PyObject_IsTrue(s->strict); + int has_pairs_hook = (s->object_pairs_hook != Py_None); Py_ssize_t next_idx; + + if (has_pairs_hook) + rval = PyList_New(0); + else + rval = PyDict_New(); if (rval == NULL) return NULL; @@ -606,6 +630,8 @@ /* only loop if the object is non-empty */ if (idx <= end_idx && str[idx] != '}') { while (idx <= end_idx) { + PyObject *memokey; + /* read key */ if (str[idx] != '"') { raise_errmsg("Expecting property name", pystr, idx); @@ -614,6 +640,16 @@ key = scanstring_unicode(pystr, idx + 1, strict, &next_idx); if (key == NULL) goto bail; + memokey = PyDict_GetItem(s->memo, key); + if (memokey != NULL) { + Py_INCREF(memokey); + Py_DECREF(key); + key = memokey; + } + else { + if (PyDict_SetItem(s->memo, key, key) < 0) + goto bail; + } idx = next_idx; /* skip whitespace between key and : delimiter, read :, skip whitespace */ @@ -630,19 +666,24 @@ if (val == NULL) goto bail; - { - PyObject *tuple = PyTuple_Pack(2, key, val); - if (tuple == NULL) + if (has_pairs_hook) { + PyObject *item = PyTuple_Pack(2, key, val); + if (item == NULL) goto bail; - if (PyList_Append(rval, tuple) == -1) { - Py_DECREF(tuple); + Py_CLEAR(key); + Py_CLEAR(val); + if (PyList_Append(rval, item) == -1) { + Py_DECREF(item); goto bail; } - Py_DECREF(tuple); + Py_DECREF(item); + } + else { + if (PyDict_SetItem(rval, key, val) < 0) + goto bail; + Py_CLEAR(key); + Py_CLEAR(val); } - - Py_CLEAR(key); - Py_CLEAR(val); idx = next_idx; /* skip whitespace before } or , */ @@ -672,36 +713,23 @@ *next_idx_ptr = idx + 1; - if (s->object_pairs_hook != Py_None) { + if (has_pairs_hook) { val = PyObject_CallFunctionObjArgs(s->object_pairs_hook, rval, NULL); - if (val == NULL) - goto bail; Py_DECREF(rval); return val; } - val = PyDict_New(); - if (val == NULL) - goto bail; - if (PyDict_MergeFromSeq2(val, rval, 1) == -1) - goto bail; - Py_DECREF(rval); - rval = val; - /* if object_hook is not None: rval = object_hook(rval) */ if (s->object_hook != Py_None) { val = PyObject_CallFunctionObjArgs(s->object_hook, rval, NULL); - if (val == NULL) - goto bail; Py_DECREF(rval); - rval = val; - val = NULL; + return val; } return rval; bail: Py_XDECREF(key); Py_XDECREF(val); - Py_DECREF(rval); + Py_XDECREF(rval); return NULL; } @@ -988,6 +1016,9 @@ Py_TYPE(pystr)->tp_name); return NULL; } + PyDict_Clear(s->memo); + if (rval == NULL) + return NULL; return _build_rval_index_tuple(rval, next_idx); } @@ -1021,6 +1052,12 @@ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:make_scanner", kwlist, &ctx)) return -1; + if (s->memo == NULL) { + s->memo = PyDict_New(); + if (s->memo == NULL) + goto bail; + } + /* All of these will fail "gracefully" so we don't need to verify them */ s->strict = PyObject_GetAttrString(ctx, "strict"); if (s->strict == NULL) From python-checkins at python.org Sat Sep 4 22:53:30 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 22:53:30 +0200 (CEST) Subject: [Python-checkins] r84506 - in python/branches/py3k: Lib/test/test_fileio.py Misc/NEWS PC/msvcrtmodule.c Message-ID: <20100904205330.0C9E3FB04@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 22:53:29 2010 New Revision: 84506 Log: Issue #8734: Avoid crash in msvcrt.get_osfhandle() when an invalid file descriptor is provided. Patch by Pascal Chambon. Modified: python/branches/py3k/Lib/test/test_fileio.py python/branches/py3k/Misc/NEWS python/branches/py3k/PC/msvcrtmodule.c Modified: python/branches/py3k/Lib/test/test_fileio.py ============================================================================== --- python/branches/py3k/Lib/test/test_fileio.py (original) +++ python/branches/py3k/Lib/test/test_fileio.py Sat Sep 4 22:53:29 2010 @@ -309,6 +309,9 @@ def testInvalidFd(self): self.assertRaises(ValueError, _FileIO, -10) self.assertRaises(OSError, _FileIO, make_bad_fd()) + if sys.platform == 'win32': + import msvcrt + self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd()) def testBadModeArgument(self): # verify that we get a sensible error message for bad mode argument Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 4 22:53:29 2010 @@ -100,6 +100,9 @@ Extensions ---------- +- Issue #8734: Avoid crash in msvcrt.get_osfhandle() when an invalid file + descriptor is provided. Patch by Pascal Chambon. + - Issue #7736: Release the GIL around calls to opendir() and closedir() in the posix module. Patch by Marcin Bachry. Modified: python/branches/py3k/PC/msvcrtmodule.c ============================================================================== --- python/branches/py3k/PC/msvcrtmodule.c (original) +++ python/branches/py3k/PC/msvcrtmodule.c Sat Sep 4 22:53:29 2010 @@ -143,6 +143,9 @@ if (!PyArg_ParseTuple(args,"i:get_osfhandle", &fd)) return NULL; + if (!_PyVerify_fd(fd)) + return PyErr_SetFromErrno(PyExc_IOError); + handle = _get_osfhandle(fd); if (handle == -1) return PyErr_SetFromErrno(PyExc_IOError); From python-checkins at python.org Sat Sep 4 23:02:41 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 23:02:41 +0200 (CEST) Subject: [Python-checkins] r84507 - in python/branches/release31-maint: Lib/test/test_fileio.py Misc/NEWS PC/msvcrtmodule.c Message-ID: <20100904210241.72D3FF794@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 23:02:41 2010 New Revision: 84507 Log: Merged revisions 84506 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84506 | antoine.pitrou | 2010-09-04 22:53:29 +0200 (sam., 04 sept. 2010) | 5 lines Issue #8734: Avoid crash in msvcrt.get_osfhandle() when an invalid file descriptor is provided. Patch by Pascal Chambon. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_fileio.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/PC/msvcrtmodule.c Modified: python/branches/release31-maint/Lib/test/test_fileio.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_fileio.py (original) +++ python/branches/release31-maint/Lib/test/test_fileio.py Sat Sep 4 23:02:41 2010 @@ -313,6 +313,9 @@ def testInvalidFd(self): self.assertRaises(ValueError, _FileIO, -10) self.assertRaises(OSError, _FileIO, make_bad_fd()) + if sys.platform == 'win32': + import msvcrt + self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd()) def testBadModeArgument(self): # verify that we get a sensible error message for bad mode argument Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sat Sep 4 23:02:41 2010 @@ -477,6 +477,9 @@ Extension Modules ----------------- +- Issue #8734: Avoid crash in msvcrt.get_osfhandle() when an invalid file + descriptor is provided. Patch by Pascal Chambon. + - Issue #7736: Release the GIL around calls to opendir() and closedir() in the posix module. Patch by Marcin Bachry. Modified: python/branches/release31-maint/PC/msvcrtmodule.c ============================================================================== --- python/branches/release31-maint/PC/msvcrtmodule.c (original) +++ python/branches/release31-maint/PC/msvcrtmodule.c Sat Sep 4 23:02:41 2010 @@ -143,6 +143,9 @@ if (!PyArg_ParseTuple(args,"i:get_osfhandle", &fd)) return NULL; + if (!_PyVerify_fd(fd)) + return PyErr_SetFromErrno(PyExc_IOError); + handle = _get_osfhandle(fd); if (handle == -1) return PyErr_SetFromErrno(PyExc_IOError); From python-checkins at python.org Sat Sep 4 23:24:42 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 4 Sep 2010 23:24:42 +0200 (CEST) Subject: [Python-checkins] r84508 - in python/branches/release27-maint: Lib/test/test_fileio.py Misc/NEWS PC/msvcrtmodule.c Message-ID: <20100904212442.3F72DF99B@mail.python.org> Author: antoine.pitrou Date: Sat Sep 4 23:24:42 2010 New Revision: 84508 Log: Merged revisions 84506 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84506 | antoine.pitrou | 2010-09-04 22:53:29 +0200 (sam., 04 sept. 2010) | 5 lines Issue #8734: Avoid crash in msvcrt.get_osfhandle() when an invalid file descriptor is provided. Patch by Pascal Chambon. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_fileio.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/PC/msvcrtmodule.c Modified: python/branches/release27-maint/Lib/test/test_fileio.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_fileio.py (original) +++ python/branches/release27-maint/Lib/test/test_fileio.py Sat Sep 4 23:24:42 2010 @@ -312,6 +312,9 @@ def testInvalidFd(self): self.assertRaises(ValueError, _FileIO, -10) self.assertRaises(OSError, _FileIO, make_bad_fd()) + if sys.platform == 'win32': + import msvcrt + self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd()) def testBadModeArgument(self): # verify that we get a sensible error message for bad mode argument Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sat Sep 4 23:24:42 2010 @@ -228,6 +228,9 @@ Extension Modules ----------------- +- Issue #8734: Avoid crash in msvcrt.get_osfhandle() when an invalid file + descriptor is provided. Patch by Pascal Chambon. + - Issue #7736: Release the GIL around calls to opendir() and closedir() in the posix module. Patch by Marcin Bachry. Modified: python/branches/release27-maint/PC/msvcrtmodule.c ============================================================================== --- python/branches/release27-maint/PC/msvcrtmodule.c (original) +++ python/branches/release27-maint/PC/msvcrtmodule.c Sat Sep 4 23:24:42 2010 @@ -141,6 +141,9 @@ if (!PyArg_ParseTuple(args,"i:get_osfhandle", &fd)) return NULL; + if (!_PyVerify_fd(fd)) + return PyErr_SetFromErrno(PyExc_IOError); + handle = _get_osfhandle(fd); if (handle == -1) return PyErr_SetFromErrno(PyExc_IOError); From python-checkins at python.org Sun Sep 5 00:12:46 2010 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 5 Sep 2010 00:12:46 +0200 (CEST) Subject: [Python-checkins] r84509 - python/branches/py3k/Tools/msi/msilib.py Message-ID: <20100904221246.6EF5FFA4B@mail.python.org> Author: martin.v.loewis Date: Sun Sep 5 00:12:46 2010 New Revision: 84509 Log: Drop cabarc artifact. Modified: python/branches/py3k/Tools/msi/msilib.py Modified: python/branches/py3k/Tools/msi/msilib.py ============================================================================== --- python/branches/py3k/Tools/msi/msilib.py (original) +++ python/branches/py3k/Tools/msi/msilib.py Sun Sep 5 00:12:46 2010 @@ -381,7 +381,6 @@ add_data(db, "Media", [(1, self.index, None, "#"+self.name, None, None)]) add_stream(db, self.name, self.name+".cab") - os.unlink(self.name+".txt") os.unlink(self.name+".cab") db.Commit() From python-checkins at python.org Sun Sep 5 00:46:06 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 00:46:06 +0200 (CEST) Subject: [Python-checkins] r84510 - in python/branches/py3k: Doc/library/functools.rst Doc/whatsnew/3.2.rst Lib/functools.py Lib/re.py Lib/test/test_functools.py Message-ID: <20100904224606.6CC40FA4B@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 00:46:06 2010 New Revision: 84510 Log: Adopt more descriptive attribute names as suggested on python-dev. Modified: python/branches/py3k/Doc/library/functools.rst python/branches/py3k/Doc/whatsnew/3.2.rst python/branches/py3k/Lib/functools.py python/branches/py3k/Lib/re.py python/branches/py3k/Lib/test/test_functools.py Modified: python/branches/py3k/Doc/library/functools.rst ============================================================================== --- python/branches/py3k/Doc/library/functools.rst (original) +++ python/branches/py3k/Doc/library/functools.rst Sun Sep 5 00:46:06 2010 @@ -47,12 +47,12 @@ results, the positional and keyword arguments to the function must be hashable. - The wrapped function is instrumented with two attributes, :attr:`hits` - and :attr:`misses` which count the number of successful or unsuccessful + The wrapped function is instrumented with two attributes, :attr:`cache_hits` + and :attr:`cache_misses` which count the number of successful or unsuccessful cache lookups. These statistics are helpful for tuning the *maxsize* parameter and for measuring the cache's effectiveness. - The wrapped function also has a :attr:`clear` attribute which can be + The wrapped function also has a :attr:`cache_clear` attribute which can be called (with no arguments) to clear the cache. The original underlying function is accessible through the Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 00:46:06 2010 @@ -85,17 +85,17 @@ return c.fetchone()[0] To help with choosing an effective cache size, the wrapped function is - instrumented with two attributes *hits* and *misses*:: + instrumented with two attributes *cache_hits* and *cache_misses*:: >>> for name in user_requests: ... get_phone_number(name) - >>> print(get_phone_number.hits, get_phone_number.misses) + >>> print(get_phone_number.cache_hits, get_phone_number.cache_misses) 4805 980 If the phonelist table gets updated, the outdated contents of the cache can be cleared with:: - >>> get_phone_number.clear() + >>> get_phone_number.cache_clear() (Contributed by Raymond Hettinger) Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Sun Sep 5 00:46:06 2010 @@ -142,23 +142,23 @@ with lock: result = cache[key] cache_renew(key) # record recent use of this key - wrapper.hits += 1 + wrapper.cache_hits += 1 except KeyError: result = user_function(*args, **kwds) with lock: cache[key] = result # record recent use of this key - wrapper.misses += 1 + wrapper.cache_misses += 1 if len(cache) > maxsize: cache_popitem(0) # purge least recently used cache entry return result - def clear(): + def cache_clear(): """Clear the cache and cache statistics""" with lock: cache.clear() - wrapper.hits = wrapper.misses = 0 + wrapper.cache_hits = wrapper.cache_misses = 0 - wrapper.hits = wrapper.misses = 0 - wrapper.clear = clear + wrapper.cache_hits = wrapper.cache_misses = 0 + wrapper.cache_clear = cache_clear return wrapper return decorating_function Modified: python/branches/py3k/Lib/re.py ============================================================================== --- python/branches/py3k/Lib/re.py (original) +++ python/branches/py3k/Lib/re.py Sun Sep 5 00:46:06 2010 @@ -207,8 +207,8 @@ def purge(): "Clear the regular expression caches" - _compile_typed.clear() - _compile_repl.clear() + _compile_typed.cache_clear() + _compile_repl.cache_clear() def template(pattern, flags=0): "Compile a template pattern, returning a pattern object" Modified: python/branches/py3k/Lib/test/test_functools.py ============================================================================== --- python/branches/py3k/Lib/test/test_functools.py (original) +++ python/branches/py3k/Lib/test/test_functools.py Sun Sep 5 00:46:06 2010 @@ -508,21 +508,21 @@ actual = f(x, y) expected = orig(x, y) self.assertEquals(actual, expected) - self.assert_(f.hits > f.misses) - self.assertEquals(f.hits + f.misses, 1000) + self.assert_(f.cache_hits > f.cache_misses) + self.assertEquals(f.cache_hits + f.cache_misses, 1000) - f.clear() # test clearing - self.assertEqual(f.hits, 0) - self.assertEqual(f.misses, 0) + f.cache_clear() # test clearing + self.assertEqual(f.cache_hits, 0) + self.assertEqual(f.cache_misses, 0) f(x, y) - self.assertEqual(f.hits, 0) - self.assertEqual(f.misses, 1) + self.assertEqual(f.cache_hits, 0) + self.assertEqual(f.cache_misses, 1) # Test bypassing the cache self.assertIs(f.__wrapped__, orig) f.__wrapped__(x, y) - self.assertEqual(f.hits, 0) - self.assertEqual(f.misses, 1) + self.assertEqual(f.cache_hits, 0) + self.assertEqual(f.cache_misses, 1) # test size zero (which means "never-cache") @functools.lru_cache(0) From python-checkins at python.org Sun Sep 5 01:53:24 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 01:53:24 +0200 (CEST) Subject: [Python-checkins] r84511 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100904235324.D465BFCB4@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 01:53:24 2010 New Revision: 84511 Log: Update whatsnew for Pep3147. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 01:53:24 2010 @@ -51,12 +51,65 @@ This article explains the new features in Python 3.2, compared to 3.1. +PEP 3147: PYC Repository Directories +===================================== + +Python's scheme for caching bytecode in *.pyc* files did not work well in +environments with multiple python interpreters. If one interpreter encountered +a cached file created by another interpreter, it would recompile the source and +overwrite the cached file, thus losing the benefits of caching. + +The issue of "pyc fights" has become more pronounced as it has become +common-place for Linux distributions to ship with multiple versions of Python. +These conflicts also arise with CPython alternatives such as Unladen Swallow. + +To solve this problem, Python's import machinery has been extended to use +distinct filenames for each interpreter. Instead of Python3.2 and Python3.3 and +UnladenSwallow each competing for a file called "mymodule.pyc", they will now +look for "mymodule.cpython-32.pyc", "mymodule.cpython-33.pyc", and +"mymodule.unladen10.pyc". And to keep prevent all of these new files from +cluttering source directories, the *pyc* files are now collected in a +"__pycache__" directory stored under the package directory. + +Aside from the filenames and target directories, the new scheme has a few +aspects that are visible to the programmer: + +* Imported modules now have a :attr:`__cached__` attribute which stores the + name of the actual file that was imported:: + + >>> import collections + >>> collections.__cached__ + 'c:/py32/lib/__pycache__/collections.cpython-32.pyc' + +* The tag that is unique to each interpreter is accessible from the :mod:`imp` + module:: + + >>> import imp + >>> imp.get_tag() + 'cpython-32' + +* Scripts that try to deduce source filename from the imported file now need to + be smarter. It is no longer sufficient to simply strip the "c" from a ".pyc" + filename. Instead, use the new functions in the :mod:`imp` module: + + >>> imp.source_from_cache('c:/py32/lib/__pycache__/collections.cpython-32.pyc') + 'c:/py32/lib/collections.py' + >>> imp.cache_from_source('c:/py32/lib/collections.py') + 'c:/py32/lib/__pycache__/collections.cpython-32.pyc' + +* The :mod:`py_compile` and :mod:`compileall` modules have been updated to + reflect the new naming convention and target directory. + +.. seealso:: + + :pep:`3147` - PYC Repository Directories + PEP written by Barry Warsaw. + PEPs ==== Implemented PEPs: -* :pep:`3147` * :pep:`3149` From python-checkins at python.org Sun Sep 5 02:09:07 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 5 Sep 2010 02:09:07 +0200 (CEST) Subject: [Python-checkins] r84512 - in python/branches/py3k: configure configure.in pyconfig.h.in Message-ID: <20100905000907.ED500EB74@mail.python.org> Author: benjamin.peterson Date: Sun Sep 5 02:09:07 2010 New Revision: 84512 Log: run autoreconf Modified: python/branches/py3k/configure python/branches/py3k/configure.in python/branches/py3k/pyconfig.h.in Modified: python/branches/py3k/configure ============================================================================== --- python/branches/py3k/configure (original) +++ python/branches/py3k/configure Sun Sep 5 02:09:07 2010 @@ -1,14 +1,14 @@ #! /bin/sh -# From configure.in Revision: 84379 . +# From configure.in Revision: 84477 . # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.67 for python 3.2. +# Generated by GNU Autoconf 2.65 for python 3.2. # # Report bugs to . # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software -# Foundation, Inc. +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. # # # This configure script is free software; the Free Software Foundation @@ -320,7 +320,7 @@ test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" } # as_fn_mkdir_p @@ -360,19 +360,19 @@ fi # as_fn_arith -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. +# script with status $?, using 1 if that was 0. as_fn_error () { - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 fi - $as_echo "$as_me: error: $2" >&2 + $as_echo "$as_me: error: $1" >&2 as_fn_exit $as_status } # as_fn_error @@ -534,7 +534,7 @@ exec 6>&1 # Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` @@ -826,9 +826,8 @@ fi case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. @@ -873,7 +872,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" + as_fn_error "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -899,7 +898,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" + as_fn_error "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1103,7 +1102,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" + as_fn_error "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1119,7 +1118,7 @@ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" + as_fn_error "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1149,8 +1148,8 @@ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" + -*) as_fn_error "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information." ;; *=*) @@ -1158,7 +1157,7 @@ # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + as_fn_error "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; @@ -1176,13 +1175,13 @@ if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" + as_fn_error "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi @@ -1205,7 +1204,7 @@ [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" + as_fn_error "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' @@ -1219,8 +1218,8 @@ if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used" >&2 + $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1235,9 +1234,9 @@ ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" + as_fn_error "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" + as_fn_error "pwd does not report name of working directory" # Find the source files, if location was not specified. @@ -1276,11 +1275,11 @@ fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" + as_fn_error "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then @@ -1320,7 +1319,7 @@ --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages + -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files @@ -1505,9 +1504,9 @@ if $ac_init_version; then cat <<\_ACEOF python configure 3.2 -generated by GNU Autoconf 2.67 +generated by GNU Autoconf 2.65 -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2009 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1577,7 +1576,7 @@ mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { + test $ac_status = 0; } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : @@ -1601,10 +1600,10 @@ ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval "test \"\${$3+set}\"" = set; then : + if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 @@ -1640,7 +1639,7 @@ else ac_header_preproc=no fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } @@ -1663,15 +1662,17 @@ $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## -------------------------------------- ## +( cat <<\_ASBOX +## -------------------------------------- ## ## Report this to http://bugs.python.org/ ## -## -------------------------------------- ##" +## -------------------------------------- ## +_ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" @@ -1735,7 +1736,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -1812,7 +1813,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else eval "$3=no" @@ -1866,7 +1867,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 $as_echo_n "checking for uint$2_t... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else eval "$3=no" @@ -1896,7 +1897,8 @@ esac fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if eval test \"x\$"$3"\" = x"no"; then : + eval as_val=\$$3 + if test "x$as_val" = x""no; then : else break @@ -1919,7 +1921,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 $as_echo_n "checking for int$2_t... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else eval "$3=no" @@ -1930,11 +1932,11 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default - enum { N = $2 / 2 - 1 }; int main () { -static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; +static int test_array [1 - 2 * !(enum { N = $2 / 2 - 1 }; + 0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; test_array [0] = 0 ; @@ -1945,11 +1947,11 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default - enum { N = $2 / 2 - 1 }; int main () { -static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) +static int test_array [1 - 2 * !(enum { N = $2 / 2 - 1 }; + ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; test_array [0] = 0 @@ -1970,7 +1972,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if eval test \"x\$"$3"\" = x"no"; then : + eval as_val=\$$3 + if test "x$as_val" = x""no; then : else break @@ -2170,7 +2173,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2238,7 +2241,7 @@ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 $as_echo_n "checking for $2.$3... " >&6; } -if eval "test \"\${$4+set}\"" = set; then : +if { as_var=$4; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2286,18 +2289,15 @@ } # ac_fn_c_check_member -# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES -# --------------------------------------------- -# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR -# accordingly. +# ac_fn_c_check_decl LINENO SYMBOL VAR +# ------------------------------------ +# Tests whether SYMBOL is declared, setting cache variable VAR accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - as_decl_name=`echo $2|sed 's/ *(.*//'` - as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 -$as_echo_n "checking whether $as_decl_name is declared... " >&6; } -if eval "test \"\${$3+set}\"" = set; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $2 is declared" >&5 +$as_echo_n "checking whether $2 is declared... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2306,12 +2306,8 @@ int main () { -#ifndef $as_decl_name -#ifdef __cplusplus - (void) $as_decl_use; -#else - (void) $as_decl_name; -#endif +#ifndef $2 + (void) $2; #endif ; @@ -2336,7 +2332,7 @@ running configure, to aid debugging if configure makes a mistake. It was created by python $as_me 3.2, which was -generated by GNU Autoconf 2.67. Invocation command line was +generated by GNU Autoconf 2.65. Invocation command line was $ $0 $@ @@ -2446,9 +2442,11 @@ { echo - $as_echo "## ---------------- ## + cat <<\_ASBOX +## ---------------- ## ## Cache variables. ## -## ---------------- ##" +## ---------------- ## +_ASBOX echo # The following way of writing the cache mishandles newlines in values, ( @@ -2482,9 +2480,11 @@ ) echo - $as_echo "## ----------------- ## + cat <<\_ASBOX +## ----------------- ## ## Output variables. ## -## ----------------- ##" +## ----------------- ## +_ASBOX echo for ac_var in $ac_subst_vars do @@ -2497,9 +2497,11 @@ echo if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## + cat <<\_ASBOX +## ------------------- ## ## File substitutions. ## -## ------------------- ##" +## ------------------- ## +_ASBOX echo for ac_var in $ac_subst_files do @@ -2513,9 +2515,11 @@ fi if test -s confdefs.h; then - $as_echo "## ----------- ## + cat <<\_ASBOX +## ----------- ## ## confdefs.h. ## -## ----------- ##" +## ----------- ## +_ASBOX echo cat confdefs.h echo @@ -2570,12 +2574,7 @@ ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac + ac_site_file1=$CONFIG_SITE elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site @@ -2590,11 +2589,7 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5 ; } + . "$ac_site_file" fi done @@ -2670,7 +2665,7 @@ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 + as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## @@ -2704,7 +2699,7 @@ VERSION=3.2 -# Version number or Python's own shared library file. +# Version number of Python's own shared library file. SOVERSION=1.0 @@ -2771,7 +2766,7 @@ UNIVERSALSDK=$enableval if test ! -d "${UNIVERSALSDK}" then - as_fn_error $? "--enable-universalsdk specifies non-existing SDK: ${UNIVERSALSDK}" "$LINENO" 5 + as_fn_error "--enable-universalsdk specifies non-existing SDK: ${UNIVERSALSDK}" "$LINENO" 5 fi ;; esac @@ -3163,7 +3158,7 @@ # If the user switches compilers, we can't believe the cache if test ! -z "$ac_cv_prog_CC" -a ! -z "$CC" -a "$CC" != "$ac_cv_prog_CC" then - as_fn_error $? "cached CC is different -- throw away $cache_file + as_fn_error "cached CC is different -- throw away $cache_file (it is also a good idea to do 'make clean' before compiling)" "$LINENO" 5 fi @@ -3473,8 +3468,8 @@ test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5 ; } +as_fn_error "no acceptable C compiler found in \$PATH +See \`config.log' for more details." "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 @@ -3588,8 +3583,9 @@ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "C compiler cannot create executables +See \`config.log' for more details." "$LINENO" 5; }; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } @@ -3631,8 +3627,8 @@ else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5 ; } +as_fn_error "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 @@ -3689,9 +3685,9 @@ else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. +as_fn_error "cannot run C compiled programs. If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5 ; } +See \`config.log' for more details." "$LINENO" 5; } fi fi fi @@ -3742,8 +3738,8 @@ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5 ; } +as_fn_error "cannot compute suffix of object files: cannot compile +See \`config.log' for more details." "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi @@ -4196,7 +4192,7 @@ # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -4212,11 +4208,11 @@ ac_preproc_ok=: break fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi @@ -4255,7 +4251,7 @@ # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -4271,18 +4267,18 @@ ac_preproc_ok=: break fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5 ; } +as_fn_error "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." "$LINENO" 5; } fi ac_ext=c @@ -4343,7 +4339,7 @@ done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP @@ -4409,7 +4405,7 @@ done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP @@ -4541,7 +4537,8 @@ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -4803,7 +4800,8 @@ esac if test "$ac_cv_c_inline" != no ; then - $as_echo "#define USE_INLINE 1" >>confdefs.h + +$as_echo "#define USE_INLINE 1" >>confdefs.h fi @@ -5145,22 +5143,16 @@ esac ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi + for ac_t in install-sh install.sh shtool; do + if test -f "$ac_dir/$ac_t"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/$ac_t -c" + break 2 + fi + done done if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 + as_fn_error "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, @@ -5497,7 +5489,7 @@ ARCH_RUN_32BIT="/usr/bin/arch -i386 -ppc" else - as_fn_error $? "proper usage is --with-universal-arch=32-bit|64-bit|all|intel|3-way" "$LINENO" 5 + as_fn_error "proper usage is --with-universal-arch=32-bit|64-bit|all|intel|3-way" "$LINENO" 5 fi @@ -6074,7 +6066,8 @@ do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -6088,7 +6081,7 @@ as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 $as_echo_n "checking for $ac_hdr that defines DIR... " >&6; } -if eval "test \"\${$as_ac_Header+set}\"" = set; then : +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -6115,7 +6108,8 @@ eval ac_res=\$$as_ac_Header { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 _ACEOF @@ -6635,8 +6629,9 @@ if test "$ac_cv_type_int" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (int) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (int) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_int=0 fi @@ -6668,8 +6663,9 @@ if test "$ac_cv_type_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (long) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (long) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_long=0 fi @@ -6701,8 +6697,9 @@ if test "$ac_cv_type_void_p" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (void *) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (void *) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_void_p=0 fi @@ -6734,8 +6731,9 @@ if test "$ac_cv_type_short" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (short) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (short) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_short=0 fi @@ -6767,8 +6765,9 @@ if test "$ac_cv_type_float" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (float) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (float) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_float=0 fi @@ -6800,8 +6799,9 @@ if test "$ac_cv_type_double" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (double) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (double) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_double=0 fi @@ -6833,8 +6833,9 @@ if test "$ac_cv_type_fpos_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (fpos_t) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (fpos_t) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_fpos_t=0 fi @@ -6866,8 +6867,9 @@ if test "$ac_cv_type_size_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (size_t) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (size_t) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_size_t=0 fi @@ -6899,8 +6901,9 @@ if test "$ac_cv_type_pid_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (pid_t) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (pid_t) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_pid_t=0 fi @@ -6959,8 +6962,9 @@ if test "$ac_cv_type_long_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (long long) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (long long) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_long_long=0 fi @@ -7020,8 +7024,9 @@ if test "$ac_cv_type_long_double" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (long double) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (long double) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_long_double=0 fi @@ -7082,8 +7087,9 @@ if test "$ac_cv_type__Bool" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (_Bool) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (_Bool) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof__Bool=0 fi @@ -7127,8 +7133,9 @@ if test "$ac_cv_type_uintptr_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (uintptr_t) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (uintptr_t) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_uintptr_t=0 fi @@ -7168,8 +7175,9 @@ if test "$ac_cv_type_off_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (off_t) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (off_t) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_off_t=0 fi @@ -7230,8 +7238,9 @@ if test "$ac_cv_type_time_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (time_t) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (time_t) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_time_t=0 fi @@ -7302,8 +7311,9 @@ if test "$ac_cv_type_pthread_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (pthread_t) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (pthread_t) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_pthread_t=0 fi @@ -7390,7 +7400,7 @@ MACOSX_DEFAULT_ARCH="ppc" ;; *) - as_fn_error $? "Unexpected output of 'arch' on OSX" "$LINENO" 5 + as_fn_error "Unexpected output of 'arch' on OSX" "$LINENO" 5 ;; esac else @@ -7402,7 +7412,7 @@ MACOSX_DEFAULT_ARCH="ppc64" ;; *) - as_fn_error $? "Unexpected output of 'arch' on OSX" "$LINENO" 5 + as_fn_error "Unexpected output of 'arch' on OSX" "$LINENO" 5 ;; esac @@ -7428,7 +7438,7 @@ $as_echo "yes" >&6; } if test $enable_shared = "yes" then - as_fn_error $? "Specifying both --enable-shared and --enable-framework is not supported, use only --enable-framework instead" "$LINENO" 5 + as_fn_error "Specifying both --enable-shared and --enable-framework is not supported, use only --enable-framework instead" "$LINENO" 5 fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 @@ -8206,12 +8216,12 @@ withval=$with_dbmliborder; if test x$with_dbmliborder = xyes then -as_fn_error $? "proper usage is --with-dbmliborder=db1:db2:..." "$LINENO" 5 +as_fn_error "proper usage is --with-dbmliborder=db1:db2:..." "$LINENO" 5 else for db in `echo $with_dbmliborder | sed 's/:/ /g'`; do if test x$db != xndbm && test x$db != xgdbm && test x$db != xbdb then - as_fn_error $? "proper usage is --with-dbmliborder=db1:db2:..." "$LINENO" 5 + as_fn_error "proper usage is --with-dbmliborder=db1:db2:..." "$LINENO" 5 fi done fi @@ -9210,7 +9220,7 @@ $as_echo "#define WITH_VALGRIND 1" >>confdefs.h else - as_fn_error $? "Valgrind support requested but headers not available" "$LINENO" 5 + as_fn_error "Valgrind support requested but headers not available" "$LINENO" 5 fi @@ -9327,7 +9337,8 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -10261,7 +10272,8 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -10270,44 +10282,25 @@ done -ac_fn_c_check_func "$LINENO" "dup2" "ac_cv_func_dup2" -if test "x$ac_cv_func_dup2" = x""yes; then : - $as_echo "#define HAVE_DUP2 1" >>confdefs.h - -else - case " $LIBOBJS " in - *" dup2.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS dup2.$ac_objext" - ;; -esac - -fi - -ac_fn_c_check_func "$LINENO" "getcwd" "ac_cv_func_getcwd" -if test "x$ac_cv_func_getcwd" = x""yes; then : - $as_echo "#define HAVE_GETCWD 1" >>confdefs.h - -else - case " $LIBOBJS " in - *" getcwd.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS getcwd.$ac_objext" - ;; -esac - -fi - -ac_fn_c_check_func "$LINENO" "strdup" "ac_cv_func_strdup" -if test "x$ac_cv_func_strdup" = x""yes; then : - $as_echo "#define HAVE_STRDUP 1" >>confdefs.h +for ac_func in dup2 getcwd strdup +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF else case " $LIBOBJS " in - *" strdup.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS strdup.$ac_objext" + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; esac fi +done for ac_func in getpgrp @@ -11516,7 +11509,7 @@ then LIBM=$withval { $as_echo "$as_me:${as_lineno-$LINENO}: result: set LIBM=\"$withval\"" >&5 $as_echo "set LIBM=\"$withval\"" >&6; } -else as_fn_error $? "proper usage is --with-libm=STRING" "$LINENO" 5 +else as_fn_error "proper usage is --with-libm=STRING" "$LINENO" 5 fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: default LIBM=\"$LIBM\"" >&5 @@ -11540,7 +11533,7 @@ then LIBC=$withval { $as_echo "$as_me:${as_lineno-$LINENO}: result: set LIBC=\"$withval\"" >&5 $as_echo "set LIBC=\"$withval\"" >&6; } -else as_fn_error $? "proper usage is --with-libc=STRING" "$LINENO" 5 +else as_fn_error "proper usage is --with-libc=STRING" "$LINENO" 5 fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: default LIBC=\"$LIBC\"" >&5 @@ -11836,7 +11829,8 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -11848,7 +11842,8 @@ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -12016,7 +12011,7 @@ 15|30) ;; *) - as_fn_error $? "bad value $enable_big_digits for --enable-big-digits; value should be 15 or 30" "$LINENO" 5 ;; + as_fn_error "bad value $enable_big_digits for --enable-big-digits; value should be 15 or 30" "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_big_digits" >&5 $as_echo "$enable_big_digits" >&6; } @@ -12067,8 +12062,9 @@ if test "$ac_cv_type_wchar_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (wchar_t) -See \`config.log' for more details" "$LINENO" 5 ; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (wchar_t) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_wchar_t=0 fi @@ -12437,8 +12433,8 @@ ;; #( *) - as_fn_error $? "unknown endianness - presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + as_fn_error "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac @@ -12454,8 +12450,8 @@ # * --with-wide-unicode (adds a 'u') # # Thus for example, Python 3.2 built with wide unicode, pydebug, and pymalloc, -# would get a shared library ABI version tag of 'cpython-32udm' and shared -# libraries would be named 'foo.cpython-32udm.so'. +# would get a shared library ABI version tag of 'cpython-32dmu' and shared +# libraries would be named 'foo.cpython-32dmu.so'. { $as_echo "$as_me:${as_lineno-$LINENO}: checking SOABI" >&5 $as_echo_n "checking SOABI... " >&6; } @@ -12477,19 +12473,19 @@ esac ;; CYGWIN*) SO=.dll;; - Linux*) SO=.${SOABI}.so;; + Linux*) SO=.${SOABI}.so;; *) SO=.so;; esac else # this might also be a termcap variable, see #610332 - echo - echo '=====================================================================' - echo '+ +' + echo + echo '=====================================================================' + echo '+ +' echo '+ WARNING: You have set SO in your environment. +' - echo '+ Do you really mean to change the extension for shared libraries? +' - echo '+ Continuing in 10 seconds to let you to ponder. +' - echo '+ +' - echo '=====================================================================' + echo '+ Do you really mean to change the extension for shared libraries? +' + echo '+ Continuing in 10 seconds to let you to ponder. +' + echo '+ +' + echo '=====================================================================' sleep 10 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SO" >&5 @@ -12688,7 +12684,7 @@ have_readline=no fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext if test $have_readline = yes then cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -12862,7 +12858,7 @@ have_readline=no fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext if test $have_readline = yes then cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -13672,7 +13668,7 @@ case $ac_sys_system in - OSF*) as_fn_error $? "OSF* systems are deprecated unless somebody volunteers. Check http://bugs.python.org/issue8606" "$LINENO" 5 ;; + OSF*) as_fn_error "OSF* systems are deprecated unless somebody volunteers. Check http://bugs.python.org/issue8606" "$LINENO" 5 ;; esac @@ -13782,7 +13778,6 @@ ac_libobjs= ac_ltlibobjs= -U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' @@ -13945,19 +13940,19 @@ (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. +# script with status $?, using 1 if that was 0. as_fn_error () { - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 fi - $as_echo "$as_me: error: $2" >&2 + $as_echo "$as_me: error: $1" >&2 as_fn_exit $as_status } # as_fn_error @@ -14153,7 +14148,7 @@ test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" } # as_fn_mkdir_p @@ -14207,7 +14202,7 @@ # values after options handling. ac_log=" This file was extended by python $as_me 3.2, which was -generated by GNU Autoconf 2.67. Invocation command line was +generated by GNU Autoconf 2.65. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -14269,10 +14264,10 @@ ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ python config.status 3.2 -configured by $0, generated by GNU Autoconf 2.67, +configured by $0, generated by GNU Autoconf 2.65, with options \\"\$ac_cs_config\\" -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2009 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -14288,16 +14283,11 @@ while test $# != 0 do case $1 in - --*=?*) + --*=*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; *) ac_option=$1 ac_optarg=$2 @@ -14319,7 +14309,6 @@ $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; @@ -14332,7 +14321,7 @@ ac_need_defaults=false;; --he | --h) # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' + as_fn_error "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; @@ -14341,7 +14330,7 @@ ac_cs_silent=: ;; # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' + -*) as_fn_error "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" @@ -14399,7 +14388,7 @@ "Modules/Setup.config") CONFIG_FILES="$CONFIG_FILES Modules/Setup.config" ;; "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5 ;; + *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done @@ -14436,7 +14425,7 @@ { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5 # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. @@ -14453,7 +14442,7 @@ fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' + ac_cs_awk_cr='\r' else ac_cs_awk_cr=$ac_cr fi @@ -14467,18 +14456,18 @@ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi @@ -14567,28 +14556,20 @@ else cat fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 + || as_fn_error "could not setup config files machinery" "$LINENO" 5 _ACEOF -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// s/^[^=]*=[ ]*$// }' fi @@ -14616,7 +14597,7 @@ if test -z "$ac_t"; then break elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi @@ -14701,7 +14682,7 @@ _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 + as_fn_error "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" @@ -14714,7 +14695,7 @@ esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5 ;; + :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac @@ -14742,7 +14723,7 @@ [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5 ;; + as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" @@ -14769,7 +14750,7 @@ case $ac_tag in *:-:* | *:-) cat >"$tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac @@ -14900,22 +14881,22 @@ $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + || as_fn_error "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 +which seems to be undefined. Please make sure it is defined." >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} +which seems to be undefined. Please make sure it is defined." >&2;} rm -f "$tmp/stdin" case $ac_file in -) cat "$tmp/out" && rm -f "$tmp/out";; *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; :H) # @@ -14926,19 +14907,19 @@ $as_echo "/* $configure_input */" \ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" } >"$tmp/config.h" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + || as_fn_error "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$tmp/config.h" "$ac_file" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + || as_fn_error "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error $? "could not create -" "$LINENO" 5 + || as_fn_error "could not create -" "$LINENO" 5 fi ;; @@ -14953,7 +14934,7 @@ ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. @@ -14974,7 +14955,7 @@ exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 + $ac_cs_success || as_fn_exit $? fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 Modified: python/branches/py3k/configure.in ============================================================================== --- python/branches/py3k/configure.in (original) +++ python/branches/py3k/configure.in Sun Sep 5 02:09:07 2010 @@ -663,7 +663,7 @@ AC_C_INLINE if test "$ac_cv_c_inline" != no ; then - AC_DEFINE(USE_INLINE,1) + AC_DEFINE(USE_INLINE, 1, [Define to use the C99 inline keyword.]) AC_SUBST(USE_INLINE) fi Modified: python/branches/py3k/pyconfig.h.in ============================================================================== --- python/branches/py3k/pyconfig.h.in (original) +++ python/branches/py3k/pyconfig.h.in Sun Sep 5 02:09:07 2010 @@ -1040,11 +1040,10 @@ /* Define to 1 if your declares `struct tm'. */ #undef TM_IN_SYS_TIME -/* Define to 0 if you don't want to use computed gotos in ceval.c. - (The default is to use them if HAVE_COMPUTED_GOTOS is defined.) */ +/* Define if you want to use computed gotos in ceval.c. */ #undef USE_COMPUTED_GOTOS -/* Define if the compiler supports the inline keyword */ +/* Define to use the C99 inline keyword. */ #undef USE_INLINE /* Enable extensions on AIX 3, Interix. */ @@ -1200,6 +1199,12 @@ /* Define to `int' if doesn't define. */ #undef gid_t +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + /* Define to the type of a signed integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef int32_t From python-checkins at python.org Sun Sep 5 02:27:25 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 02:27:25 +0200 (CEST) Subject: [Python-checkins] r84513 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100905002725.6F458FBF2@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 02:27:25 2010 New Revision: 84513 Log: Update whatsnew for Pep3149. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 02:27:25 2010 @@ -105,12 +105,35 @@ :pep:`3147` - PYC Repository Directories PEP written by Barry Warsaw. -PEPs -==== +PEP 3149 ABI Version Tagged .so Files +===================================== -Implemented PEPs: +The PYC repository directory allows multiple bytecode cache files to be +co-located. This PEP implements a similar mechanism for shared object files by +giving them a common directory and distinct names for each version. + +The common directory is "pyshared" and the file names are made distinct by +identifying the Python implementation (such as CPython, PyPy, Jython, etc.), the +major and minor version numbers, and optional build flags (such as "d" for +debug, "m" for pymalloc, "u" for wide-unicode). For an arbtrary package, "foo", +you may see these files when the distribution package is installed:: + + /usr/share/pyshared/foo.cpython-32m.so + /usr/share/pyshared/foo.cpython-33md.so + +In Python itself, the tags are accessible from functions in the :mod:`sysconfig` +module:: + + >>> import sysconfig + >>> sysconfig.get_config_var('SOABI') # find the version tag + 'cpython-32mu' + >>> sysconfig.get_config_var('SO') # find the full filename extension + 'cpython-32mu.so' -* :pep:`3149` +.. seealso:: + + :pep:`3149` - ABI Version Tagged .so Files + PEP written by Barry Warsaw. Other Language Changes From python-checkins at python.org Sun Sep 5 02:36:26 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 02:36:26 +0200 (CEST) Subject: [Python-checkins] r84514 - python/branches/py3k/Misc/NEWS Message-ID: <20100905003626.EBC2AFCB4@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 02:36:26 2010 New Revision: 84514 Log: Typo Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 5 02:36:26 2010 @@ -1,4 +1,4 @@ -+++++++++++ +?+++++++++++ Python News +++++++++++ @@ -33,7 +33,7 @@ properly. Patch by Stefan Behnel. - Issue #5553: The Py_LOCAL_INLINE macro now results in inlining on - most platforms. Previously, it online inlined when using Microsoft + most platforms. Previously, it inlined when using Microsoft Visual C. - Issue #9712: Fix tokenize on identifiers that start with non-ascii names. From python-checkins at python.org Sun Sep 5 03:00:19 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 03:00:19 +0200 (CEST) Subject: [Python-checkins] r84515 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100905010019.E7ED3EE9F0@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 03:00:19 2010 New Revision: 84515 Log: More updates to whatsnew Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 03:00:19 2010 @@ -141,7 +141,22 @@ Some smaller changes made to the core Python language are: -* Stub +* The :func:`hasattr` function used to catch and suppress any Exception. + Now, it only catches :exc:`AttributeError`. Under the hood, :func:`hasattr` + works by calling :func:`getattr` and throwing away the results. This is + necessary because dynamic attribute creation is possible using + :meth:`__getattribute__` or :meth:`__getattr`. If :func:`hasattr` were to + just scan instance and class dictionaries it would miss the dynmaic methods + and make it difficult to implement proxy objects. + + (Discovered by Yury Selivanov and fixed by Benjamin Peterson; :issue:`9666`.) + +* The :func:`str` of a float or complex number is now the same as it + :func:`repr`. Previously, the :func:`str` form was shorter but that just + caused confusion and is no longer needed now that we the shortest possible + :func:`repr` is displayed by default. + + (Proposed and implemented by Mark Dickinson; :issue:`9337`). New, Improved, and Deprecated Modules @@ -342,8 +357,19 @@ Changes to Python's build process and to the C API include: -* Stub +* The C functions that access the Unicode Database now accept and + return characters from the full Unicode range, even on narrow unicode builds + (Py_UNICODE_TOLOWER, Py_UNICODE_ISDECIMAL, and others). A visible difference + in Python is that :cfunc:`unicodedata.numeric` now returns the correct value for + large code points, and :func:`repr` may consider more characters as printable. + + (Reported by Bupjoe Lee and fixed by Amaury Forgeot D'Arc; :issue:`5127`.) + +* Computed gotos are now enabled by default on supported + compilers (which are detected by the configure script). They can still + be disable selectively by specifying ``--without-computed-gotos``. + (:issue:`9203`) Porting to Python 3.2 ===================== From solipsis at pitrou.net Sun Sep 5 05:01:30 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 05 Sep 2010 05:01:30 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84515): sum=0 Message-ID: py3k results for svn r84515 (hg cset 4f9044e31b9e) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogYZi0P_', '-x'] From python-checkins at python.org Sun Sep 5 07:26:10 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 07:26:10 +0200 (CEST) Subject: [Python-checkins] r84516 - python/branches/py3k/Modules/_heapqmodule.c Message-ID: <20100905052610.D4808F6E5@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 07:26:10 2010 New Revision: 84516 Log: Inline cmp_lt(). Modified: python/branches/py3k/Modules/_heapqmodule.c Modified: python/branches/py3k/Modules/_heapqmodule.c ============================================================================== --- python/branches/py3k/Modules/_heapqmodule.c (original) +++ python/branches/py3k/Modules/_heapqmodule.c Sun Sep 5 07:26:10 2010 @@ -9,12 +9,6 @@ #include "Python.h" static int -cmp_lt(PyObject *x, PyObject *y) -{ - return PyObject_RichCompareBool(x, y, Py_LT); -} - -static int _siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) { PyObject *newitem, *parent; @@ -34,7 +28,7 @@ while (pos > startpos){ parentpos = (pos - 1) >> 1; parent = PyList_GET_ITEM(heap, parentpos); - cmp = cmp_lt(newitem, parent); + cmp = PyObject_RichCompareBool(newitem, parent, Py_LT); if (cmp == -1) { Py_DECREF(newitem); return -1; @@ -74,9 +68,10 @@ /* Set childpos to index of smaller child. */ rightpos = childpos + 1; if (rightpos < endpos) { - cmp = cmp_lt( + cmp = PyObject_RichCompareBool( PyList_GET_ITEM(heap, childpos), - PyList_GET_ITEM(heap, rightpos)); + PyList_GET_ITEM(heap, rightpos), + Py_LT); if (cmp == -1) { Py_DECREF(newitem); return -1; @@ -219,7 +214,7 @@ return item; } - cmp = cmp_lt(PyList_GET_ITEM(heap, 0), item); + cmp = PyObject_RichCompareBool(PyList_GET_ITEM(heap, 0), item, Py_LT); if (cmp == -1) return NULL; if (cmp == 0) { @@ -318,7 +313,7 @@ else goto sortit; } - cmp = cmp_lt(sol, elem); + cmp = PyObject_RichCompareBool(sol, elem, Py_LT); if (cmp == -1) { Py_DECREF(elem); goto fail; @@ -373,7 +368,7 @@ while (pos > startpos){ parentpos = (pos - 1) >> 1; parent = PyList_GET_ITEM(heap, parentpos); - cmp = cmp_lt(parent, newitem); + cmp = PyObject_RichCompareBool(parent, newitem, Py_LT); if (cmp == -1) { Py_DECREF(newitem); return -1; @@ -413,9 +408,10 @@ /* Set childpos to index of smaller child. */ rightpos = childpos + 1; if (rightpos < endpos) { - cmp = cmp_lt( + cmp = PyObject_RichCompareBool( PyList_GET_ITEM(heap, rightpos), - PyList_GET_ITEM(heap, childpos)); + PyList_GET_ITEM(heap, childpos), + Py_LT); if (cmp == -1) { Py_DECREF(newitem); return -1; @@ -488,7 +484,7 @@ else goto sortit; } - cmp = cmp_lt(elem, los); + cmp = PyObject_RichCompareBool(elem, los, Py_LT); if (cmp == -1) { Py_DECREF(elem); goto fail; From python-checkins at python.org Sun Sep 5 07:56:44 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 07:56:44 +0200 (CEST) Subject: [Python-checkins] r84517 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100905055644.9FED0F7BF@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 07:56:44 2010 New Revision: 84517 Log: Add an example Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 07:56:44 2010 @@ -154,7 +154,12 @@ * The :func:`str` of a float or complex number is now the same as it :func:`repr`. Previously, the :func:`str` form was shorter but that just caused confusion and is no longer needed now that we the shortest possible - :func:`repr` is displayed by default. + :func:`repr` is displayed by default:: + + >>> repr(math.pi) + '3.141592653589793' + >>> str(math.pi) + '3.141592653589793' (Proposed and implemented by Mark Dickinson; :issue:`9337`). From python-checkins at python.org Sun Sep 5 07:57:35 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 07:57:35 +0200 (CEST) Subject: [Python-checkins] r84518 - in python/branches/py3k: Lib/functools.py Misc/NEWS Message-ID: <20100905055735.DFC28F7BF@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 07:57:35 2010 New Revision: 84518 Log: Clean-up functools.total_ordering(). Modified: python/branches/py3k/Lib/functools.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Sun Sep 5 07:57:35 2010 @@ -65,7 +65,6 @@ return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) -_object_defaults = {object.__lt__, object.__le__, object.__gt__, object.__ge__} def total_ordering(cls): """Class decorator that fills in missing ordering methods""" convert = { @@ -82,9 +81,8 @@ ('__gt__', lambda self, other: not other >= self), ('__lt__', lambda self, other: not self >= other)] } - roots = set(dir(cls)) & set(convert) - # Remove default comparison operations defined on object. - roots -= {meth for meth in roots if getattr(cls, meth) in _object_defaults} + # Find comparisons not inherited from object. + roots = [op for op in convert if getattr(cls, op) is not getattr(object, op)] if not roots: raise ValueError('must define at least one ordering operation: < > <= >=') root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 5 07:57:35 2010 @@ -222,7 +222,7 @@ - Issue #9501: Fixed logging regressions in cleanup code. -- Fix functools.total_ordering() to actually work. +- Fix functools.total_ordering() to skip methods inherited from object(). - Issue #9572: Importlib should not raise an exception if a directory it thought it needed to create was done concurrently by another process. From python-checkins at python.org Sun Sep 5 08:13:48 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 08:13:48 +0200 (CEST) Subject: [Python-checkins] r84519 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100905061348.1EDECFB89@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 08:13:47 2010 New Revision: 84519 Log: More updates to whatsnew. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 08:13:47 2010 @@ -163,6 +163,19 @@ (Proposed and implemented by Mark Dickinson; :issue:`9337`). +* The :func:`functools.wraps` decorator now adds a :attr:`__wrapped__` attribute + pointing to the original callable function. This allows wrapped functions to + be introspected. It also copies :attr:`__annotations__` if defined. And now + it also gracefully skips over missing attributes such as :attr:`__doc__` which + might not be defined for the wrapped callable. + + (By Nick Coghlan and Terrence Cole; :issue:`9567`, :issue:`3445`, and + :issue:`8814`.) + +* The :mod:`abc` module now supports :func:`abstractclassmethod` and + :func:`staticmethod`. + + (:issue:`5867`) New, Improved, and Deprecated Modules ===================================== @@ -286,11 +299,6 @@ (Contributed by Georg Brandl; :issue:`5675`.) -* Parameters passed to :func:`socket.getaddrinfo()` function can now be - specified as single keyword arguments. - - (Contributed by Giampaolo Rodol?; :issue:`8866`.) - * :class:`~poplib.POP3_SSL` class now accepts a *context* parameter, which is a :class:`ssl.SSLContext` object allowing bundling SSL configuration options, certificates and private keys into a single (potentially long-lived) From python-checkins at python.org Sun Sep 5 10:28:41 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 10:28:41 +0200 (CEST) Subject: [Python-checkins] r84520 - python/branches/py3k/Lib/pydoc_data/topics.py Message-ID: <20100905082841.7FB94FB89@mail.python.org> Author: georg.brandl Date: Sun Sep 5 10:28:41 2010 New Revision: 84520 Log: Update pydoc topics. Modified: python/branches/py3k/Lib/pydoc_data/topics.py Modified: python/branches/py3k/Lib/pydoc_data/topics.py ============================================================================== --- python/branches/py3k/Lib/pydoc_data/topics.py (original) +++ python/branches/py3k/Lib/pydoc_data/topics.py Sun Sep 5 10:28:41 2010 @@ -1,4 +1,4 @@ -# Autogenerated by Sphinx on Sat Jul 31 10:22:58 2010 +# Autogenerated by Sphinx on Sun Sep 5 10:24:54 2010 topics = {'assert': '\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': '\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 | "*" target\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, optionally enclosed in\nparentheses or square brackets, is recursively defined as follows.\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\n * If the target list contains one target prefixed with an asterisk,\n called a "starred" target: The object must be a sequence with at\n least as many items as there are targets in the target list, minus\n one. The first items of the sequence are assigned, from left to\n right, to the targets before the starred target. The final items\n of the sequence are assigned to the targets after the starred\n target. A list of the remaining items in the sequence is then\n assigned to the starred target (the list can be empty).\n\n * Else: The object must be a sequence with the same number of items\n as there are targets in the target list, and the items are\n assigned, from left to right, to the corresponding targets.\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`` or ``nonlocal``\n statement in the current code block: the name is bound to the\n object in the current local namespace.\n\n * Otherwise: the name is bound to the object in the global namespace\n or the outer namespace determined by ``nonlocal``, respectively.\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 an integer. If it is negative, the sequence\'s\n length is added to it. The resulting value must be a nonnegative\n integer less than the sequence\'s length, and the sequence is asked\n to assign the assigned object to its item with that index. If the\n index is out of range, ``IndexError`` is raised (assignment to a\n subscripted sequence cannot add new items to a 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 For user-defined objects, the ``__setitem__()`` method is called\n with appropriate arguments.\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 integers. If either bound is\n negative, the sequence\'s length is added to it. The resulting\n bounds are clipped to lie between zero and the sequence\'s length,\n inclusive. Finally, the sequence object is asked to replace the\n slice with the items of the assigned sequence. The length of the\n 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\nSee also:\n\n **PEP 3132** - Extended Iterable Unpacking\n The specification for the ``*target`` feature.\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': '\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', @@ -16,9 +16,9 @@ 'break': '\nThe ``break`` statement\n***********************\n\n break_stmt ::= "break"\n\n``break`` may only occur syntactically nested in a ``for`` or\n``while`` loop, but not nested in a function or class definition\nwithin that loop.\n\nIt terminates the nearest enclosing loop, skipping the optional\n``else`` clause if the loop has one.\n\nIf a ``for`` loop is terminated by ``break``, the loop control target\nkeeps its current value.\n\nWhen ``break`` passes control out of a ``try`` statement with a\n``finally`` clause, that ``finally`` clause is executed before really\nleaving the loop.\n', 'callable-types': '\nEmulating callable objects\n**************************\n\nobject.__call__(self[, args...])\n\n Called when the instance is "called" as a function; if this method\n is defined, ``x(arg1, arg2, ...)`` is a shorthand for\n ``x.__call__(arg1, arg2, ...)``.\n', 'calls': '\nCalls\n*****\n\nA call calls a callable object (e.g., a function) with a possibly\nempty series of arguments:\n\n call ::= primary "(" [argument_list [","] | comprehension] ")"\n argument_list ::= positional_arguments ["," keyword_arguments]\n ["," "*" expression] ["," keyword_arguments]\n ["," "**" expression]\n | keyword_arguments ["," "*" expression]\n ["," keyword_arguments] ["," "**" expression]\n | "*" expression ["," keyword_arguments] ["," "**" expression]\n | "**" expression\n positional_arguments ::= expression ("," expression)*\n keyword_arguments ::= keyword_item ("," keyword_item)*\n keyword_item ::= identifier "=" expression\n\nA trailing comma may be present after the positional and keyword\narguments but does not affect the semantics.\n\nThe primary must evaluate to a callable object (user-defined\nfunctions, built-in functions, methods of built-in objects, class\nobjects, methods of class instances, and all objects having a\n``__call__()`` method are callable). All argument expressions are\nevaluated before the call is attempted. Please refer to section\n*Function definitions* for the syntax of formal parameter lists.\n\nIf keyword arguments are present, they are first converted to\npositional arguments, as follows. First, a list of unfilled slots is\ncreated for the formal parameters. If there are N positional\narguments, they are placed in the first N slots. Next, for each\nkeyword argument, the identifier is used to determine the\ncorresponding slot (if the identifier is the same as the first formal\nparameter name, the first slot is used, and so on). If the slot is\nalready filled, a ``TypeError`` exception is raised. Otherwise, the\nvalue of the argument is placed in the slot, filling it (even if the\nexpression is ``None``, it fills the slot). When all arguments have\nbeen processed, the slots that are still unfilled are filled with the\ncorresponding default value from the function definition. (Default\nvalues are calculated, once, when the function is defined; thus, a\nmutable object such as a list or dictionary used as default value will\nbe shared by all calls that don\'t specify an argument value for the\ncorresponding slot; this should usually be avoided.) If there are any\nunfilled slots for which no default value is specified, a\n``TypeError`` exception is raised. Otherwise, the list of filled\nslots is used as the argument list for the call.\n\n**CPython implementation detail:** An implementation may provide\nbuilt-in functions whose positional parameters do not have names, even\nif they are \'named\' for the purpose of documentation, and which\ntherefore cannot be supplied by keyword. In CPython, this is the case\nfor functions implemented in C that use ``PyArg_ParseTuple()`` to\nparse their arguments.\n\nIf there are more positional arguments than there are formal parameter\nslots, a ``TypeError`` exception is raised, unless a formal parameter\nusing the syntax ``*identifier`` is present; in this case, that formal\nparameter receives a tuple containing the excess positional arguments\n(or an empty tuple if there were no excess positional arguments).\n\nIf any keyword argument does not correspond to a formal parameter\nname, a ``TypeError`` exception is raised, unless a formal parameter\nusing the syntax ``**identifier`` is present; in this case, that\nformal parameter receives a dictionary containing the excess keyword\narguments (using the keywords as keys and the argument values as\ncorresponding values), or a (new) empty dictionary if there were no\nexcess keyword arguments.\n\nIf the syntax ``*expression`` appears in the function call,\n``expression`` must evaluate to a sequence. Elements from this\nsequence are treated as if they were additional positional arguments;\nif there are positional arguments *x1*,..., *xN*, and ``expression``\nevaluates to a sequence *y1*, ..., *yM*, this is equivalent to a call\nwith M+N positional arguments *x1*, ..., *xN*, *y1*, ..., *yM*.\n\nA consequence of this is that although the ``*expression`` syntax may\nappear *after* some keyword arguments, it is processed *before* the\nkeyword arguments (and the ``**expression`` argument, if any -- see\nbelow). So:\n\n >>> def f(a, b):\n ... print(a, b)\n ...\n >>> f(b=1, *(2,))\n 2 1\n >>> f(a=1, *(2,))\n Traceback (most recent call last):\n File "", line 1, in ?\n TypeError: f() got multiple values for keyword argument \'a\'\n >>> f(1, *(2,))\n 1 2\n\nIt is unusual for both keyword arguments and the ``*expression``\nsyntax to be used in the same call, so in practice this confusion does\nnot arise.\n\nIf the syntax ``**expression`` appears in the function call,\n``expression`` must evaluate to a mapping, the contents of which are\ntreated as additional keyword arguments. In the case of a keyword\nappearing in both ``expression`` and as an explicit keyword argument,\na ``TypeError`` exception is raised.\n\nFormal parameters using the syntax ``*identifier`` or ``**identifier``\ncannot be used as positional argument slots or as keyword argument\nnames.\n\nA call always returns some value, possibly ``None``, unless it raises\nan exception. How this value is computed depends on the type of the\ncallable object.\n\nIf it is---\n\na user-defined function:\n The code block for the function is executed, passing it the\n argument list. The first thing the code block will do is bind the\n formal parameters to the arguments; this is described in section\n *Function definitions*. When the code block executes a ``return``\n statement, this specifies the return value of the function call.\n\na built-in function or method:\n The result is up to the interpreter; see *Built-in Functions* for\n the descriptions of built-in functions and methods.\n\na class object:\n A new instance of that class is returned.\n\na class instance method:\n The corresponding user-defined function is called, with an argument\n list that is one longer than the argument list of the call: the\n instance becomes the first argument.\n\na class instance:\n The class must define a ``__call__()`` method; the effect is then\n the same as if that method was called.\n', - 'class': '\nClass definitions\n*****************\n\nA class definition defines a class object (see section *The standard\ntype hierarchy*):\n\n classdef ::= [decorators] "class" classname [inheritance] ":" suite\n inheritance ::= "(" [expression_list] ")"\n classname ::= identifier\n\nA class definition is an executable statement. It first evaluates the\ninheritance list, if present. Each item in the inheritance list\nshould evaluate to a class object or class type which allows\nsubclassing. The class\'s suite is then executed in a new execution\nframe (see section *Naming and binding*), using a newly created local\nnamespace and the original global namespace. (Usually, the suite\ncontains only function definitions.) When the class\'s suite finishes\nexecution, its execution frame is discarded but its local namespace is\nsaved. [4] A class object is then created using the inheritance list\nfor the base classes and the saved local namespace for the attribute\ndictionary. The class name is bound to this class object in the\noriginal local namespace.\n\nClasses can also be decorated; as with functions,\n\n @f1(arg)\n @f2\n class Foo: pass\n\nis equivalent to\n\n class Foo: pass\n Foo = f1(arg)(f2(Foo))\n\n**Programmer\'s note:** Variables defined in the class definition are\nclass variables; they are shared by instances. Instance variables can\nbe set in a method with ``self.name = value``. Both class and\ninstance variables are accessible through the notation\n"``self.name``", and an instance variable hides a class variable with\nthe same name when accessed in this way. Class variables can be used\nas defaults for instance variables, but using mutable values there can\nlead to unexpected results. Descriptors can be used to create\ninstance variables with different implementation details.\n\nSee also:\n\n **PEP 3129** - Class Decorators\n\nClass definitions, like function definitions, may be wrapped by one or\nmore *decorator* expressions. The evaluation rules for the decorator\nexpressions are the same as for functions. The result must be a class\nobject, which is then bound to the class name.\n\n-[ Footnotes ]-\n\n[1] The exception is propagated to the invocation stack only if there\n is no ``finally`` clause that negates the exception.\n\n[2] Currently, control "flows off the end" except in the case of an\n exception or the execution of a ``return``, ``continue``, or\n ``break`` statement.\n\n[3] A string literal appearing as the first statement in the function\n body is transformed into the function\'s ``__doc__`` attribute and\n therefore the function\'s *docstring*.\n\n[4] A string literal appearing as the first statement in the class\n body is transformed into the namespace\'s ``__doc__`` item and\n therefore the class\'s *docstring*.\n', + 'class': '\nClass definitions\n*****************\n\nA class definition defines a class object (see section *The standard\ntype hierarchy*):\n\n classdef ::= [decorators] "class" classname [inheritance] ":" suite\n inheritance ::= "(" [argument_list [","] ] ")"\n classname ::= identifier\n\nA class definition is an executable statement. The inheritance list\nusually gives a list of base classes (see *Customizing class creation*\nfor more advanced uses), so each item in the list should evaluate to a\nclass object which allows subclassing.\n\nThe class\'s suite is then executed in a new execution frame (see\n*Naming and binding*), using a newly created local namespace and the\noriginal global namespace. (Usually, the suite contains mostly\nfunction definitions.) When the class\'s suite finishes execution, its\nexecution frame is discarded but its local namespace is saved. [4] A\nclass object is then created using the inheritance list for the base\nclasses and the saved local namespace for the attribute dictionary.\nThe class name is bound to this class object in the original local\nnamespace.\n\nClass creation can be customized heavily using *metaclasses*.\n\nClasses can also be decorated; as with functions,\n\n @f1(arg)\n @f2\n class Foo: pass\n\nis equivalent to\n\n class Foo: pass\n Foo = f1(arg)(f2(Foo))\n\n**Programmer\'s note:** Variables defined in the class definition are\nclass attributes; they are shared by instances. Instance attributes\ncan be set in a method with ``self.name = value``. Both class and\ninstance attributes are accessible through the notation\n"``self.name``", and an instance attribute hides a class attribute\nwith the same name when accessed in this way. Class attributes can be\nused as defaults for instance attributes, but using mutable values\nthere can lead to unexpected results. *Descriptors* can be used to\ncreate instance variables with different implementation details.\n\nSee also:\n\n **PEP 3116** - Metaclasses in Python 3 **PEP 3129** - Class\n Decorators\n\n-[ Footnotes ]-\n\n[1] The exception is propagated to the invocation stack only if there\n is no ``finally`` clause that negates the exception.\n\n[2] Currently, control "flows off the end" except in the case of an\n exception or the execution of a ``return``, ``continue``, or\n ``break`` statement.\n\n[3] A string literal appearing as the first statement in the function\n body is transformed into the function\'s ``__doc__`` attribute and\n therefore the function\'s *docstring*.\n\n[4] A string literal appearing as the first statement in the class\n body is transformed into the namespace\'s ``__doc__`` item and\n therefore the class\'s *docstring*.\n', 'comparisons': '\nComparisons\n***********\n\nUnlike C, all comparison operations in Python have the same priority,\nwhich is lower than that of any arithmetic, shifting or bitwise\noperation. Also unlike C, expressions like ``a < b < c`` have the\ninterpretation that is conventional in mathematics:\n\n comparison ::= or_expr ( comp_operator or_expr )*\n comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="\n | "is" ["not"] | ["not"] "in"\n\nComparisons yield boolean values: ``True`` or ``False``.\n\nComparisons can be chained arbitrarily, e.g., ``x < y <= z`` is\nequivalent to ``x < y and y <= z``, except that ``y`` is evaluated\nonly once (but in both cases ``z`` is not evaluated at all when ``x <\ny`` is found to be false).\n\nFormally, if *a*, *b*, *c*, ..., *y*, *z* are expressions and *op1*,\n*op2*, ..., *opN* are comparison operators, then ``a op1 b op2 c ... y\nopN z`` is equivalent to ``a op1 b and b op2 c and ... y opN z``,\nexcept that each expression is evaluated at most once.\n\nNote that ``a op1 b op2 c`` doesn\'t imply any kind of comparison\nbetween *a* and *c*, so that, e.g., ``x < y > z`` is perfectly legal\n(though perhaps not pretty).\n\nThe operators ``<``, ``>``, ``==``, ``>=``, ``<=``, and ``!=`` compare\nthe values of two objects. The objects need not have the same type.\nIf both are numbers, they are converted to a common type. Otherwise,\nthe ``==`` and ``!=`` operators *always* consider objects of different\ntypes to be unequal, while the ``<``, ``>``, ``>=`` and ``<=``\noperators raise a ``TypeError`` when comparing objects of different\ntypes that do not implement these operators for the given pair of\ntypes. You can control comparison behavior of objects of non-built-in\ntypes by defining rich comparison methods like ``__gt__()``, described\nin section *Basic customization*.\n\nComparison of objects of the same type depends on the type:\n\n* Numbers are compared arithmetically.\n\n* The values ``float(\'NaN\')`` and ``Decimal(\'NaN\')`` are special. The\n are identical to themselves, ``x is x`` but are not equal to\n themselves, ``x != x``. Additionally, comparing any value to a\n not-a-number value will return ``False``. For example, both ``3 <\n float(\'NaN\')`` and ``float(\'NaN\') < 3`` will return ``False``.\n\n* Bytes objects are compared lexicographically using the numeric\n values of their elements.\n\n* Strings are compared lexicographically using the numeric equivalents\n (the result of the built-in function ``ord()``) of their characters.\n [3] String and bytes object can\'t be compared!\n\n* Tuples and lists are compared lexicographically using comparison of\n corresponding elements. This means that to compare equal, each\n element must compare equal and the two sequences must be of the same\n type and have the same length.\n\n If not equal, the sequences are ordered the same as their first\n differing elements. For example, ``[1,2,x] <= [1,2,y]`` has the\n same value as ``x <= y``. If the corresponding element does not\n exist, the shorter sequence is ordered first (for example, ``[1,2] <\n [1,2,3]``).\n\n* Mappings (dictionaries) compare equal if and only if they have the\n same ``(key, value)`` pairs. Order comparisons ``(\'<\', \'<=\', \'>=\',\n \'>\')`` raise ``TypeError``.\n\n* Sets and frozensets define comparison operators to mean subset and\n superset tests. Those relations do not define total orderings (the\n two sets ``{1,2}`` and {2,3} are not equal, nor subsets of one\n another, nor supersets of one another). Accordingly, sets are not\n appropriate arguments for functions which depend on total ordering.\n For example, ``min()``, ``max()``, and ``sorted()`` produce\n undefined results given a list of sets as inputs.\n\n* Most other objects of built-in types compare unequal unless they are\n the same object; the choice whether one object is considered smaller\n or larger than another one is made arbitrarily but consistently\n within one execution of a program.\n\nComparison of objects of the differing types depends on whether either\nof the types provide explicit support for the comparison. Most\nnumeric types can be compared with one another, but comparisons of\n``float`` and ``Decimal`` are not supported to avoid the inevitable\nconfusion arising from representation issues such as ``float(\'1.1\')``\nbeing inexactly represented and therefore not exactly equal to\n``Decimal(\'1.1\')`` which is. When cross-type comparison is not\nsupported, the comparison method returns ``NotImplemented``. This can\ncreate the illusion of non-transitivity between supported cross-type\ncomparisons and unsupported comparisons. For example, ``Decimal(2) ==\n2`` and *2 == float(2)`* but ``Decimal(2) != float(2)``.\n\nThe operators ``in`` and ``not in`` test for membership. ``x in s``\nevaluates to true if *x* is a member of *s*, and false otherwise. ``x\nnot in s`` returns the negation of ``x in s``. All built-in sequences\nand set types support this as well as dictionary, for which ``in``\ntests whether a the dictionary has a given key. For container types\nsuch as list, tuple, set, frozenset, dict, or collections.deque, the\nexpression ``x in y`` is equivalent to ``any(x is e or x == e for e in\ny)``.\n\nFor the string and bytes types, ``x in y`` is true if and only if *x*\nis a substring of *y*. An equivalent test is ``y.find(x) != -1``.\nEmpty strings are always considered to be a substring of any other\nstring, so ``"" in "abc"`` will return ``True``.\n\nFor user-defined classes which define the ``__contains__()`` method,\n``x in y`` is true if and only if ``y.__contains__(x)`` is true.\n\nFor user-defined classes which do not define ``__contains__()`` but do\ndefine ``__iter__()``, ``x in y`` is true if some value ``z`` with ``x\n== z`` is produced while iterating over ``y``. If an exception is\nraised during the iteration, it is as if ``in`` raised that exception.\n\nLastly, the old-style iteration protocol is tried: if a class defines\n``__getitem__()``, ``x in y`` is true if and only if there is a non-\nnegative integer index *i* such that ``x == y[i]``, and all lower\ninteger indices do not raise ``IndexError`` exception. (If any other\nexception is raised, it is as if ``in`` raised that exception).\n\nThe operator ``not in`` is defined to have the inverse true value of\n``in``.\n\nThe operators ``is`` and ``is not`` test for object identity: ``x is\ny`` is true if and only if *x* and *y* are the same object. ``x is\nnot y`` yields the inverse truth value. [4]\n', - 'compound': '\nCompound statements\n*******************\n\nCompound statements contain (groups of) other statements; they affect\nor control the execution of those other statements in some way. In\ngeneral, compound statements span multiple lines, although in simple\nincarnations a whole compound statement may be contained in one line.\n\nThe ``if``, ``while`` and ``for`` statements implement traditional\ncontrol flow constructs. ``try`` specifies exception handlers and/or\ncleanup code for a group of statements, while the ``with`` statement\nallows the execution of initialization and finalization code around a\nblock of code. Function and class definitions are also syntactically\ncompound statements.\n\nCompound statements consist of one or more \'clauses.\' A clause\nconsists of a header and a \'suite.\' The clause headers of a\nparticular compound statement are all at the same indentation level.\nEach clause header begins with a uniquely identifying keyword and ends\nwith a colon. A suite is a group of statements controlled by a\nclause. A suite can be one or more semicolon-separated simple\nstatements on the same line as the header, following the header\'s\ncolon, or it can be one or more indented statements on subsequent\nlines. Only the latter form of suite can contain nested compound\nstatements; the following is illegal, mostly because it wouldn\'t be\nclear to which ``if`` clause a following ``else`` clause would belong:\n\n if test1: if test2: print(x)\n\nAlso note that the semicolon binds tighter than the colon in this\ncontext, so that in the following example, either all or none of the\n``print()`` calls are executed:\n\n if x < y < z: print(x); print(y); print(z)\n\nSummarizing:\n\n compound_stmt ::= if_stmt\n | while_stmt\n | for_stmt\n | try_stmt\n | with_stmt\n | funcdef\n | classdef\n suite ::= stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT\n statement ::= stmt_list NEWLINE | compound_stmt\n stmt_list ::= simple_stmt (";" simple_stmt)* [";"]\n\nNote that statements always end in a ``NEWLINE`` possibly followed by\na ``DEDENT``. Also note that optional continuation clauses always\nbegin with a keyword that cannot start a statement, thus there are no\nambiguities (the \'dangling ``else``\' problem is solved in Python by\nrequiring nested ``if`` statements to be indented).\n\nThe formatting of the grammar rules in the following sections places\neach clause on a separate line for clarity.\n\n\nThe ``if`` statement\n====================\n\nThe ``if`` statement is used for conditional execution:\n\n if_stmt ::= "if" expression ":" suite\n ( "elif" expression ":" suite )*\n ["else" ":" suite]\n\nIt selects exactly one of the suites by evaluating the expressions one\nby one until one is found to be true (see section *Boolean operations*\nfor the definition of true and false); then that suite is executed\n(and no other part of the ``if`` statement is executed or evaluated).\nIf all expressions are false, the suite of the ``else`` clause, if\npresent, is executed.\n\n\nThe ``while`` statement\n=======================\n\nThe ``while`` statement is used for repeated execution as long as an\nexpression is true:\n\n while_stmt ::= "while" expression ":" suite\n ["else" ":" suite]\n\nThis repeatedly tests the expression and, if it is true, executes the\nfirst suite; if the expression is false (which may be the first time\nit is tested) the suite of the ``else`` clause, if present, is\nexecuted and the loop terminates.\n\nA ``break`` statement executed in the first suite terminates the loop\nwithout executing the ``else`` clause\'s suite. A ``continue``\nstatement executed in the first suite skips the rest of the suite and\ngoes back to testing the expression.\n\n\nThe ``for`` statement\n=====================\n\nThe ``for`` statement is used to iterate over the elements of a\nsequence (such as a string, tuple or list) or other iterable object:\n\n for_stmt ::= "for" target_list "in" expression_list ":" suite\n ["else" ":" suite]\n\nThe expression list is evaluated once; it should yield an iterable\nobject. An iterator is created for the result of the\n``expression_list``. The suite is then executed once for each item\nprovided by the iterator, in the order of ascending indices. Each\nitem in turn is assigned to the target list using the standard rules\nfor assignments (see *Assignment statements*), and then the suite is\nexecuted. When the items are exhausted (which is immediately when the\nsequence is empty or an iterator raises a ``StopIteration``\nexception), the suite in the ``else`` clause, if present, is executed,\nand the loop terminates.\n\nA ``break`` statement executed in the first suite terminates the loop\nwithout executing the ``else`` clause\'s suite. A ``continue``\nstatement executed in the first suite skips the rest of the suite and\ncontinues with the next item, or with the ``else`` clause if there was\nno next item.\n\nThe suite may assign to the variable(s) in the target list; this does\nnot affect the next item assigned to it.\n\nNames in the target list are not deleted when the loop is finished,\nbut if the sequence is empty, it will not have been assigned to at all\nby the loop. Hint: the built-in function ``range()`` returns an\niterator of integers suitable to emulate the effect of Pascal\'s ``for\ni := a to b do``; e.g., ``list(range(3))`` returns the list ``[0, 1,\n2]``.\n\nNote: There is a subtlety when the sequence is being modified by the loop\n (this can only occur for mutable sequences, i.e. lists). An\n internal counter is used to keep track of which item is used next,\n and this is incremented on each iteration. When this counter has\n reached the length of the sequence the loop terminates. This means\n that if the suite deletes the current (or a previous) item from the\n sequence, the next item will be skipped (since it gets the index of\n the current item which has already been treated). Likewise, if the\n suite inserts an item in the sequence before the current item, the\n current item will be treated again the next time through the loop.\n This can lead to nasty bugs that can be avoided by making a\n temporary copy using a slice of the whole sequence, e.g.,\n\n for x in a[:]:\n if x < 0: a.remove(x)\n\n\nThe ``try`` statement\n=====================\n\nThe ``try`` statement specifies exception handlers and/or cleanup code\nfor a group of statements:\n\n try_stmt ::= try1_stmt | try2_stmt\n try1_stmt ::= "try" ":" suite\n ("except" [expression ["as" target]] ":" suite)+\n ["else" ":" suite]\n ["finally" ":" suite]\n try2_stmt ::= "try" ":" suite\n "finally" ":" suite\n\nThe ``except`` clause(s) specify one or more exception handlers. When\nno exception occurs in the ``try`` clause, no exception handler is\nexecuted. When an exception occurs in the ``try`` suite, a search for\nan exception handler is started. This search inspects the except\nclauses in turn until one is found that matches the exception. An\nexpression-less except clause, if present, must be last; it matches\nany exception. For an except clause with an expression, that\nexpression is evaluated, and the clause matches the exception if the\nresulting object is "compatible" with the exception. An object is\ncompatible with an exception if it is the class or a base class of the\nexception object or a tuple containing an item compatible with the\nexception.\n\nIf no except clause matches the exception, the search for an exception\nhandler continues in the surrounding code and on the invocation stack.\n[1]\n\nIf the evaluation of an expression in the header of an except clause\nraises an exception, the original search for a handler is canceled and\na search starts for the new exception in the surrounding code and on\nthe call stack (it is treated as if the entire ``try`` statement\nraised the exception).\n\nWhen a matching except clause is found, the exception is assigned to\nthe target specified after the ``as`` keyword in that except clause,\nif present, and the except clause\'s suite is executed. All except\nclauses must have an executable block. When the end of this block is\nreached, execution continues normally after the entire try statement.\n(This means that if two nested handlers exist for the same exception,\nand the exception occurs in the try clause of the inner handler, the\nouter handler will not handle the exception.)\n\nWhen an exception has been assigned using ``as target``, it is cleared\nat the end of the except clause. This is as if\n\n except E as N:\n foo\n\nwas translated to\n\n except E as N:\n try:\n foo\n finally:\n del N\n\nThis means the exception must be assigned to a different name to be\nable to refer to it after the except clause. Exceptions are cleared\nbecause with the traceback attached to them, they form a reference\ncycle with the stack frame, keeping all locals in that frame alive\nuntil the next garbage collection occurs.\n\nBefore an except clause\'s suite is executed, details about the\nexception are stored in the ``sys`` module and can be access via\n``sys.exc_info()``. ``sys.exc_info()`` returns a 3-tuple consisting\nof: ``exc_type``, the exception class; ``exc_value``, the exception\ninstance; ``exc_traceback``, a traceback object (see section *The\nstandard type hierarchy*) identifying the point in the program where\nthe exception occurred. ``sys.exc_info()`` values are restored to\ntheir previous values (before the call) when returning from a function\nthat handled an exception.\n\nThe optional ``else`` clause is executed if and when control flows off\nthe end of the ``try`` clause. [2] Exceptions in the ``else`` clause\nare not handled by the preceding ``except`` clauses.\n\nIf ``finally`` is present, it specifies a \'cleanup\' handler. The\n``try`` clause is executed, including any ``except`` and ``else``\nclauses. If an exception occurs in any of the clauses and is not\nhandled, the exception is temporarily saved. The ``finally`` clause is\nexecuted. If there is a saved exception, it is re-raised at the end\nof the ``finally`` clause. If the ``finally`` clause raises another\nexception or executes a ``return`` or ``break`` statement, the saved\nexception is lost. The exception information is not available to the\nprogram during execution of the ``finally`` clause.\n\nWhen a ``return``, ``break`` or ``continue`` statement is executed in\nthe ``try`` suite of a ``try``...``finally`` statement, the\n``finally`` clause is also executed \'on the way out.\' A ``continue``\nstatement is illegal in the ``finally`` clause. (The reason is a\nproblem with the current implementation --- this restriction may be\nlifted in the future).\n\nAdditional information on exceptions can be found in section\n*Exceptions*, and information on using the ``raise`` statement to\ngenerate exceptions may be found in section *The raise statement*.\n\n\nThe ``with`` statement\n======================\n\nThe ``with`` statement is used to wrap the execution of a block with\nmethods defined by a context manager (see section *With Statement\nContext Managers*). This allows common\n``try``...``except``...``finally`` usage patterns to be encapsulated\nfor convenient reuse.\n\n with_stmt ::= "with" with_item ("," with_item)* ":" suite\n with_item ::= expression ["as" target]\n\nThe execution of the ``with`` statement with one "item" proceeds as\nfollows:\n\n1. The context expression is evaluated to obtain a context manager.\n\n2. The context manager\'s ``__exit__()`` is loaded for later use.\n\n3. The context manager\'s ``__enter__()`` method is invoked.\n\n4. If a target was included in the ``with`` statement, the return\n value from ``__enter__()`` is assigned to it.\n\n Note: The ``with`` statement guarantees that if the ``__enter__()``\n method returns without an error, then ``__exit__()`` will always\n be called. Thus, if an error occurs during the assignment to the\n target list, it will be treated the same as an error occurring\n within the suite would be. See step 6 below.\n\n5. The suite is executed.\n\n6. The context manager\'s ``__exit__()`` method is invoked. If an\n exception caused the suite to be exited, its type, value, and\n traceback are passed as arguments to ``__exit__()``. Otherwise,\n three ``None`` arguments are supplied.\n\n If the suite was exited due to an exception, and the return value\n from the ``__exit__()`` method was false, the exception is\n reraised. If the return value was true, the exception is\n suppressed, and execution continues with the statement following\n the ``with`` statement.\n\n If the suite was exited for any reason other than an exception, the\n return value from ``__exit__()`` is ignored, and execution proceeds\n at the normal location for the kind of exit that was taken.\n\nWith more than one item, the context managers are processed as if\nmultiple ``with`` statements were nested:\n\n with A() as a, B() as b:\n suite\n\nis equivalent to\n\n with A() as a:\n with B() as b:\n suite\n\nChanged in version 3.1: Support for multiple context expressions.\n\nSee also:\n\n **PEP 0343** - The "with" statement\n The specification, background, and examples for the Python\n ``with`` statement.\n\n\nFunction definitions\n====================\n\nA function definition defines a user-defined function object (see\nsection *The standard type hierarchy*):\n\n funcdef ::= [decorators] "def" funcname "(" [parameter_list] ")" ["->" expression] ":" suite\n decorators ::= decorator+\n decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE\n dotted_name ::= identifier ("." identifier)*\n parameter_list ::= (defparameter ",")*\n ( "*" [parameter] ("," defparameter)*\n [, "**" parameter]\n | "**" parameter\n | defparameter [","] )\n parameter ::= identifier [":" expression]\n defparameter ::= parameter ["=" expression]\n funcname ::= identifier\n\nA function definition is an executable statement. Its execution binds\nthe function name in the current local namespace to a function object\n(a wrapper around the executable code for the function). This\nfunction object contains a reference to the current global namespace\nas the global namespace to be used when the function is called.\n\nThe function definition does not execute the function body; this gets\nexecuted only when the function is called. [3]\n\nA function definition may be wrapped by one or more *decorator*\nexpressions. Decorator expressions are evaluated when the function is\ndefined, in the scope that contains the function definition. The\nresult must be a callable, which is invoked with the function object\nas the only argument. The returned value is bound to the function name\ninstead of the function object. Multiple decorators are applied in\nnested fashion. For example, the following code\n\n @f1(arg)\n @f2\n def func(): pass\n\nis equivalent to\n\n def func(): pass\n func = f1(arg)(f2(func))\n\nWhen one or more parameters have the form *parameter* ``=``\n*expression*, the function is said to have "default parameter values."\nFor a parameter with a default value, the corresponding argument may\nbe omitted from a call, in which case the parameter\'s default value is\nsubstituted. If a parameter has a default value, all following\nparameters up until the "``*``" must also have a default value ---\nthis is a syntactic restriction that is not expressed by the grammar.\n\n**Default parameter values are evaluated when the function definition\nis executed.** This means that the expression is evaluated once, when\nthe function is defined, and that that same "pre-computed" value is\nused for each call. This is especially important to understand when a\ndefault parameter is a mutable object, such as a list or a dictionary:\nif the function modifies the object (e.g. by appending an item to a\nlist), the default value is in effect modified. This is generally not\nwhat was intended. A way around this is to use ``None`` as the\ndefault, and explicitly test for it in the body of the function, e.g.:\n\n def whats_on_the_telly(penguin=None):\n if penguin is None:\n penguin = []\n penguin.append("property of the zoo")\n return penguin\n\nFunction call semantics are described in more detail in section\n*Calls*. A function call always assigns values to all parameters\nmentioned in the parameter list, either from position arguments, from\nkeyword arguments, or from default values. If the form\n"``*identifier``" is present, it is initialized to a tuple receiving\nany excess positional parameters, defaulting to the empty tuple. If\nthe form "``**identifier``" is present, it is initialized to a new\ndictionary receiving any excess keyword arguments, defaulting to a new\nempty dictionary. Parameters after "``*``" or "``*identifier``" are\nkeyword-only parameters and may only be passed used keyword arguments.\n\nParameters may have annotations of the form "``: expression``"\nfollowing the parameter name. Any parameter may have an annotation\neven those of the form ``*identifier`` or ``**identifier``. Functions\nmay have "return" annotation of the form "``-> expression``" after the\nparameter list. These annotations can be any valid Python expression\nand are evaluated when the function definition is executed.\nAnnotations may be evaluated in a different order than they appear in\nthe source code. The presence of annotations does not change the\nsemantics of a function. The annotation values are available as\nvalues of a dictionary keyed by the parameters\' names in the\n``__annotations__`` attribute of the function object.\n\nIt is also possible to create anonymous functions (functions not bound\nto a name), for immediate use in expressions. This uses lambda forms,\ndescribed in section *Lambdas*. Note that the lambda form is merely a\nshorthand for a simplified function definition; a function defined in\na "``def``" statement can be passed around or assigned to another name\njust like a function defined by a lambda form. The "``def``" form is\nactually more powerful since it allows the execution of multiple\nstatements and annotations.\n\n**Programmer\'s note:** Functions are first-class objects. A "``def``"\nform executed inside a function definition defines a local function\nthat can be returned or passed around. Free variables used in the\nnested function can access the local variables of the function\ncontaining the def. See section *Naming and binding* for details.\n\n\nClass definitions\n=================\n\nA class definition defines a class object (see section *The standard\ntype hierarchy*):\n\n classdef ::= [decorators] "class" classname [inheritance] ":" suite\n inheritance ::= "(" [expression_list] ")"\n classname ::= identifier\n\nA class definition is an executable statement. It first evaluates the\ninheritance list, if present. Each item in the inheritance list\nshould evaluate to a class object or class type which allows\nsubclassing. The class\'s suite is then executed in a new execution\nframe (see section *Naming and binding*), using a newly created local\nnamespace and the original global namespace. (Usually, the suite\ncontains only function definitions.) When the class\'s suite finishes\nexecution, its execution frame is discarded but its local namespace is\nsaved. [4] A class object is then created using the inheritance list\nfor the base classes and the saved local namespace for the attribute\ndictionary. The class name is bound to this class object in the\noriginal local namespace.\n\nClasses can also be decorated; as with functions,\n\n @f1(arg)\n @f2\n class Foo: pass\n\nis equivalent to\n\n class Foo: pass\n Foo = f1(arg)(f2(Foo))\n\n**Programmer\'s note:** Variables defined in the class definition are\nclass variables; they are shared by instances. Instance variables can\nbe set in a method with ``self.name = value``. Both class and\ninstance variables are accessible through the notation\n"``self.name``", and an instance variable hides a class variable with\nthe same name when accessed in this way. Class variables can be used\nas defaults for instance variables, but using mutable values there can\nlead to unexpected results. Descriptors can be used to create\ninstance variables with different implementation details.\n\nSee also:\n\n **PEP 3129** - Class Decorators\n\nClass definitions, like function definitions, may be wrapped by one or\nmore *decorator* expressions. The evaluation rules for the decorator\nexpressions are the same as for functions. The result must be a class\nobject, which is then bound to the class name.\n\n-[ Footnotes ]-\n\n[1] The exception is propagated to the invocation stack only if there\n is no ``finally`` clause that negates the exception.\n\n[2] Currently, control "flows off the end" except in the case of an\n exception or the execution of a ``return``, ``continue``, or\n ``break`` statement.\n\n[3] A string literal appearing as the first statement in the function\n body is transformed into the function\'s ``__doc__`` attribute and\n therefore the function\'s *docstring*.\n\n[4] A string literal appearing as the first statement in the class\n body is transformed into the namespace\'s ``__doc__`` item and\n therefore the class\'s *docstring*.\n', + 'compound': '\nCompound statements\n*******************\n\nCompound statements contain (groups of) other statements; they affect\nor control the execution of those other statements in some way. In\ngeneral, compound statements span multiple lines, although in simple\nincarnations a whole compound statement may be contained in one line.\n\nThe ``if``, ``while`` and ``for`` statements implement traditional\ncontrol flow constructs. ``try`` specifies exception handlers and/or\ncleanup code for a group of statements, while the ``with`` statement\nallows the execution of initialization and finalization code around a\nblock of code. Function and class definitions are also syntactically\ncompound statements.\n\nCompound statements consist of one or more \'clauses.\' A clause\nconsists of a header and a \'suite.\' The clause headers of a\nparticular compound statement are all at the same indentation level.\nEach clause header begins with a uniquely identifying keyword and ends\nwith a colon. A suite is a group of statements controlled by a\nclause. A suite can be one or more semicolon-separated simple\nstatements on the same line as the header, following the header\'s\ncolon, or it can be one or more indented statements on subsequent\nlines. Only the latter form of suite can contain nested compound\nstatements; the following is illegal, mostly because it wouldn\'t be\nclear to which ``if`` clause a following ``else`` clause would belong:\n\n if test1: if test2: print(x)\n\nAlso note that the semicolon binds tighter than the colon in this\ncontext, so that in the following example, either all or none of the\n``print()`` calls are executed:\n\n if x < y < z: print(x); print(y); print(z)\n\nSummarizing:\n\n compound_stmt ::= if_stmt\n | while_stmt\n | for_stmt\n | try_stmt\n | with_stmt\n | funcdef\n | classdef\n suite ::= stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT\n statement ::= stmt_list NEWLINE | compound_stmt\n stmt_list ::= simple_stmt (";" simple_stmt)* [";"]\n\nNote that statements always end in a ``NEWLINE`` possibly followed by\na ``DEDENT``. Also note that optional continuation clauses always\nbegin with a keyword that cannot start a statement, thus there are no\nambiguities (the \'dangling ``else``\' problem is solved in Python by\nrequiring nested ``if`` statements to be indented).\n\nThe formatting of the grammar rules in the following sections places\neach clause on a separate line for clarity.\n\n\nThe ``if`` statement\n====================\n\nThe ``if`` statement is used for conditional execution:\n\n if_stmt ::= "if" expression ":" suite\n ( "elif" expression ":" suite )*\n ["else" ":" suite]\n\nIt selects exactly one of the suites by evaluating the expressions one\nby one until one is found to be true (see section *Boolean operations*\nfor the definition of true and false); then that suite is executed\n(and no other part of the ``if`` statement is executed or evaluated).\nIf all expressions are false, the suite of the ``else`` clause, if\npresent, is executed.\n\n\nThe ``while`` statement\n=======================\n\nThe ``while`` statement is used for repeated execution as long as an\nexpression is true:\n\n while_stmt ::= "while" expression ":" suite\n ["else" ":" suite]\n\nThis repeatedly tests the expression and, if it is true, executes the\nfirst suite; if the expression is false (which may be the first time\nit is tested) the suite of the ``else`` clause, if present, is\nexecuted and the loop terminates.\n\nA ``break`` statement executed in the first suite terminates the loop\nwithout executing the ``else`` clause\'s suite. A ``continue``\nstatement executed in the first suite skips the rest of the suite and\ngoes back to testing the expression.\n\n\nThe ``for`` statement\n=====================\n\nThe ``for`` statement is used to iterate over the elements of a\nsequence (such as a string, tuple or list) or other iterable object:\n\n for_stmt ::= "for" target_list "in" expression_list ":" suite\n ["else" ":" suite]\n\nThe expression list is evaluated once; it should yield an iterable\nobject. An iterator is created for the result of the\n``expression_list``. The suite is then executed once for each item\nprovided by the iterator, in the order of ascending indices. Each\nitem in turn is assigned to the target list using the standard rules\nfor assignments (see *Assignment statements*), and then the suite is\nexecuted. When the items are exhausted (which is immediately when the\nsequence is empty or an iterator raises a ``StopIteration``\nexception), the suite in the ``else`` clause, if present, is executed,\nand the loop terminates.\n\nA ``break`` statement executed in the first suite terminates the loop\nwithout executing the ``else`` clause\'s suite. A ``continue``\nstatement executed in the first suite skips the rest of the suite and\ncontinues with the next item, or with the ``else`` clause if there was\nno next item.\n\nThe suite may assign to the variable(s) in the target list; this does\nnot affect the next item assigned to it.\n\nNames in the target list are not deleted when the loop is finished,\nbut if the sequence is empty, it will not have been assigned to at all\nby the loop. Hint: the built-in function ``range()`` returns an\niterator of integers suitable to emulate the effect of Pascal\'s ``for\ni := a to b do``; e.g., ``list(range(3))`` returns the list ``[0, 1,\n2]``.\n\nNote: There is a subtlety when the sequence is being modified by the loop\n (this can only occur for mutable sequences, i.e. lists). An\n internal counter is used to keep track of which item is used next,\n and this is incremented on each iteration. When this counter has\n reached the length of the sequence the loop terminates. This means\n that if the suite deletes the current (or a previous) item from the\n sequence, the next item will be skipped (since it gets the index of\n the current item which has already been treated). Likewise, if the\n suite inserts an item in the sequence before the current item, the\n current item will be treated again the next time through the loop.\n This can lead to nasty bugs that can be avoided by making a\n temporary copy using a slice of the whole sequence, e.g.,\n\n for x in a[:]:\n if x < 0: a.remove(x)\n\n\nThe ``try`` statement\n=====================\n\nThe ``try`` statement specifies exception handlers and/or cleanup code\nfor a group of statements:\n\n try_stmt ::= try1_stmt | try2_stmt\n try1_stmt ::= "try" ":" suite\n ("except" [expression ["as" target]] ":" suite)+\n ["else" ":" suite]\n ["finally" ":" suite]\n try2_stmt ::= "try" ":" suite\n "finally" ":" suite\n\nThe ``except`` clause(s) specify one or more exception handlers. When\nno exception occurs in the ``try`` clause, no exception handler is\nexecuted. When an exception occurs in the ``try`` suite, a search for\nan exception handler is started. This search inspects the except\nclauses in turn until one is found that matches the exception. An\nexpression-less except clause, if present, must be last; it matches\nany exception. For an except clause with an expression, that\nexpression is evaluated, and the clause matches the exception if the\nresulting object is "compatible" with the exception. An object is\ncompatible with an exception if it is the class or a base class of the\nexception object or a tuple containing an item compatible with the\nexception.\n\nIf no except clause matches the exception, the search for an exception\nhandler continues in the surrounding code and on the invocation stack.\n[1]\n\nIf the evaluation of an expression in the header of an except clause\nraises an exception, the original search for a handler is canceled and\na search starts for the new exception in the surrounding code and on\nthe call stack (it is treated as if the entire ``try`` statement\nraised the exception).\n\nWhen a matching except clause is found, the exception is assigned to\nthe target specified after the ``as`` keyword in that except clause,\nif present, and the except clause\'s suite is executed. All except\nclauses must have an executable block. When the end of this block is\nreached, execution continues normally after the entire try statement.\n(This means that if two nested handlers exist for the same exception,\nand the exception occurs in the try clause of the inner handler, the\nouter handler will not handle the exception.)\n\nWhen an exception has been assigned using ``as target``, it is cleared\nat the end of the except clause. This is as if\n\n except E as N:\n foo\n\nwas translated to\n\n except E as N:\n try:\n foo\n finally:\n del N\n\nThis means the exception must be assigned to a different name to be\nable to refer to it after the except clause. Exceptions are cleared\nbecause with the traceback attached to them, they form a reference\ncycle with the stack frame, keeping all locals in that frame alive\nuntil the next garbage collection occurs.\n\nBefore an except clause\'s suite is executed, details about the\nexception are stored in the ``sys`` module and can be access via\n``sys.exc_info()``. ``sys.exc_info()`` returns a 3-tuple consisting\nof: ``exc_type``, the exception class; ``exc_value``, the exception\ninstance; ``exc_traceback``, a traceback object (see section *The\nstandard type hierarchy*) identifying the point in the program where\nthe exception occurred. ``sys.exc_info()`` values are restored to\ntheir previous values (before the call) when returning from a function\nthat handled an exception.\n\nThe optional ``else`` clause is executed if and when control flows off\nthe end of the ``try`` clause. [2] Exceptions in the ``else`` clause\nare not handled by the preceding ``except`` clauses.\n\nIf ``finally`` is present, it specifies a \'cleanup\' handler. The\n``try`` clause is executed, including any ``except`` and ``else``\nclauses. If an exception occurs in any of the clauses and is not\nhandled, the exception is temporarily saved. The ``finally`` clause is\nexecuted. If there is a saved exception, it is re-raised at the end\nof the ``finally`` clause. If the ``finally`` clause raises another\nexception or executes a ``return`` or ``break`` statement, the saved\nexception is lost. The exception information is not available to the\nprogram during execution of the ``finally`` clause.\n\nWhen a ``return``, ``break`` or ``continue`` statement is executed in\nthe ``try`` suite of a ``try``...``finally`` statement, the\n``finally`` clause is also executed \'on the way out.\' A ``continue``\nstatement is illegal in the ``finally`` clause. (The reason is a\nproblem with the current implementation --- this restriction may be\nlifted in the future).\n\nAdditional information on exceptions can be found in section\n*Exceptions*, and information on using the ``raise`` statement to\ngenerate exceptions may be found in section *The raise statement*.\n\n\nThe ``with`` statement\n======================\n\nThe ``with`` statement is used to wrap the execution of a block with\nmethods defined by a context manager (see section *With Statement\nContext Managers*). This allows common\n``try``...``except``...``finally`` usage patterns to be encapsulated\nfor convenient reuse.\n\n with_stmt ::= "with" with_item ("," with_item)* ":" suite\n with_item ::= expression ["as" target]\n\nThe execution of the ``with`` statement with one "item" proceeds as\nfollows:\n\n1. The context expression (the expression given in the ``with_item``)\n is evaluated to obtain a context manager.\n\n2. The context manager\'s ``__exit__()`` is loaded for later use.\n\n3. The context manager\'s ``__enter__()`` method is invoked.\n\n4. If a target was included in the ``with`` statement, the return\n value from ``__enter__()`` is assigned to it.\n\n Note: The ``with`` statement guarantees that if the ``__enter__()``\n method returns without an error, then ``__exit__()`` will always\n be called. Thus, if an error occurs during the assignment to the\n target list, it will be treated the same as an error occurring\n within the suite would be. See step 6 below.\n\n5. The suite is executed.\n\n6. The context manager\'s ``__exit__()`` method is invoked. If an\n exception caused the suite to be exited, its type, value, and\n traceback are passed as arguments to ``__exit__()``. Otherwise,\n three ``None`` arguments are supplied.\n\n If the suite was exited due to an exception, and the return value\n from the ``__exit__()`` method was false, the exception is\n reraised. If the return value was true, the exception is\n suppressed, and execution continues with the statement following\n the ``with`` statement.\n\n If the suite was exited for any reason other than an exception, the\n return value from ``__exit__()`` is ignored, and execution proceeds\n at the normal location for the kind of exit that was taken.\n\nWith more than one item, the context managers are processed as if\nmultiple ``with`` statements were nested:\n\n with A() as a, B() as b:\n suite\n\nis equivalent to\n\n with A() as a:\n with B() as b:\n suite\n\nChanged in version 3.1: Support for multiple context expressions.\n\nSee also:\n\n **PEP 0343** - The "with" statement\n The specification, background, and examples for the Python\n ``with`` statement.\n\n\nFunction definitions\n====================\n\nA function definition defines a user-defined function object (see\nsection *The standard type hierarchy*):\n\n funcdef ::= [decorators] "def" funcname "(" [parameter_list] ")" ["->" expression] ":" suite\n decorators ::= decorator+\n decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE\n dotted_name ::= identifier ("." identifier)*\n parameter_list ::= (defparameter ",")*\n ( "*" [parameter] ("," defparameter)*\n [, "**" parameter]\n | "**" parameter\n | defparameter [","] )\n parameter ::= identifier [":" expression]\n defparameter ::= parameter ["=" expression]\n funcname ::= identifier\n\nA function definition is an executable statement. Its execution binds\nthe function name in the current local namespace to a function object\n(a wrapper around the executable code for the function). This\nfunction object contains a reference to the current global namespace\nas the global namespace to be used when the function is called.\n\nThe function definition does not execute the function body; this gets\nexecuted only when the function is called. [3]\n\nA function definition may be wrapped by one or more *decorator*\nexpressions. Decorator expressions are evaluated when the function is\ndefined, in the scope that contains the function definition. The\nresult must be a callable, which is invoked with the function object\nas the only argument. The returned value is bound to the function name\ninstead of the function object. Multiple decorators are applied in\nnested fashion. For example, the following code\n\n @f1(arg)\n @f2\n def func(): pass\n\nis equivalent to\n\n def func(): pass\n func = f1(arg)(f2(func))\n\nWhen one or more parameters have the form *parameter* ``=``\n*expression*, the function is said to have "default parameter values."\nFor a parameter with a default value, the corresponding argument may\nbe omitted from a call, in which case the parameter\'s default value is\nsubstituted. If a parameter has a default value, all following\nparameters up until the "``*``" must also have a default value ---\nthis is a syntactic restriction that is not expressed by the grammar.\n\n**Default parameter values are evaluated when the function definition\nis executed.** This means that the expression is evaluated once, when\nthe function is defined, and that that same "pre-computed" value is\nused for each call. This is especially important to understand when a\ndefault parameter is a mutable object, such as a list or a dictionary:\nif the function modifies the object (e.g. by appending an item to a\nlist), the default value is in effect modified. This is generally not\nwhat was intended. A way around this is to use ``None`` as the\ndefault, and explicitly test for it in the body of the function, e.g.:\n\n def whats_on_the_telly(penguin=None):\n if penguin is None:\n penguin = []\n penguin.append("property of the zoo")\n return penguin\n\nFunction call semantics are described in more detail in section\n*Calls*. A function call always assigns values to all parameters\nmentioned in the parameter list, either from position arguments, from\nkeyword arguments, or from default values. If the form\n"``*identifier``" is present, it is initialized to a tuple receiving\nany excess positional parameters, defaulting to the empty tuple. If\nthe form "``**identifier``" is present, it is initialized to a new\ndictionary receiving any excess keyword arguments, defaulting to a new\nempty dictionary. Parameters after "``*``" or "``*identifier``" are\nkeyword-only parameters and may only be passed used keyword arguments.\n\nParameters may have annotations of the form "``: expression``"\nfollowing the parameter name. Any parameter may have an annotation\neven those of the form ``*identifier`` or ``**identifier``. Functions\nmay have "return" annotation of the form "``-> expression``" after the\nparameter list. These annotations can be any valid Python expression\nand are evaluated when the function definition is executed.\nAnnotations may be evaluated in a different order than they appear in\nthe source code. The presence of annotations does not change the\nsemantics of a function. The annotation values are available as\nvalues of a dictionary keyed by the parameters\' names in the\n``__annotations__`` attribute of the function object.\n\nIt is also possible to create anonymous functions (functions not bound\nto a name), for immediate use in expressions. This uses lambda forms,\ndescribed in section *Lambdas*. Note that the lambda form is merely a\nshorthand for a simplified function definition; a function defined in\na "``def``" statement can be passed around or assigned to another name\njust like a function defined by a lambda form. The "``def``" form is\nactually more powerful since it allows the execution of multiple\nstatements and annotations.\n\n**Programmer\'s note:** Functions are first-class objects. A "``def``"\nform executed inside a function definition defines a local function\nthat can be returned or passed around. Free variables used in the\nnested function can access the local variables of the function\ncontaining the def. See section *Naming and binding* for details.\n\n\nClass definitions\n=================\n\nA class definition defines a class object (see section *The standard\ntype hierarchy*):\n\n classdef ::= [decorators] "class" classname [inheritance] ":" suite\n inheritance ::= "(" [argument_list [","] ] ")"\n classname ::= identifier\n\nA class definition is an executable statement. The inheritance list\nusually gives a list of base classes (see *Customizing class creation*\nfor more advanced uses), so each item in the list should evaluate to a\nclass object which allows subclassing.\n\nThe class\'s suite is then executed in a new execution frame (see\n*Naming and binding*), using a newly created local namespace and the\noriginal global namespace. (Usually, the suite contains mostly\nfunction definitions.) When the class\'s suite finishes execution, its\nexecution frame is discarded but its local namespace is saved. [4] A\nclass object is then created using the inheritance list for the base\nclasses and the saved local namespace for the attribute dictionary.\nThe class name is bound to this class object in the original local\nnamespace.\n\nClass creation can be customized heavily using *metaclasses*.\n\nClasses can also be decorated; as with functions,\n\n @f1(arg)\n @f2\n class Foo: pass\n\nis equivalent to\n\n class Foo: pass\n Foo = f1(arg)(f2(Foo))\n\n**Programmer\'s note:** Variables defined in the class definition are\nclass attributes; they are shared by instances. Instance attributes\ncan be set in a method with ``self.name = value``. Both class and\ninstance attributes are accessible through the notation\n"``self.name``", and an instance attribute hides a class attribute\nwith the same name when accessed in this way. Class attributes can be\nused as defaults for instance attributes, but using mutable values\nthere can lead to unexpected results. *Descriptors* can be used to\ncreate instance variables with different implementation details.\n\nSee also:\n\n **PEP 3116** - Metaclasses in Python 3 **PEP 3129** - Class\n Decorators\n\n-[ Footnotes ]-\n\n[1] The exception is propagated to the invocation stack only if there\n is no ``finally`` clause that negates the exception.\n\n[2] Currently, control "flows off the end" except in the case of an\n exception or the execution of a ``return``, ``continue``, or\n ``break`` statement.\n\n[3] A string literal appearing as the first statement in the function\n body is transformed into the function\'s ``__doc__`` attribute and\n therefore the function\'s *docstring*.\n\n[4] A string literal appearing as the first statement in the class\n body is transformed into the namespace\'s ``__doc__`` item and\n therefore the class\'s *docstring*.\n', 'context-managers': '\nWith Statement Context Managers\n*******************************\n\nA *context manager* is an object that defines the runtime context to\nbe established when executing a ``with`` statement. The context\nmanager handles the entry into, and the exit from, the desired runtime\ncontext for the execution of the block of code. Context managers are\nnormally invoked using the ``with`` statement (described in section\n*The with statement*), but can also be used by directly invoking their\nmethods.\n\nTypical uses of context managers include saving and restoring various\nkinds of global state, locking and unlocking resources, closing opened\nfiles, etc.\n\nFor more information on context managers, see *Context Manager Types*.\n\nobject.__enter__(self)\n\n Enter the runtime context related to this object. The ``with``\n statement will bind this method\'s return value to the target(s)\n specified in the ``as`` clause of the statement, if any.\n\nobject.__exit__(self, exc_type, exc_value, traceback)\n\n Exit the runtime context related to this object. The parameters\n describe the exception that caused the context to be exited. If the\n context was exited without an exception, all three arguments will\n be ``None``.\n\n If an exception is supplied, and the method wishes to suppress the\n exception (i.e., prevent it from being propagated), it should\n return a true value. Otherwise, the exception will be processed\n normally upon exit from this method.\n\n Note that ``__exit__()`` methods should not reraise the passed-in\n exception; this is the caller\'s responsibility.\n\nSee also:\n\n **PEP 0343** - The "with" statement\n The specification, background, and examples for the Python\n ``with`` statement.\n', 'continue': '\nThe ``continue`` statement\n**************************\n\n continue_stmt ::= "continue"\n\n``continue`` may only occur syntactically nested in a ``for`` or\n``while`` loop, but not nested in a function or class definition or\n``finally`` clause within that loop. It continues with the next cycle\nof the nearest enclosing loop.\n\nWhen ``continue`` passes control out of a ``try`` statement with a\n``finally`` clause, that ``finally`` clause is executed before really\nstarting the next loop cycle.\n', 'conversions': '\nArithmetic conversions\n**********************\n\nWhen a description of an arithmetic operator below uses the phrase\n"the numeric arguments are converted to a common type," this means\nthat the operator implementation for built-in types works that way:\n\n* If either argument is a complex number, the other is converted to\n complex;\n\n* otherwise, if either argument is a floating point number, the other\n is converted to floating point;\n\n* otherwise, both must be integers and no conversion is necessary.\n\nSome additional rules apply for certain operators (e.g., a string left\nargument to the \'%\' operator). Extensions must define their own\nconversion behavior.\n', @@ -36,16 +36,17 @@ 'formatstrings': '\nFormat String Syntax\n********************\n\nThe ``str.format()`` method and the ``Formatter`` class share the same\nsyntax for format strings (although in the case of ``Formatter``,\nsubclasses can define their own format string syntax).\n\nFormat strings contain "replacement fields" surrounded by curly braces\n``{}``. Anything that is not contained in braces is considered literal\ntext, which is copied unchanged to the output. If you need to include\na brace character in the literal text, it can be escaped by doubling:\n``{{`` and ``}}``.\n\nThe grammar for a replacement field is as follows:\n\n replacement_field ::= "{" [field_name] ["!" conversion] [":" format_spec] "}"\n field_name ::= arg_name ("." attribute_name | "[" element_index "]")*\n arg_name ::= [identifier | integer]\n attribute_name ::= identifier\n element_index ::= integer | index_string\n index_string ::= +\n conversion ::= "r" | "s" | "a"\n format_spec ::= \n\nIn less formal terms, the replacement field can start with a\n*field_name* that specifies the object whose value is to be formatted\nand inserted into the output instead of the replacement field. The\n*field_name* is optionally followed by a *conversion* field, which is\npreceded by an exclamation point ``\'!\'``, and a *format_spec*, which\nis preceded by a colon ``\':\'``. These specify a non-default format\nfor the replacement value.\n\nSee also the *Format Specification Mini-Language* section.\n\nThe *field_name* itself begins with an *arg_name* that is either\neither a number or a keyword. If it\'s a number, it refers to a\npositional argument, and if it\'s a keyword, it refers to a named\nkeyword argument. If the numerical arg_names in a format string are\n0, 1, 2, ... in sequence, they can all be omitted (not just some) and\nthe numbers 0, 1, 2, ... will be automatically inserted in that order.\nThe *arg_name* can be followed by any number of index or attribute\nexpressions. An expression of the form ``\'.name\'`` selects the named\nattribute using ``getattr()``, while an expression of the form\n``\'[index]\'`` does an index lookup using ``__getitem__()``.\n\nChanged in version 3.1: The positional argument specifiers can be\nomitted, so ``\'{} {}\'`` is equivalent to ``\'{0} {1}\'``.\n\nSome simple format string examples:\n\n "First, thou shalt count to {0}" # References first positional argument\n "Bring me a {}" # Implicitly references the first positional argument\n "From {} to {}" # Same as "From {0} to {1}"\n "My quest is {name}" # References keyword argument \'name\'\n "Weight in tons {0.weight}" # \'weight\' attribute of first positional arg\n "Units destroyed: {players[0]}" # First element of keyword argument \'players\'.\n\nThe *conversion* field causes a type coercion before formatting.\nNormally, the job of formatting a value is done by the\n``__format__()`` method of the value itself. However, in some cases\nit is desirable to force a type to be formatted as a string,\noverriding its own definition of formatting. By converting the value\nto a string before calling ``__format__()``, the normal formatting\nlogic is bypassed.\n\nThree conversion flags are currently supported: ``\'!s\'`` which calls\n``str()`` on the value, ``\'!r\'`` which calls ``repr()`` and ``\'!a\'``\nwhich calls ``ascii()``.\n\nSome examples:\n\n "Harold\'s a clever {0!s}" # Calls str() on the argument first\n "Bring out the holy {name!r}" # Calls repr() on the argument first\n "More {!a}" # Calls ascii() on the argument first\n\nThe *format_spec* field contains a specification of how the value\nshould be presented, including such details as field width, alignment,\npadding, decimal precision and so on. Each value type can define its\nown "formatting mini-language" or interpretation of the *format_spec*.\n\nMost built-in types support a common formatting mini-language, which\nis described in the next section.\n\nA *format_spec* field can also include nested replacement fields\nwithin it. These nested replacement fields can contain only a field\nname; conversion flags and format specifications are not allowed. The\nreplacement fields within the format_spec are substituted before the\n*format_spec* string is interpreted. This allows the formatting of a\nvalue to be dynamically specified.\n\nSee the *Format examples* section for some examples.\n\n\nFormat Specification Mini-Language\n==================================\n\n"Format specifications" are used within replacement fields contained\nwithin a format string to define how individual values are presented\n(see *Format String Syntax*). They can also be passed directly to the\nbuilt-in ``format()`` function. Each formattable type may define how\nthe format specification is to be interpreted.\n\nMost built-in types implement the following options for format\nspecifications, although some of the formatting options are only\nsupported by the numeric types.\n\nA general convention is that an empty format string (``""``) produces\nthe same result as if you had called ``str()`` on the value. A non-\nempty format string typically modifies the result.\n\nThe general form of a *standard format specifier* is:\n\n format_spec ::= [[fill]align][sign][#][0][width][,][.precision][type]\n fill ::= \n align ::= "<" | ">" | "=" | "^"\n sign ::= "+" | "-" | " "\n width ::= integer\n precision ::= integer\n type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"\n\nThe *fill* character can be any character other than \'}\' (which\nsignifies the end of the field). The presence of a fill character is\nsignaled by the *next* character, which must be one of the alignment\noptions. If the second character of *format_spec* is not a valid\nalignment option, then it is assumed that both the fill character and\nthe alignment option are absent.\n\nThe meaning of the various alignment options is as follows:\n\n +-----------+------------------------------------------------------------+\n | Option | Meaning |\n +===========+============================================================+\n | ``\'<\'`` | Forces the field to be left-aligned within the available |\n | | space (this is the default). |\n +-----------+------------------------------------------------------------+\n | ``\'>\'`` | Forces the field to be right-aligned within the available |\n | | space. |\n +-----------+------------------------------------------------------------+\n | ``\'=\'`` | Forces the padding to be placed after the sign (if any) |\n | | but before the digits. This is used for printing fields |\n | | in the form \'+000000120\'. This alignment option is only |\n | | valid for numeric types. |\n +-----------+------------------------------------------------------------+\n | ``\'^\'`` | Forces the field to be centered within the available |\n | | space. |\n +-----------+------------------------------------------------------------+\n\nNote that unless a minimum field width is defined, the field width\nwill always be the same size as the data to fill it, so that the\nalignment option has no meaning in this case.\n\nThe *sign* option is only valid for number types, and can be one of\nthe following:\n\n +-----------+------------------------------------------------------------+\n | Option | Meaning |\n +===========+============================================================+\n | ``\'+\'`` | indicates that a sign should be used for both positive as |\n | | well as negative numbers. |\n +-----------+------------------------------------------------------------+\n | ``\'-\'`` | indicates that a sign should be used only for negative |\n | | numbers (this is the default behavior). |\n +-----------+------------------------------------------------------------+\n | space | indicates that a leading space should be used on positive |\n | | numbers, and a minus sign on negative numbers. |\n +-----------+------------------------------------------------------------+\n\nThe ``\'#\'`` option is only valid for integers, and only for binary,\noctal, or hexadecimal output. If present, it specifies that the\noutput will be prefixed by ``\'0b\'``, ``\'0o\'``, or ``\'0x\'``,\nrespectively.\n\nThe ``\',\'`` option signals the use of a comma for a thousands\nseparator. For a locale aware separator, use the ``\'n\'`` integer\npresentation type instead.\n\nChanged in version 3.1: Added the ``\',\'`` option (see also **PEP\n378**).\n\n*width* is a decimal integer defining the minimum field width. If not\nspecified, then the field width will be determined by the content.\n\nIf the *width* field is preceded by a zero (``\'0\'``) character, this\nenables zero-padding. This is equivalent to an *alignment* type of\n``\'=\'`` and a *fill* character of ``\'0\'``.\n\nThe *precision* is a decimal number indicating how many digits should\nbe displayed after the decimal point for a floating point value\nformatted with ``\'f\'`` and ``\'F\'``, or before and after the decimal\npoint for a floating point value formatted with ``\'g\'`` or ``\'G\'``.\nFor non-number types the field indicates the maximum field size - in\nother words, how many characters will be used from the field content.\nThe *precision* is not allowed for integer values.\n\nFinally, the *type* determines how the data should be presented.\n\nThe available string presentation types are:\n\n +-----------+------------------------------------------------------------+\n | Type | Meaning |\n +===========+============================================================+\n | ``\'s\'`` | String format. This is the default type for strings and |\n | | may be omitted. |\n +-----------+------------------------------------------------------------+\n | None | The same as ``\'s\'``. |\n +-----------+------------------------------------------------------------+\n\nThe available integer presentation types are:\n\n +-----------+------------------------------------------------------------+\n | Type | Meaning |\n +===========+============================================================+\n | ``\'b\'`` | Binary format. Outputs the number in base 2. |\n +-----------+------------------------------------------------------------+\n | ``\'c\'`` | Character. Converts the integer to the corresponding |\n | | unicode character before printing. |\n +-----------+------------------------------------------------------------+\n | ``\'d\'`` | Decimal Integer. Outputs the number in base 10. |\n +-----------+------------------------------------------------------------+\n | ``\'o\'`` | Octal format. Outputs the number in base 8. |\n +-----------+------------------------------------------------------------+\n | ``\'x\'`` | Hex format. Outputs the number in base 16, using lower- |\n | | case letters for the digits above 9. |\n +-----------+------------------------------------------------------------+\n | ``\'X\'`` | Hex format. Outputs the number in base 16, using upper- |\n | | case letters for the digits above 9. |\n +-----------+------------------------------------------------------------+\n | ``\'n\'`` | Number. This is the same as ``\'d\'``, except that it uses |\n | | the current locale setting to insert the appropriate |\n | | number separator characters. |\n +-----------+------------------------------------------------------------+\n | None | The same as ``\'d\'``. |\n +-----------+------------------------------------------------------------+\n\nIn addition to the above presentation types, integers can be formatted\nwith the floating point presentation types listed below (except\n``\'n\'`` and None). When doing so, ``float()`` is used to convert the\ninteger to a floating point number before formatting.\n\nThe available presentation types for floating point and decimal values\nare:\n\n +-----------+------------------------------------------------------------+\n | Type | Meaning |\n +===========+============================================================+\n | ``\'e\'`` | Exponent notation. Prints the number in scientific |\n | | notation using the letter \'e\' to indicate the exponent. |\n +-----------+------------------------------------------------------------+\n | ``\'E\'`` | Exponent notation. Same as ``\'e\'`` except it uses an upper |\n | | case \'E\' as the separator character. |\n +-----------+------------------------------------------------------------+\n | ``\'f\'`` | Fixed point. Displays the number as a fixed-point number. |\n +-----------+------------------------------------------------------------+\n | ``\'F\'`` | Fixed point. Same as ``\'f\'``, but converts ``nan`` to |\n | | ``NAN`` and ``inf`` to ``INF``. |\n +-----------+------------------------------------------------------------+\n | ``\'g\'`` | General format. For a given precision ``p >= 1``, this |\n | | rounds the number to ``p`` significant digits and then |\n | | formats the result in either fixed-point format or in |\n | | scientific notation, depending on its magnitude. The |\n | | precise rules are as follows: suppose that the result |\n | | formatted with presentation type ``\'e\'`` and precision |\n | | ``p-1`` would have exponent ``exp``. Then if ``-4 <= exp |\n | | < p``, the number is formatted with presentation type |\n | | ``\'f\'`` and precision ``p-1-exp``. Otherwise, the number |\n | | is formatted with presentation type ``\'e\'`` and precision |\n | | ``p-1``. In both cases insignificant trailing zeros are |\n | | removed from the significand, and the decimal point is |\n | | also removed if there are no remaining digits following |\n | | it. Postive and negative infinity, positive and negative |\n | | zero, and nans, are formatted as ``inf``, ``-inf``, ``0``, |\n | | ``-0`` and ``nan`` respectively, regardless of the |\n | | precision. A precision of ``0`` is treated as equivalent |\n | | to a precision of ``1``. |\n +-----------+------------------------------------------------------------+\n | ``\'G\'`` | General format. Same as ``\'g\'`` except switches to ``\'E\'`` |\n | | if the number gets too large. The representations of |\n | | infinity and NaN are uppercased, too. |\n +-----------+------------------------------------------------------------+\n | ``\'n\'`` | Number. This is the same as ``\'g\'``, except that it uses |\n | | the current locale setting to insert the appropriate |\n | | number separator characters. |\n +-----------+------------------------------------------------------------+\n | ``\'%\'`` | Percentage. Multiplies the number by 100 and displays in |\n | | fixed (``\'f\'``) format, followed by a percent sign. |\n +-----------+------------------------------------------------------------+\n | None | Similar to ``\'g\'``, except with at least one digit past |\n | | the decimal point and a default precision of 12. This is |\n | | intended to match ``str()``, except you can add the other |\n | | format modifiers. |\n +-----------+------------------------------------------------------------+\n\n\nFormat examples\n===============\n\nThis section contains examples of the new format syntax and comparison\nwith the old ``%``-formatting.\n\nIn most of the cases the syntax is similar to the old\n``%``-formatting, with the addition of the ``{}`` and with ``:`` used\ninstead of ``%``. For example, ``\'%03.2f\'`` can be translated to\n``\'{:03.2f}\'``.\n\nThe new format syntax also supports new and different options, shown\nin the follow examples.\n\nAccessing arguments by position:\n\n >>> \'{0}, {1}, {2}\'.format(\'a\', \'b\', \'c\')\n \'a, b, c\'\n >>> \'{}, {}, {}\'.format(\'a\', \'b\', \'c\') # 3.1+ only\n \'a, b, c\'\n >>> \'{2}, {1}, {0}\'.format(\'a\', \'b\', \'c\')\n \'c, b, a\'\n >>> \'{2}, {1}, {0}\'.format(*\'abc\') # unpacking argument sequence\n \'c, b, a\'\n >>> \'{0}{1}{0}\'.format(\'abra\', \'cad\') # arguments\' indices can be repeated\n \'abracadabra\'\n\nAccessing arguments by name:\n\n >>> \'Coordinates: {latitude}, {longitude}\'.format(latitude=\'37.24N\', longitude=\'-115.81W\')\n \'Coordinates: 37.24N, -115.81W\'\n >>> coord = {\'latitude\': \'37.24N\', \'longitude\': \'-115.81W\'}\n >>> \'Coordinates: {latitude}, {longitude}\'.format(**coord)\n \'Coordinates: 37.24N, -115.81W\'\n\nAccessing arguments\' attributes:\n\n >>> c = 3-5j\n >>> (\'The complex number {0} is formed from the real part {0.real} \'\n ... \'and the imaginary part {0.imag}.\').format(c)\n \'The complex number (3-5j) is formed from the real part 3.0 and the imaginary part -5.0.\'\n >>> class Point:\n ... def __init__(self, x, y):\n ... self.x, self.y = x, y\n ... def __str__(self):\n ... return \'Point({self.x}, {self.y})\'.format(self=self)\n ...\n >>> str(Point(4, 2))\n \'Point(4, 2)\'\n\nAccessing arguments\' items:\n\n >>> coord = (3, 5)\n >>> \'X: {0[0]}; Y: {0[1]}\'.format(coord)\n \'X: 3; Y: 5\'\n\nReplacing ``%s`` and ``%r``:\n\n >>> "repr() shows quotes: {!r}; str() doesn\'t: {!s}".format(\'test1\', \'test2\')\n "repr() shows quotes: \'test1\'; str() doesn\'t: test2"\n\nAligning the text and specifying a width:\n\n >>> \'{:<30}\'.format(\'left aligned\')\n \'left aligned \'\n >>> \'{:>30}\'.format(\'right aligned\')\n \' right aligned\'\n >>> \'{:^30}\'.format(\'centered\')\n \' centered \'\n >>> \'{:*^30}\'.format(\'centered\') # use \'*\' as a fill char\n \'***********centered***********\'\n\nReplacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign:\n\n >>> \'{:+f}; {:+f}\'.format(3.14, -3.14) # show it always\n \'+3.140000; -3.140000\'\n >>> \'{: f}; {: f}\'.format(3.14, -3.14) # show a space for positive numbers\n \' 3.140000; -3.140000\'\n >>> \'{:-f}; {:-f}\'.format(3.14, -3.14) # show only the minus -- same as \'{:f}; {:f}\'\n \'3.140000; -3.140000\'\n\nReplacing ``%x`` and ``%o`` and converting the value to different\nbases:\n\n >>> # format also supports binary numbers\n >>> "int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}".format(42)\n \'int: 42; hex: 2a; oct: 52; bin: 101010\'\n >>> # with 0x, 0o, or 0b as prefix:\n >>> "int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}".format(42)\n \'int: 42; hex: 0x2a; oct: 0o52; bin: 0b101010\'\n\nUsing the comma as a thousands separator:\n\n >>> \'{:,}\'.format(1234567890)\n \'1,234,567,890\'\n\nExpressing a percentage:\n\n >>> points = 19\n >>> total = 22\n >>> \'Correct answers: {:.2%}.\'.format(points/total)\n \'Correct answers: 86.36%\'\n\nUsing type-specific formatting:\n\n >>> import datetime\n >>> d = datetime.datetime(2010, 7, 4, 12, 15, 58)\n >>> \'{:%Y-%m-%d %H:%M:%S}\'.format(d)\n \'2010-07-04 12:15:58\'\n\nNesting arguments and more complex examples:\n\n >>> for align, text in zip(\'<^>\', [\'left\', \'center\', \'right\']):\n ... \'{0:{align}{fill}16}\'.format(text, fill=align, align=align)\n ...\n \'left<<<<<<<<<<<<\'\n \'^^^^^center^^^^^\'\n \'>>>>>>>>>>>right\'\n >>>\n >>> octets = [192, 168, 0, 1]\n >>> \'{:02X}{:02X}{:02X}{:02X}\'.format(*octets)\n \'C0A80001\'\n >>> int(_, 16)\n 3232235521\n >>>\n >>> width = 5\n >>> for num in range(5,12):\n ... for base in \'dXob\':\n ... print(\'{0:{width}{base}}\'.format(num, base=base, width=width), end=\' \')\n ... print()\n ...\n 5 5 5 101\n 6 6 6 110\n 7 7 7 111\n 8 8 10 1000\n 9 9 11 1001\n 10 A 12 1010\n 11 B 13 1011\n', 'function': '\nFunction definitions\n********************\n\nA function definition defines a user-defined function object (see\nsection *The standard type hierarchy*):\n\n funcdef ::= [decorators] "def" funcname "(" [parameter_list] ")" ["->" expression] ":" suite\n decorators ::= decorator+\n decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE\n dotted_name ::= identifier ("." identifier)*\n parameter_list ::= (defparameter ",")*\n ( "*" [parameter] ("," defparameter)*\n [, "**" parameter]\n | "**" parameter\n | defparameter [","] )\n parameter ::= identifier [":" expression]\n defparameter ::= parameter ["=" expression]\n funcname ::= identifier\n\nA function definition is an executable statement. Its execution binds\nthe function name in the current local namespace to a function object\n(a wrapper around the executable code for the function). This\nfunction object contains a reference to the current global namespace\nas the global namespace to be used when the function is called.\n\nThe function definition does not execute the function body; this gets\nexecuted only when the function is called. [3]\n\nA function definition may be wrapped by one or more *decorator*\nexpressions. Decorator expressions are evaluated when the function is\ndefined, in the scope that contains the function definition. The\nresult must be a callable, which is invoked with the function object\nas the only argument. The returned value is bound to the function name\ninstead of the function object. Multiple decorators are applied in\nnested fashion. For example, the following code\n\n @f1(arg)\n @f2\n def func(): pass\n\nis equivalent to\n\n def func(): pass\n func = f1(arg)(f2(func))\n\nWhen one or more parameters have the form *parameter* ``=``\n*expression*, the function is said to have "default parameter values."\nFor a parameter with a default value, the corresponding argument may\nbe omitted from a call, in which case the parameter\'s default value is\nsubstituted. If a parameter has a default value, all following\nparameters up until the "``*``" must also have a default value ---\nthis is a syntactic restriction that is not expressed by the grammar.\n\n**Default parameter values are evaluated when the function definition\nis executed.** This means that the expression is evaluated once, when\nthe function is defined, and that that same "pre-computed" value is\nused for each call. This is especially important to understand when a\ndefault parameter is a mutable object, such as a list or a dictionary:\nif the function modifies the object (e.g. by appending an item to a\nlist), the default value is in effect modified. This is generally not\nwhat was intended. A way around this is to use ``None`` as the\ndefault, and explicitly test for it in the body of the function, e.g.:\n\n def whats_on_the_telly(penguin=None):\n if penguin is None:\n penguin = []\n penguin.append("property of the zoo")\n return penguin\n\nFunction call semantics are described in more detail in section\n*Calls*. A function call always assigns values to all parameters\nmentioned in the parameter list, either from position arguments, from\nkeyword arguments, or from default values. If the form\n"``*identifier``" is present, it is initialized to a tuple receiving\nany excess positional parameters, defaulting to the empty tuple. If\nthe form "``**identifier``" is present, it is initialized to a new\ndictionary receiving any excess keyword arguments, defaulting to a new\nempty dictionary. Parameters after "``*``" or "``*identifier``" are\nkeyword-only parameters and may only be passed used keyword arguments.\n\nParameters may have annotations of the form "``: expression``"\nfollowing the parameter name. Any parameter may have an annotation\neven those of the form ``*identifier`` or ``**identifier``. Functions\nmay have "return" annotation of the form "``-> expression``" after the\nparameter list. These annotations can be any valid Python expression\nand are evaluated when the function definition is executed.\nAnnotations may be evaluated in a different order than they appear in\nthe source code. The presence of annotations does not change the\nsemantics of a function. The annotation values are available as\nvalues of a dictionary keyed by the parameters\' names in the\n``__annotations__`` attribute of the function object.\n\nIt is also possible to create anonymous functions (functions not bound\nto a name), for immediate use in expressions. This uses lambda forms,\ndescribed in section *Lambdas*. Note that the lambda form is merely a\nshorthand for a simplified function definition; a function defined in\na "``def``" statement can be passed around or assigned to another name\njust like a function defined by a lambda form. The "``def``" form is\nactually more powerful since it allows the execution of multiple\nstatements and annotations.\n\n**Programmer\'s note:** Functions are first-class objects. A "``def``"\nform executed inside a function definition defines a local function\nthat can be returned or passed around. Free variables used in the\nnested function can access the local variables of the function\ncontaining the def. See section *Naming and binding* for details.\n', 'global': '\nThe ``global`` statement\n************************\n\n global_stmt ::= "global" identifier ("," identifier)*\n\nThe ``global`` statement is a declaration which holds for the entire\ncurrent code block. It means that the listed identifiers are to be\ninterpreted as globals. It would be impossible to assign to a global\nvariable without ``global``, although free variables may refer to\nglobals without being declared global.\n\nNames listed in a ``global`` statement must not be used in the same\ncode block textually preceding that ``global`` statement.\n\nNames listed in a ``global`` statement must not be defined as formal\nparameters or in a ``for`` loop control target, ``class`` definition,\nfunction definition, or ``import`` statement.\n\n**CPython implementation detail:** The current implementation does not\nenforce the latter two restrictions, but programs should not abuse\nthis freedom, as future implementations may enforce them or silently\nchange the meaning of the program.\n\n**Programmer\'s note:** the ``global`` is a directive to the parser.\nIt applies only to code parsed at the same time as the ``global``\nstatement. In particular, a ``global`` statement contained in a string\nor code object supplied to the built-in ``exec()`` function does not\naffect the code block *containing* the function call, and code\ncontained in such a string is unaffected by ``global`` statements in\nthe code containing the function call. The same applies to the\n``eval()`` and ``compile()`` functions.\n', - 'id-classes': '\nReserved classes of identifiers\n*******************************\n\nCertain classes of identifiers (besides keywords) have special\nmeanings. These classes are identified by the patterns of leading and\ntrailing underscore characters:\n\n``_*``\n Not imported by ``from module import *``. The special identifier\n ``_`` is used in the interactive interpreter to store the result of\n the last evaluation; it is stored in the ``builtins`` module. When\n not in interactive mode, ``_`` has no special meaning and is not\n defined. See section *The import statement*.\n\n Note: The name ``_`` is often used in conjunction with\n internationalization; refer to the documentation for the\n ``gettext`` module for more information on this convention.\n\n``__*__``\n System-defined names. These names are defined by the interpreter\n and its implementation (including the standard library);\n applications should not expect to define additional names using\n this convention. The set of names of this class defined by Python\n may be extended in future versions. See section *Special method\n names*.\n\n``__*``\n Class-private names. Names in this category, when used within the\n context of a class definition, are re-written to use a mangled form\n to help avoid name clashes between "private" attributes of base and\n derived classes. See section *Identifiers (Names)*.\n', - 'identifiers': '\nIdentifiers and keywords\n************************\n\nIdentifiers (also referred to as *names*) are described by the\nfollowing lexical definitions.\n\nThe syntax of identifiers in Python is based on the Unicode standard\nannex UAX-31, with elaboration and changes as defined below; see also\n**PEP 3131** for further details.\n\nWithin the ASCII range (U+0001..U+007F), the valid characters for\nidentifiers are the same as in Python 2.x: the uppercase and lowercase\nletters ``A`` through ``Z``, the underscore ``_`` and, except for the\nfirst character, the digits ``0`` through ``9``.\n\nPython 3.0 introduces additional characters from outside the ASCII\nrange (see **PEP 3131**). For these characters, the classification\nuses the version of the Unicode Character Database as included in the\n``unicodedata`` module.\n\nIdentifiers are unlimited in length. Case is significant.\n\n identifier ::= id_start id_continue*\n id_start ::= \n id_continue ::= \n\nThe Unicode category codes mentioned above stand for:\n\n* *Lu* - uppercase letters\n\n* *Ll* - lowercase letters\n\n* *Lt* - titlecase letters\n\n* *Lm* - modifier letters\n\n* *Lo* - other letters\n\n* *Nl* - letter numbers\n\n* *Mn* - nonspacing marks\n\n* *Mc* - spacing combining marks\n\n* *Nd* - decimal numbers\n\n* *Pc* - connector punctuations\n\nAll identifiers are converted into the normal form NFC while parsing;\ncomparison of identifiers is based on NFC.\n\nA non-normative HTML file listing all valid identifier characters for\nUnicode 4.1 can be found at http://www.dcl.hpi.uni-\npotsdam.de/home/loewis/table-3131.html.\n\n\nKeywords\n========\n\nThe following identifiers are used as reserved words, or *keywords* of\nthe language, and cannot be used as ordinary identifiers. They must\nbe spelled exactly as written here:\n\n False class finally is return\n None continue for lambda try\n True def from nonlocal while\n and del global not with\n as elif if or yield\n assert else import pass\n break except in raise\n\n\nReserved classes of identifiers\n===============================\n\nCertain classes of identifiers (besides keywords) have special\nmeanings. These classes are identified by the patterns of leading and\ntrailing underscore characters:\n\n``_*``\n Not imported by ``from module import *``. The special identifier\n ``_`` is used in the interactive interpreter to store the result of\n the last evaluation; it is stored in the ``builtins`` module. When\n not in interactive mode, ``_`` has no special meaning and is not\n defined. See section *The import statement*.\n\n Note: The name ``_`` is often used in conjunction with\n internationalization; refer to the documentation for the\n ``gettext`` module for more information on this convention.\n\n``__*__``\n System-defined names. These names are defined by the interpreter\n and its implementation (including the standard library);\n applications should not expect to define additional names using\n this convention. The set of names of this class defined by Python\n may be extended in future versions. See section *Special method\n names*.\n\n``__*``\n Class-private names. Names in this category, when used within the\n context of a class definition, are re-written to use a mangled form\n to help avoid name clashes between "private" attributes of base and\n derived classes. See section *Identifiers (Names)*.\n', + 'id-classes': '\nReserved classes of identifiers\n*******************************\n\nCertain classes of identifiers (besides keywords) have special\nmeanings. These classes are identified by the patterns of leading and\ntrailing underscore characters:\n\n``_*``\n Not imported by ``from module import *``. The special identifier\n ``_`` is used in the interactive interpreter to store the result of\n the last evaluation; it is stored in the ``builtins`` module. When\n not in interactive mode, ``_`` has no special meaning and is not\n defined. See section *The import statement*.\n\n Note: The name ``_`` is often used in conjunction with\n internationalization; refer to the documentation for the\n ``gettext`` module for more information on this convention.\n\n``__*__``\n System-defined names. These names are defined by the interpreter\n and its implementation (including the standard library). Current\n system names are discussed in the *Special method names* section\n and elsewhere. More will likely be defined in future versions of\n Python. *Any* use of ``__*__`` names, in any context, that does\n not follow explicitly documented use, is subject to breakage\n without warning.\n\n``__*``\n Class-private names. Names in this category, when used within the\n context of a class definition, are re-written to use a mangled form\n to help avoid name clashes between "private" attributes of base and\n derived classes. See section *Identifiers (Names)*.\n', + 'identifiers': '\nIdentifiers and keywords\n************************\n\nIdentifiers (also referred to as *names*) are described by the\nfollowing lexical definitions.\n\nThe syntax of identifiers in Python is based on the Unicode standard\nannex UAX-31, with elaboration and changes as defined below; see also\n**PEP 3131** for further details.\n\nWithin the ASCII range (U+0001..U+007F), the valid characters for\nidentifiers are the same as in Python 2.x: the uppercase and lowercase\nletters ``A`` through ``Z``, the underscore ``_`` and, except for the\nfirst character, the digits ``0`` through ``9``.\n\nPython 3.0 introduces additional characters from outside the ASCII\nrange (see **PEP 3131**). For these characters, the classification\nuses the version of the Unicode Character Database as included in the\n``unicodedata`` module.\n\nIdentifiers are unlimited in length. Case is significant.\n\n identifier ::= id_start id_continue*\n id_start ::= \n id_continue ::= \n\nThe Unicode category codes mentioned above stand for:\n\n* *Lu* - uppercase letters\n\n* *Ll* - lowercase letters\n\n* *Lt* - titlecase letters\n\n* *Lm* - modifier letters\n\n* *Lo* - other letters\n\n* *Nl* - letter numbers\n\n* *Mn* - nonspacing marks\n\n* *Mc* - spacing combining marks\n\n* *Nd* - decimal numbers\n\n* *Pc* - connector punctuations\n\nAll identifiers are converted into the normal form NFC while parsing;\ncomparison of identifiers is based on NFC.\n\nA non-normative HTML file listing all valid identifier characters for\nUnicode 4.1 can be found at http://www.dcl.hpi.uni-\npotsdam.de/home/loewis/table-3131.html.\n\n\nKeywords\n========\n\nThe following identifiers are used as reserved words, or *keywords* of\nthe language, and cannot be used as ordinary identifiers. They must\nbe spelled exactly as written here:\n\n False class finally is return\n None continue for lambda try\n True def from nonlocal while\n and del global not with\n as elif if or yield\n assert else import pass\n break except in raise\n\n\nReserved classes of identifiers\n===============================\n\nCertain classes of identifiers (besides keywords) have special\nmeanings. These classes are identified by the patterns of leading and\ntrailing underscore characters:\n\n``_*``\n Not imported by ``from module import *``. The special identifier\n ``_`` is used in the interactive interpreter to store the result of\n the last evaluation; it is stored in the ``builtins`` module. When\n not in interactive mode, ``_`` has no special meaning and is not\n defined. See section *The import statement*.\n\n Note: The name ``_`` is often used in conjunction with\n internationalization; refer to the documentation for the\n ``gettext`` module for more information on this convention.\n\n``__*__``\n System-defined names. These names are defined by the interpreter\n and its implementation (including the standard library). Current\n system names are discussed in the *Special method names* section\n and elsewhere. More will likely be defined in future versions of\n Python. *Any* use of ``__*__`` names, in any context, that does\n not follow explicitly documented use, is subject to breakage\n without warning.\n\n``__*``\n Class-private names. Names in this category, when used within the\n context of a class definition, are re-written to use a mangled form\n to help avoid name clashes between "private" attributes of base and\n derived classes. See section *Identifiers (Names)*.\n', 'if': '\nThe ``if`` statement\n********************\n\nThe ``if`` statement is used for conditional execution:\n\n if_stmt ::= "if" expression ":" suite\n ( "elif" expression ":" suite )*\n ["else" ":" suite]\n\nIt selects exactly one of the suites by evaluating the expressions one\nby one until one is found to be true (see section *Boolean operations*\nfor the definition of true and false); then that suite is executed\n(and no other part of the ``if`` statement is executed or evaluated).\nIf all expressions are false, the suite of the ``else`` clause, if\npresent, is executed.\n', 'imaginary': '\nImaginary literals\n******************\n\nImaginary literals are described by the following lexical definitions:\n\n imagnumber ::= (floatnumber | intpart) ("j" | "J")\n\nAn imaginary literal yields a complex number with a real part of 0.0.\nComplex numbers are represented as a pair of floating point numbers\nand have the same restrictions on their range. To create a complex\nnumber with a nonzero real part, add a floating point number to it,\ne.g., ``(3+4j)``. Some examples of imaginary literals:\n\n 3.14j 10.j 10j .001j 1e100j 3.14e-10j\n', - 'import': '\nThe ``import`` statement\n************************\n\n import_stmt ::= "import" module ["as" name] ( "," module ["as" name] )*\n | "from" relative_module "import" identifier ["as" name]\n ( "," identifier ["as" name] )*\n | "from" relative_module "import" "(" identifier ["as" name]\n ( "," identifier ["as" name] )* [","] ")"\n | "from" module "import" "*"\n module ::= (identifier ".")* identifier\n relative_module ::= "."* module | "."+\n name ::= identifier\n\nImport statements are executed in two steps: (1) find a module, and\ninitialize it if necessary; (2) define a name or names in the local\nnamespace (of the scope where the ``import`` statement occurs). The\nstatement comes in two forms differing on whether it uses the ``from``\nkeyword. The first form (without ``from``) repeats these steps for\neach identifier in the list. The form with ``from`` performs step (1)\nonce, and then performs step (2) repeatedly. For a reference\nimplementation of step (1), see the ``importlib`` module.\n\nTo understand how step (1) occurs, one must first understand how\nPython handles hierarchical naming of modules. To help organize\nmodules and provide a hierarchy in naming, Python has a concept of\npackages. A package can contain other packages and modules while\nmodules cannot contain other modules or packages. From a file system\nperspective, packages are directories and modules are files. The\noriginal specification for packages is still available to read,\nalthough minor details have changed since the writing of that\ndocument.\n\nOnce the name of the module is known (unless otherwise specified, the\nterm "module" will refer to both packages and modules), searching for\nthe module or package can begin. The first place checked is\n``sys.modules``, the cache of all modules that have been imported\npreviously. If the module is found there then it is used in step (2)\nof import unless ``None`` is found in ``sys.modules``, in which case\n``ImportError`` is raised.\n\nIf the module is not found in the cache, then ``sys.meta_path`` is\nsearched (the specification for ``sys.meta_path`` can be found in\n**PEP 302**). The object is a list of *finder* objects which are\nqueried in order as to whether they know how to load the module by\ncalling their ``find_module()`` method with the name of the module. If\nthe module happens to be contained within a package (as denoted by the\nexistence of a dot in the name), then a second argument to\n``find_module()`` is given as the value of the ``__path__`` attribute\nfrom the parent package (everything up to the last dot in the name of\nthe module being imported). If a finder can find the module it returns\na *loader* (discussed later) or returns ``None``.\n\nIf none of the finders on ``sys.meta_path`` are able to find the\nmodule then some implicitly defined finders are queried.\nImplementations of Python vary in what implicit meta path finders are\ndefined. The one they all do define, though, is one that handles\n``sys.path_hooks``, ``sys.path_importer_cache``, and ``sys.path``.\n\nThe implicit finder searches for the requested module in the "paths"\nspecified in one of two places ("paths" do not have to be file system\npaths). If the module being imported is supposed to be contained\nwithin a package then the second argument passed to ``find_module()``,\n``__path__`` on the parent package, is used as the source of paths. If\nthe module is not contained in a package then ``sys.path`` is used as\nthe source of paths.\n\nOnce the source of paths is chosen it is iterated over to find a\nfinder that can handle that path. The dict at\n``sys.path_importer_cache`` caches finders for paths and is checked\nfor a finder. If the path does not have a finder cached then\n``sys.path_hooks`` is searched by calling each object in the list with\na single argument of the path, returning a finder or raises\n``ImportError``. If a finder is returned then it is cached in\n``sys.path_importer_cache`` and then used for that path entry. If no\nfinder can be found but the path exists then a value of ``None`` is\nstored in ``sys.path_importer_cache`` to signify that an implicit,\nfile-based finder that handles modules stored as individual files\nshould be used for that path. If the path does not exist then a finder\nwhich always returns ``None`` is placed in the cache for the path.\n\nIf no finder can find the module then ``ImportError`` is raised.\nOtherwise some finder returned a loader whose ``load_module()`` method\nis called with the name of the module to load (see **PEP 302** for the\noriginal definition of loaders). A loader has several responsibilities\nto perform on a module it loads. First, if the module already exists\nin ``sys.modules`` (a possibility if the loader is called outside of\nthe import machinery) then it is to use that module for initialization\nand not a new module. But if the module does not exist in\n``sys.modules`` then it is to be added to that dict before\ninitialization begins. If an error occurs during loading of the module\nand it was added to ``sys.modules`` it is to be removed from the dict.\nIf an error occurs but the module was already in ``sys.modules`` it is\nleft in the dict.\n\nThe loader must set several attributes on the module. ``__name__`` is\nto be set to the name of the module. ``__file__`` is to be the "path"\nto the file unless the module is built-in (and thus listed in\n``sys.builtin_module_names``) in which case the attribute is not set.\nIf what is being imported is a package then ``__path__`` is to be set\nto a list of paths to be searched when looking for modules and\npackages contained within the package being imported. ``__package__``\nis optional but should be set to the name of package that contains the\nmodule or package (the empty string is used for module not contained\nin a package). ``__loader__`` is also optional but should be set to\nthe loader object that is loading the module.\n\nIf an error occurs during loading then the loader raises\n``ImportError`` if some other exception is not already being\npropagated. Otherwise the loader returns the module that was loaded\nand initialized.\n\nWhen step (1) finishes without raising an exception, step (2) can\nbegin.\n\nThe first form of ``import`` statement binds the module name in the\nlocal namespace to the module object, and then goes on to import the\nnext identifier, if any. If the module name is followed by ``as``,\nthe name following ``as`` is used as the local name for the module.\n\nThe ``from`` form does not bind the module name: it goes through the\nlist of identifiers, looks each one of them up in the module found in\nstep (1), and binds the name in the local namespace to the object thus\nfound. As with the first form of ``import``, an alternate local name\ncan be supplied by specifying "``as`` localname". If a name is not\nfound, ``ImportError`` is raised. If the list of identifiers is\nreplaced by a star (``\'*\'``), all public names defined in the module\nare bound in the local namespace of the ``import`` statement..\n\nThe *public names* defined by a module are determined by checking the\nmodule\'s namespace for a variable named ``__all__``; if defined, it\nmust be a sequence of strings which are names defined or imported by\nthat module. The names given in ``__all__`` are all considered public\nand are required to exist. If ``__all__`` is not defined, the set of\npublic names includes all names found in the module\'s namespace which\ndo not begin with an underscore character (``\'_\'``). ``__all__``\nshould contain the entire public API. It is intended to avoid\naccidentally exporting items that are not part of the API (such as\nlibrary modules which were imported and used within the module).\n\nThe ``from`` form with ``*`` may only occur in a module scope. The\nwild card form of import --- ``import *`` --- is only allowed at the\nmodule level. Attempting to use it in class or function definitions\nwill raise a ``SyntaxError``.\n\nWhen specifying what module to import you do not have to specify the\nabsolute name of the module. When a module or package is contained\nwithin another package it is possible to make a relative import within\nthe same top package without having to mention the package name. By\nusing leading dots in the specified module or package after ``from``\nyou can specify how high to traverse up the current package hierarchy\nwithout specifying exact names. One leading dot means the current\npackage where the module making the import exists. Two dots means up\none package level. Three dots is up two levels, etc. So if you execute\n``from . import mod`` from a module in the ``pkg`` package then you\nwill end up importing ``pkg.mod``. If you execute ``from ..subpkg2\nimprt mod`` from within ``pkg.subpkg1`` you will import\n``pkg.subpkg2.mod``. The specification for relative imports is\ncontained within **PEP 328**.\n\n``importlib.import_module()`` is provided to support applications that\ndetermine which modules need to be loaded dynamically.\n\n\nFuture statements\n=================\n\nA *future statement* is a directive to the compiler that a particular\nmodule should be compiled using syntax or semantics that will be\navailable in a specified future release of Python. The future\nstatement is intended to ease migration to future versions of Python\nthat introduce incompatible changes to the language. It allows use of\nthe new features on a per-module basis before the release in which the\nfeature becomes standard.\n\n future_statement ::= "from" "__future__" "import" feature ["as" name]\n ("," feature ["as" name])*\n | "from" "__future__" "import" "(" feature ["as" name]\n ("," feature ["as" name])* [","] ")"\n feature ::= identifier\n name ::= identifier\n\nA future statement must appear near the top of the module. The only\nlines that can appear before a future statement are:\n\n* the module docstring (if any),\n\n* comments,\n\n* blank lines, and\n\n* other future statements.\n\nThe features recognized by Python 3.0 are ``absolute_import``,\n``division``, ``generators``, ``unicode_literals``,\n``print_function``, ``nested_scopes`` and ``with_statement``. They\nare all redundant because they are always enabled, and only kept for\nbackwards compatibility.\n\nA future statement is recognized and treated specially at compile\ntime: Changes to the semantics of core constructs are often\nimplemented by generating different code. It may even be the case\nthat a new feature introduces new incompatible syntax (such as a new\nreserved word), in which case the compiler may need to parse the\nmodule differently. Such decisions cannot be pushed off until\nruntime.\n\nFor any given release, the compiler knows which feature names have\nbeen defined, and raises a compile-time error if a future statement\ncontains a feature not known to it.\n\nThe direct runtime semantics are the same as for any import statement:\nthere is a standard module ``__future__``, described later, and it\nwill be imported in the usual way at the time the future statement is\nexecuted.\n\nThe interesting runtime semantics depend on the specific feature\nenabled by the future statement.\n\nNote that there is nothing special about the statement:\n\n import __future__ [as name]\n\nThat is not a future statement; it\'s an ordinary import statement with\nno special semantics or syntax restrictions.\n\nCode compiled by calls to the built-in functions ``exec()`` and\n``compile()`` that occur in a module ``M`` containing a future\nstatement will, by default, use the new syntax or semantics associated\nwith the future statement. This can be controlled by optional\narguments to ``compile()`` --- see the documentation of that function\nfor details.\n\nA future statement typed at an interactive interpreter prompt will\ntake effect for the rest of the interpreter session. If an\ninterpreter is started with the *-i* option, is passed a script name\nto execute, and the script includes a future statement, it will be in\neffect in the interactive session started after the script is\nexecuted.\n\nSee also:\n\n **PEP 236** - Back to the __future__\n The original proposal for the __future__ mechanism.\n', + 'import': '\nThe ``import`` statement\n************************\n\n import_stmt ::= "import" module ["as" name] ( "," module ["as" name] )*\n | "from" relative_module "import" identifier ["as" name]\n ( "," identifier ["as" name] )*\n | "from" relative_module "import" "(" identifier ["as" name]\n ( "," identifier ["as" name] )* [","] ")"\n | "from" module "import" "*"\n module ::= (identifier ".")* identifier\n relative_module ::= "."* module | "."+\n name ::= identifier\n\nImport statements are executed in two steps: (1) find a module, and\ninitialize it if necessary; (2) define a name or names in the local\nnamespace (of the scope where the ``import`` statement occurs). The\nstatement comes in two forms differing on whether it uses the ``from``\nkeyword. The first form (without ``from``) repeats these steps for\neach identifier in the list. The form with ``from`` performs step (1)\nonce, and then performs step (2) repeatedly. For a reference\nimplementation of step (1), see the ``importlib`` module.\n\nTo understand how step (1) occurs, one must first understand how\nPython handles hierarchical naming of modules. To help organize\nmodules and provide a hierarchy in naming, Python has a concept of\npackages. A package can contain other packages and modules while\nmodules cannot contain other modules or packages. From a file system\nperspective, packages are directories and modules are files. The\noriginal specification for packages is still available to read,\nalthough minor details have changed since the writing of that\ndocument.\n\nOnce the name of the module is known (unless otherwise specified, the\nterm "module" will refer to both packages and modules), searching for\nthe module or package can begin. The first place checked is\n``sys.modules``, the cache of all modules that have been imported\npreviously. If the module is found there then it is used in step (2)\nof import unless ``None`` is found in ``sys.modules``, in which case\n``ImportError`` is raised.\n\nIf the module is not found in the cache, then ``sys.meta_path`` is\nsearched (the specification for ``sys.meta_path`` can be found in\n**PEP 302**). The object is a list of *finder* objects which are\nqueried in order as to whether they know how to load the module by\ncalling their ``find_module()`` method with the name of the module. If\nthe module happens to be contained within a package (as denoted by the\nexistence of a dot in the name), then a second argument to\n``find_module()`` is given as the value of the ``__path__`` attribute\nfrom the parent package (everything up to the last dot in the name of\nthe module being imported). If a finder can find the module it returns\na *loader* (discussed later) or returns ``None``.\n\nIf none of the finders on ``sys.meta_path`` are able to find the\nmodule then some implicitly defined finders are queried.\nImplementations of Python vary in what implicit meta path finders are\ndefined. The one they all do define, though, is one that handles\n``sys.path_hooks``, ``sys.path_importer_cache``, and ``sys.path``.\n\nThe implicit finder searches for the requested module in the "paths"\nspecified in one of two places ("paths" do not have to be file system\npaths). If the module being imported is supposed to be contained\nwithin a package then the second argument passed to ``find_module()``,\n``__path__`` on the parent package, is used as the source of paths. If\nthe module is not contained in a package then ``sys.path`` is used as\nthe source of paths.\n\nOnce the source of paths is chosen it is iterated over to find a\nfinder that can handle that path. The dict at\n``sys.path_importer_cache`` caches finders for paths and is checked\nfor a finder. If the path does not have a finder cached then\n``sys.path_hooks`` is searched by calling each object in the list with\na single argument of the path, returning a finder or raises\n``ImportError``. If a finder is returned then it is cached in\n``sys.path_importer_cache`` and then used for that path entry. If no\nfinder can be found but the path exists then a value of ``None`` is\nstored in ``sys.path_importer_cache`` to signify that an implicit,\nfile-based finder that handles modules stored as individual files\nshould be used for that path. If the path does not exist then a finder\nwhich always returns ``None`` is placed in the cache for the path.\n\nIf no finder can find the module then ``ImportError`` is raised.\nOtherwise some finder returned a loader whose ``load_module()`` method\nis called with the name of the module to load (see **PEP 302** for the\noriginal definition of loaders). A loader has several responsibilities\nto perform on a module it loads. First, if the module already exists\nin ``sys.modules`` (a possibility if the loader is called outside of\nthe import machinery) then it is to use that module for initialization\nand not a new module. But if the module does not exist in\n``sys.modules`` then it is to be added to that dict before\ninitialization begins. If an error occurs during loading of the module\nand it was added to ``sys.modules`` it is to be removed from the dict.\nIf an error occurs but the module was already in ``sys.modules`` it is\nleft in the dict.\n\nThe loader must set several attributes on the module. ``__name__`` is\nto be set to the name of the module. ``__file__`` is to be the "path"\nto the file unless the module is built-in (and thus listed in\n``sys.builtin_module_names``) in which case the attribute is not set.\nIf what is being imported is a package then ``__path__`` is to be set\nto a list of paths to be searched when looking for modules and\npackages contained within the package being imported. ``__package__``\nis optional but should be set to the name of package that contains the\nmodule or package (the empty string is used for module not contained\nin a package). ``__loader__`` is also optional but should be set to\nthe loader object that is loading the module.\n\nIf an error occurs during loading then the loader raises\n``ImportError`` if some other exception is not already being\npropagated. Otherwise the loader returns the module that was loaded\nand initialized.\n\nWhen step (1) finishes without raising an exception, step (2) can\nbegin.\n\nThe first form of ``import`` statement binds the module name in the\nlocal namespace to the module object, and then goes on to import the\nnext identifier, if any. If the module name is followed by ``as``,\nthe name following ``as`` is used as the local name for the module.\n\nThe ``from`` form does not bind the module name: it goes through the\nlist of identifiers, looks each one of them up in the module found in\nstep (1), and binds the name in the local namespace to the object thus\nfound. As with the first form of ``import``, an alternate local name\ncan be supplied by specifying "``as`` localname". If a name is not\nfound, ``ImportError`` is raised. If the list of identifiers is\nreplaced by a star (``\'*\'``), all public names defined in the module\nare bound in the local namespace of the ``import`` statement..\n\nThe *public names* defined by a module are determined by checking the\nmodule\'s namespace for a variable named ``__all__``; if defined, it\nmust be a sequence of strings which are names defined or imported by\nthat module. The names given in ``__all__`` are all considered public\nand are required to exist. If ``__all__`` is not defined, the set of\npublic names includes all names found in the module\'s namespace which\ndo not begin with an underscore character (``\'_\'``). ``__all__``\nshould contain the entire public API. It is intended to avoid\naccidentally exporting items that are not part of the API (such as\nlibrary modules which were imported and used within the module).\n\nThe ``from`` form with ``*`` may only occur in a module scope. The\nwild card form of import --- ``import *`` --- is only allowed at the\nmodule level. Attempting to use it in class or function definitions\nwill raise a ``SyntaxError``.\n\nWhen specifying what module to import you do not have to specify the\nabsolute name of the module. When a module or package is contained\nwithin another package it is possible to make a relative import within\nthe same top package without having to mention the package name. By\nusing leading dots in the specified module or package after ``from``\nyou can specify how high to traverse up the current package hierarchy\nwithout specifying exact names. One leading dot means the current\npackage where the module making the import exists. Two dots means up\none package level. Three dots is up two levels, etc. So if you execute\n``from . import mod`` from a module in the ``pkg`` package then you\nwill end up importing ``pkg.mod``. If you execute ``from ..subpkg2\nimport mod`` from within ``pkg.subpkg1`` you will import\n``pkg.subpkg2.mod``. The specification for relative imports is\ncontained within **PEP 328**.\n\n``importlib.import_module()`` is provided to support applications that\ndetermine which modules need to be loaded dynamically.\n\n\nFuture statements\n=================\n\nA *future statement* is a directive to the compiler that a particular\nmodule should be compiled using syntax or semantics that will be\navailable in a specified future release of Python. The future\nstatement is intended to ease migration to future versions of Python\nthat introduce incompatible changes to the language. It allows use of\nthe new features on a per-module basis before the release in which the\nfeature becomes standard.\n\n future_statement ::= "from" "__future__" "import" feature ["as" name]\n ("," feature ["as" name])*\n | "from" "__future__" "import" "(" feature ["as" name]\n ("," feature ["as" name])* [","] ")"\n feature ::= identifier\n name ::= identifier\n\nA future statement must appear near the top of the module. The only\nlines that can appear before a future statement are:\n\n* the module docstring (if any),\n\n* comments,\n\n* blank lines, and\n\n* other future statements.\n\nThe features recognized by Python 3.0 are ``absolute_import``,\n``division``, ``generators``, ``unicode_literals``,\n``print_function``, ``nested_scopes`` and ``with_statement``. They\nare all redundant because they are always enabled, and only kept for\nbackwards compatibility.\n\nA future statement is recognized and treated specially at compile\ntime: Changes to the semantics of core constructs are often\nimplemented by generating different code. It may even be the case\nthat a new feature introduces new incompatible syntax (such as a new\nreserved word), in which case the compiler may need to parse the\nmodule differently. Such decisions cannot be pushed off until\nruntime.\n\nFor any given release, the compiler knows which feature names have\nbeen defined, and raises a compile-time error if a future statement\ncontains a feature not known to it.\n\nThe direct runtime semantics are the same as for any import statement:\nthere is a standard module ``__future__``, described later, and it\nwill be imported in the usual way at the time the future statement is\nexecuted.\n\nThe interesting runtime semantics depend on the specific feature\nenabled by the future statement.\n\nNote that there is nothing special about the statement:\n\n import __future__ [as name]\n\nThat is not a future statement; it\'s an ordinary import statement with\nno special semantics or syntax restrictions.\n\nCode compiled by calls to the built-in functions ``exec()`` and\n``compile()`` that occur in a module ``M`` containing a future\nstatement will, by default, use the new syntax or semantics associated\nwith the future statement. This can be controlled by optional\narguments to ``compile()`` --- see the documentation of that function\nfor details.\n\nA future statement typed at an interactive interpreter prompt will\ntake effect for the rest of the interpreter session. If an\ninterpreter is started with the *-i* option, is passed a script name\nto execute, and the script includes a future statement, it will be in\neffect in the interactive session started after the script is\nexecuted.\n\nSee also:\n\n **PEP 236** - Back to the __future__\n The original proposal for the __future__ mechanism.\n', 'in': '\nComparisons\n***********\n\nUnlike C, all comparison operations in Python have the same priority,\nwhich is lower than that of any arithmetic, shifting or bitwise\noperation. Also unlike C, expressions like ``a < b < c`` have the\ninterpretation that is conventional in mathematics:\n\n comparison ::= or_expr ( comp_operator or_expr )*\n comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="\n | "is" ["not"] | ["not"] "in"\n\nComparisons yield boolean values: ``True`` or ``False``.\n\nComparisons can be chained arbitrarily, e.g., ``x < y <= z`` is\nequivalent to ``x < y and y <= z``, except that ``y`` is evaluated\nonly once (but in both cases ``z`` is not evaluated at all when ``x <\ny`` is found to be false).\n\nFormally, if *a*, *b*, *c*, ..., *y*, *z* are expressions and *op1*,\n*op2*, ..., *opN* are comparison operators, then ``a op1 b op2 c ... y\nopN z`` is equivalent to ``a op1 b and b op2 c and ... y opN z``,\nexcept that each expression is evaluated at most once.\n\nNote that ``a op1 b op2 c`` doesn\'t imply any kind of comparison\nbetween *a* and *c*, so that, e.g., ``x < y > z`` is perfectly legal\n(though perhaps not pretty).\n\nThe operators ``<``, ``>``, ``==``, ``>=``, ``<=``, and ``!=`` compare\nthe values of two objects. The objects need not have the same type.\nIf both are numbers, they are converted to a common type. Otherwise,\nthe ``==`` and ``!=`` operators *always* consider objects of different\ntypes to be unequal, while the ``<``, ``>``, ``>=`` and ``<=``\noperators raise a ``TypeError`` when comparing objects of different\ntypes that do not implement these operators for the given pair of\ntypes. You can control comparison behavior of objects of non-built-in\ntypes by defining rich comparison methods like ``__gt__()``, described\nin section *Basic customization*.\n\nComparison of objects of the same type depends on the type:\n\n* Numbers are compared arithmetically.\n\n* The values ``float(\'NaN\')`` and ``Decimal(\'NaN\')`` are special. The\n are identical to themselves, ``x is x`` but are not equal to\n themselves, ``x != x``. Additionally, comparing any value to a\n not-a-number value will return ``False``. For example, both ``3 <\n float(\'NaN\')`` and ``float(\'NaN\') < 3`` will return ``False``.\n\n* Bytes objects are compared lexicographically using the numeric\n values of their elements.\n\n* Strings are compared lexicographically using the numeric equivalents\n (the result of the built-in function ``ord()``) of their characters.\n [3] String and bytes object can\'t be compared!\n\n* Tuples and lists are compared lexicographically using comparison of\n corresponding elements. This means that to compare equal, each\n element must compare equal and the two sequences must be of the same\n type and have the same length.\n\n If not equal, the sequences are ordered the same as their first\n differing elements. For example, ``[1,2,x] <= [1,2,y]`` has the\n same value as ``x <= y``. If the corresponding element does not\n exist, the shorter sequence is ordered first (for example, ``[1,2] <\n [1,2,3]``).\n\n* Mappings (dictionaries) compare equal if and only if they have the\n same ``(key, value)`` pairs. Order comparisons ``(\'<\', \'<=\', \'>=\',\n \'>\')`` raise ``TypeError``.\n\n* Sets and frozensets define comparison operators to mean subset and\n superset tests. Those relations do not define total orderings (the\n two sets ``{1,2}`` and {2,3} are not equal, nor subsets of one\n another, nor supersets of one another). Accordingly, sets are not\n appropriate arguments for functions which depend on total ordering.\n For example, ``min()``, ``max()``, and ``sorted()`` produce\n undefined results given a list of sets as inputs.\n\n* Most other objects of built-in types compare unequal unless they are\n the same object; the choice whether one object is considered smaller\n or larger than another one is made arbitrarily but consistently\n within one execution of a program.\n\nComparison of objects of the differing types depends on whether either\nof the types provide explicit support for the comparison. Most\nnumeric types can be compared with one another, but comparisons of\n``float`` and ``Decimal`` are not supported to avoid the inevitable\nconfusion arising from representation issues such as ``float(\'1.1\')``\nbeing inexactly represented and therefore not exactly equal to\n``Decimal(\'1.1\')`` which is. When cross-type comparison is not\nsupported, the comparison method returns ``NotImplemented``. This can\ncreate the illusion of non-transitivity between supported cross-type\ncomparisons and unsupported comparisons. For example, ``Decimal(2) ==\n2`` and *2 == float(2)`* but ``Decimal(2) != float(2)``.\n\nThe operators ``in`` and ``not in`` test for membership. ``x in s``\nevaluates to true if *x* is a member of *s*, and false otherwise. ``x\nnot in s`` returns the negation of ``x in s``. All built-in sequences\nand set types support this as well as dictionary, for which ``in``\ntests whether a the dictionary has a given key. For container types\nsuch as list, tuple, set, frozenset, dict, or collections.deque, the\nexpression ``x in y`` is equivalent to ``any(x is e or x == e for e in\ny)``.\n\nFor the string and bytes types, ``x in y`` is true if and only if *x*\nis a substring of *y*. An equivalent test is ``y.find(x) != -1``.\nEmpty strings are always considered to be a substring of any other\nstring, so ``"" in "abc"`` will return ``True``.\n\nFor user-defined classes which define the ``__contains__()`` method,\n``x in y`` is true if and only if ``y.__contains__(x)`` is true.\n\nFor user-defined classes which do not define ``__contains__()`` but do\ndefine ``__iter__()``, ``x in y`` is true if some value ``z`` with ``x\n== z`` is produced while iterating over ``y``. If an exception is\nraised during the iteration, it is as if ``in`` raised that exception.\n\nLastly, the old-style iteration protocol is tried: if a class defines\n``__getitem__()``, ``x in y`` is true if and only if there is a non-\nnegative integer index *i* such that ``x == y[i]``, and all lower\ninteger indices do not raise ``IndexError`` exception. (If any other\nexception is raised, it is as if ``in`` raised that exception).\n\nThe operator ``not in`` is defined to have the inverse true value of\n``in``.\n\nThe operators ``is`` and ``is not`` test for object identity: ``x is\ny`` is true if and only if *x* and *y* are the same object. ``x is\nnot y`` yields the inverse truth value. [4]\n', 'integers': '\nInteger literals\n****************\n\nInteger literals are described by the following lexical definitions:\n\n integer ::= decimalinteger | octinteger | hexinteger | bininteger\n decimalinteger ::= nonzerodigit digit* | "0"+\n nonzerodigit ::= "1"..."9"\n digit ::= "0"..."9"\n octinteger ::= "0" ("o" | "O") octdigit+\n hexinteger ::= "0" ("x" | "X") hexdigit+\n bininteger ::= "0" ("b" | "B") bindigit+\n octdigit ::= "0"..."7"\n hexdigit ::= digit | "a"..."f" | "A"..."F"\n bindigit ::= "0" | "1"\n\nThere is no limit for the length of integer literals apart from what\ncan be stored in available memory.\n\nNote that leading zeros in a non-zero decimal number are not allowed.\nThis is for disambiguation with C-style octal literals, which Python\nused before version 3.0.\n\nSome examples of integer literals:\n\n 7 2147483647 0o177 0b100110111\n 3 79228162514264337593543950336 0o377 0x100000000\n 79228162514264337593543950336 0xdeadbeef\n', 'lambda': '\nLambdas\n*******\n\n lambda_form ::= "lambda" [parameter_list]: expression\n lambda_form_nocond ::= "lambda" [parameter_list]: expression_nocond\n\nLambda forms (lambda expressions) have the same syntactic position as\nexpressions. They are a shorthand to create anonymous functions; the\nexpression ``lambda arguments: expression`` yields a function object.\nThe unnamed object behaves like a function object defined with\n\n def (arguments):\n return expression\n\nSee section *Function definitions* for the syntax of parameter lists.\nNote that functions created with lambda forms cannot contain\nstatements or annotations.\n', 'lists': '\nList displays\n*************\n\nA list display is a possibly empty series of expressions enclosed in\nsquare brackets:\n\n list_display ::= "[" [expression_list | comprehension] "]"\n\nA list display yields a new list object, the contents being specified\nby either a list of expressions or a comprehension. When a comma-\nseparated list of expressions is supplied, its elements are evaluated\nfrom left to right and placed into the list object in that order.\nWhen a comprehension is supplied, the list is constructed from the\nelements resulting from the comprehension.\n', 'naming': "\nNaming and binding\n******************\n\n*Names* refer to objects. Names are introduced by name binding\noperations. Each occurrence of a name in the program text refers to\nthe *binding* of that name established in the innermost function block\ncontaining the use.\n\nA *block* is a piece of Python program text that is executed as a\nunit. The following are blocks: a module, a function body, and a class\ndefinition. Each command typed interactively is a block. A script\nfile (a file given as standard input to the interpreter or specified\non the interpreter command line the first argument) is a code block.\nA script command (a command specified on the interpreter command line\nwith the '**-c**' option) is a code block. The string argument passed\nto the built-in functions ``eval()`` and ``exec()`` is a code block.\n\nA code block is executed in an *execution frame*. A frame contains\nsome administrative information (used for debugging) and determines\nwhere and how execution continues after the code block's execution has\ncompleted.\n\nA *scope* defines the visibility of a name within a block. If a local\nvariable is defined in a block, its scope includes that block. If the\ndefinition occurs in a function block, the scope extends to any blocks\ncontained within the defining one, unless a contained block introduces\na different binding for the name. The scope of names defined in a\nclass block is limited to the class block; it does not extend to the\ncode blocks of methods -- this includes comprehensions and generator\nexpressions since they are implemented using a function scope. This\nmeans that the following will fail:\n\n class A:\n a = 42\n b = list(a + i for i in range(10))\n\nWhen a name is used in a code block, it is resolved using the nearest\nenclosing scope. The set of all such scopes visible to a code block\nis called the block's *environment*.\n\nIf a name is bound in a block, it is a local variable of that block,\nunless declared as ``nonlocal``. If a name is bound at the module\nlevel, it is a global variable. (The variables of the module code\nblock are local and global.) If a variable is used in a code block\nbut not defined there, it is a *free variable*.\n\nWhen a name is not found at all, a ``NameError`` exception is raised.\nIf the name refers to a local variable that has not been bound, a\n``UnboundLocalError`` exception is raised. ``UnboundLocalError`` is a\nsubclass of ``NameError``.\n\nThe following constructs bind names: formal parameters to functions,\n``import`` statements, class and function definitions (these bind the\nclass or function name in the defining block), and targets that are\nidentifiers if occurring in an assignment, ``for`` loop header, or\nafter ``as`` in a ``with`` statement or :keyword.`except` clause. The\n``import`` statement of the form ``from ... import *`` binds all names\ndefined in the imported module, except those beginning with an\nunderscore. This form may only be used at the module level.\n\nA target occurring in a ``del`` statement is also considered bound for\nthis purpose (though the actual semantics are to unbind the name). It\nis illegal to unbind a name that is referenced by an enclosing scope;\nthe compiler will report a ``SyntaxError``.\n\nEach assignment or import statement occurs within a block defined by a\nclass or function definition or at the module level (the top-level\ncode block).\n\nIf a name binding operation occurs anywhere within a code block, all\nuses of the name within the block are treated as references to the\ncurrent block. This can lead to errors when a name is used within a\nblock before it is bound. This rule is subtle. Python lacks\ndeclarations and allows name binding operations to occur anywhere\nwithin a code block. The local variables of a code block can be\ndetermined by scanning the entire text of the block for name binding\noperations.\n\nIf the ``global`` statement occurs within a block, all uses of the\nname specified in the statement refer to the binding of that name in\nthe top-level namespace. Names are resolved in the top-level\nnamespace by searching the global namespace, i.e. the namespace of the\nmodule containing the code block, and the builtins namespace, the\nnamespace of the module ``builtins``. The global namespace is\nsearched first. If the name is not found there, the builtins\nnamespace is searched. The global statement must precede all uses of\nthe name.\n\nThe builtins namespace associated with the execution of a code block\nis actually found by looking up the name ``__builtins__`` in its\nglobal namespace; this should be a dictionary or a module (in the\nlatter case the module's dictionary is used). By default, when in the\n``__main__`` module, ``__builtins__`` is the built-in module\n``builtins``; when in any other module, ``__builtins__`` is an alias\nfor the dictionary of the ``builtins`` module itself.\n``__builtins__`` can be set to a user-created dictionary to create a\nweak form of restricted execution.\n\n**CPython implementation detail:** Users should not touch\n``__builtins__``; it is strictly an implementation detail. Users\nwanting to override values in the builtins namespace should ``import``\nthe ``builtins`` module and modify its attributes appropriately.\n\nThe namespace for a module is automatically created the first time a\nmodule is imported. The main module for a script is always called\n``__main__``.\n\nThe global statement has the same scope as a name binding operation in\nthe same block. If the nearest enclosing scope for a free variable\ncontains a global statement, the free variable is treated as a global.\n\nA class definition is an executable statement that may use and define\nnames. These references follow the normal rules for name resolution.\nThe namespace of the class definition becomes the attribute dictionary\nof the class. Names defined at the class scope are not visible in\nmethods.\n\n\nInteraction with dynamic features\n=================================\n\nThere are several cases where Python statements are illegal when used\nin conjunction with nested scopes that contain free variables.\n\nIf a variable is referenced in an enclosing scope, it is illegal to\ndelete the name. An error will be reported at compile time.\n\nIf the wild card form of import --- ``import *`` --- is used in a\nfunction and the function contains or is a nested block with free\nvariables, the compiler will raise a ``SyntaxError``.\n\nThe ``eval()`` and ``exec()`` functions do not have access to the full\nenvironment for resolving names. Names may be resolved in the local\nand global namespaces of the caller. Free variables are not resolved\nin the nearest enclosing namespace, but in the global namespace. [1]\nThe ``exec()`` and ``eval()`` functions have optional arguments to\noverride the global and local namespace. If only one namespace is\nspecified, it is used for both.\n", + 'nonlocal': '\nThe ``nonlocal`` statement\n**************************\n\n nonlocal_stmt ::= "nonlocal" identifier ("," identifier)*\n\nThe ``nonlocal`` statement causes the listed identifiers to refer to\npreviously bound variables in the nearest enclosing scope. This is\nimportant because the default behavior for binding is to search the\nlocal namespace first. The statement allows encapsulated code to\nrebind variables outside of the local scope besides the global\n(module) scope.\n\nNames listed in a ``nonlocal`` statement, unlike to those listed in a\n``global`` statement, must refer to pre-existing bindings in an\nenclosing scope (the scope in which a new binding should be created\ncannot be determined unambiguously).\n\nNames listed in a ``nonlocal`` statement must not collide with pre-\nexisting bindings in the local scope.\n\nSee also:\n\n **PEP 3104** - Access to Names in Outer Scopes\n The specification for the ``nonlocal`` statement.\n\n-[ Footnotes ]-\n\n[1] It may occur within an ``except`` or ``else`` clause. The\n restriction on occurring in the ``try`` clause is implementor\'s\n laziness and will eventually be lifted.\n', 'numbers': "\nNumeric literals\n****************\n\nThere are three types of numeric literals: integers, floating point\nnumbers, and imaginary numbers. There are no complex literals\n(complex numbers can be formed by adding a real number and an\nimaginary number).\n\nNote that numeric literals do not include a sign; a phrase like ``-1``\nis actually an expression composed of the unary operator '``-``' and\nthe literal ``1``.\n", 'numeric-types': "\nEmulating numeric types\n***********************\n\nThe following methods can be defined to emulate numeric objects.\nMethods corresponding to operations that are not supported by the\nparticular kind of number implemented (e.g., bitwise operations for\nnon-integral numbers) should be left undefined.\n\nobject.__add__(self, other)\nobject.__sub__(self, other)\nobject.__mul__(self, other)\nobject.__truediv__(self, other)\nobject.__floordiv__(self, other)\nobject.__mod__(self, other)\nobject.__divmod__(self, other)\nobject.__pow__(self, other[, modulo])\nobject.__lshift__(self, other)\nobject.__rshift__(self, other)\nobject.__and__(self, other)\nobject.__xor__(self, other)\nobject.__or__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``/``, ``//``, ``%``,\n ``divmod()``, ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``,\n ``|``). For instance, to evaluate the expression ``x + y``, where\n *x* is an instance of a class that has an ``__add__()`` method,\n ``x.__add__(y)`` is called. The ``__divmod__()`` method should be\n the equivalent to using ``__floordiv__()`` and ``__mod__()``; it\n should not be related to ``__truediv__()``. Note that\n ``__pow__()`` should be defined to accept an optional third\n argument if the ternary version of the built-in ``pow()`` function\n is to be supported.\n\n If one of those methods does not support the operation with the\n supplied arguments, it should return ``NotImplemented``.\n\nobject.__radd__(self, other)\nobject.__rsub__(self, other)\nobject.__rmul__(self, other)\nobject.__rtruediv__(self, other)\nobject.__rfloordiv__(self, other)\nobject.__rmod__(self, other)\nobject.__rdivmod__(self, other)\nobject.__rpow__(self, other)\nobject.__rlshift__(self, other)\nobject.__rrshift__(self, other)\nobject.__rand__(self, other)\nobject.__rxor__(self, other)\nobject.__ror__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``/``, ``//``, ``%``,\n ``divmod()``, ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``,\n ``|``) with reflected (swapped) operands. These functions are only\n called if the left operand does not support the corresponding\n operation and the operands are of different types. [2] For\n instance, to evaluate the expression ``x - y``, where *y* is an\n instance of a class that has an ``__rsub__()`` method,\n ``y.__rsub__(x)`` is called if ``x.__sub__(y)`` returns\n *NotImplemented*.\n\n Note that ternary ``pow()`` will not try calling ``__rpow__()``\n (the coercion rules would become too complicated).\n\n Note: If the right operand's type is a subclass of the left operand's\n type and that subclass provides the reflected method for the\n operation, this method will be called before the left operand's\n non-reflected method. This behavior allows subclasses to\n override their ancestors' operations.\n\nobject.__iadd__(self, other)\nobject.__isub__(self, other)\nobject.__imul__(self, other)\nobject.__itruediv__(self, other)\nobject.__ifloordiv__(self, other)\nobject.__imod__(self, other)\nobject.__ipow__(self, other[, modulo])\nobject.__ilshift__(self, other)\nobject.__irshift__(self, other)\nobject.__iand__(self, other)\nobject.__ixor__(self, other)\nobject.__ior__(self, other)\n\n These methods are called to implement the augmented arithmetic\n assignments (``+=``, ``-=``, ``*=``, ``/=``, ``//=``, ``%=``,\n ``**=``, ``<<=``, ``>>=``, ``&=``, ``^=``, ``|=``). These methods\n should attempt to do the operation in-place (modifying *self*) and\n return the result (which could be, but does not have to be,\n *self*). If a specific method is not defined, the augmented\n assignment falls back to the normal methods. For instance, to\n execute the statement ``x += y``, where *x* is an instance of a\n class that has an ``__iadd__()`` method, ``x.__iadd__(y)`` is\n called. If *x* is an instance of a class that does not define a\n ``__iadd__()`` method, ``x.__add__(y)`` and ``y.__radd__(x)`` are\n considered, as with the evaluation of ``x + y``.\n\nobject.__neg__(self)\nobject.__pos__(self)\nobject.__abs__(self)\nobject.__invert__(self)\n\n Called to implement the unary arithmetic operations (``-``, ``+``,\n ``abs()`` and ``~``).\n\nobject.__complex__(self)\nobject.__int__(self)\nobject.__float__(self)\nobject.__round__(self[, n])\n\n Called to implement the built-in functions ``complex()``,\n ``int()``, ``float()`` and ``round()``. Should return a value of\n the appropriate type.\n\nobject.__index__(self)\n\n Called to implement ``operator.index()``. Also called whenever\n Python needs an integer object (such as in slicing, or in the\n built-in ``bin()``, ``hex()`` and ``oct()`` functions). Must return\n an integer.\n", 'objects': '\nObjects, values and types\n*************************\n\n*Objects* are Python\'s abstraction for data. All data in a Python\nprogram is represented by objects or by relations between objects. (In\na sense, and in conformance to Von Neumann\'s model of a "stored\nprogram computer," code is also represented by objects.)\n\nEvery object has an identity, a type and a value. An object\'s\n*identity* never changes once it has been created; you may think of it\nas the object\'s address in memory. The \'``is``\' operator compares the\nidentity of two objects; the ``id()`` function returns an integer\nrepresenting its identity (currently implemented as its address). An\nobject\'s *type* is also unchangeable. [1] An object\'s type determines\nthe operations that the object supports (e.g., "does it have a\nlength?") and also defines the possible values for objects of that\ntype. The ``type()`` function returns an object\'s type (which is an\nobject itself). The *value* of some objects can change. Objects\nwhose value can change are said to be *mutable*; objects whose value\nis unchangeable once they are created are called *immutable*. (The\nvalue of an immutable container object that contains a reference to a\nmutable object can change when the latter\'s value is changed; however\nthe container is still considered immutable, because the collection of\nobjects it contains cannot be changed. So, immutability is not\nstrictly the same as having an unchangeable value, it is more subtle.)\nAn object\'s mutability is determined by its type; for instance,\nnumbers, strings and tuples are immutable, while dictionaries and\nlists are mutable.\n\nObjects are never explicitly destroyed; however, when they become\nunreachable they may be garbage-collected. An implementation is\nallowed to postpone garbage collection or omit it altogether --- it is\na matter of implementation quality how garbage collection is\nimplemented, as long as no objects are collected that are still\nreachable.\n\n**CPython implementation detail:** CPython currently uses a reference-\ncounting scheme with (optional) delayed detection of cyclically linked\ngarbage, which collects most objects as soon as they become\nunreachable, but is not guaranteed to collect garbage containing\ncircular references. See the documentation of the ``gc`` module for\ninformation on controlling the collection of cyclic garbage. Other\nimplementations act differently and CPython may change.\n\nNote that the use of the implementation\'s tracing or debugging\nfacilities may keep objects alive that would normally be collectable.\nAlso note that catching an exception with a \'``try``...``except``\'\nstatement may keep objects alive.\n\nSome objects contain references to "external" resources such as open\nfiles or windows. It is understood that these resources are freed\nwhen the object is garbage-collected, but since garbage collection is\nnot guaranteed to happen, such objects also provide an explicit way to\nrelease the external resource, usually a ``close()`` method. Programs\nare strongly recommended to explicitly close such objects. The\n\'``try``...``finally``\' statement and the \'``with``\' statement provide\nconvenient ways to do this.\n\nSome objects contain references to other objects; these are called\n*containers*. Examples of containers are tuples, lists and\ndictionaries. The references are part of a container\'s value. In\nmost cases, when we talk about the value of a container, we imply the\nvalues, not the identities of the contained objects; however, when we\ntalk about the mutability of a container, only the identities of the\nimmediately contained objects are implied. So, if an immutable\ncontainer (like a tuple) contains a reference to a mutable object, its\nvalue changes if that mutable object is changed.\n\nTypes affect almost all aspects of object behavior. Even the\nimportance of object identity is affected in some sense: for immutable\ntypes, operations that compute new values may actually return a\nreference to any existing object with the same type and value, while\nfor mutable objects this is not allowed. E.g., after ``a = 1; b =\n1``, ``a`` and ``b`` may or may not refer to the same object with the\nvalue one, depending on the implementation, but after ``c = []; d =\n[]``, ``c`` and ``d`` are guaranteed to refer to two different,\nunique, newly created empty lists. (Note that ``c = d = []`` assigns\nthe same object to both ``c`` and ``d``.)\n', @@ -59,19 +60,19 @@ 'slicings': '\nSlicings\n********\n\nA slicing selects a range of items in a sequence object (e.g., a\nstring, tuple or list). Slicings may be used as expressions or as\ntargets in assignment or ``del`` statements. The syntax for a\nslicing:\n\n slicing ::= primary "[" slice_list "]"\n slice_list ::= slice_item ("," slice_item)* [","]\n slice_item ::= expression | proper_slice\n proper_slice ::= [lower_bound] ":" [upper_bound] [ ":" [stride] ]\n lower_bound ::= expression\n upper_bound ::= expression\n stride ::= expression\n\nThere is ambiguity in the formal syntax here: anything that looks like\nan expression list also looks like a slice list, so any subscription\ncan be interpreted as a slicing. Rather than further complicating the\nsyntax, this is disambiguated by defining that in this case the\ninterpretation as a subscription takes priority over the\ninterpretation as a slicing (this is the case if the slice list\ncontains no proper slice).\n\nThe semantics for a slicing are as follows. The primary must evaluate\nto a mapping object, and it is indexed (using the same\n``__getitem__()`` method as normal subscription) with a key that is\nconstructed from the slice list, as follows. If the slice list\ncontains at least one comma, the key is a tuple containing the\nconversion of the slice items; otherwise, the conversion of the lone\nslice item is the key. The conversion of a slice item that is an\nexpression is that expression. The conversion of a proper slice is a\nslice object (see section *The standard type hierarchy*) whose\n``start``, ``stop`` and ``step`` attributes are the values of the\nexpressions given as lower bound, upper bound and stride,\nrespectively, substituting ``None`` for missing expressions.\n', 'specialattrs': "\nSpecial Attributes\n******************\n\nThe implementation adds a few special read-only attributes to several\nobject types, where they are relevant. Some of these are not reported\nby the ``dir()`` built-in function.\n\nobject.__dict__\n\n A dictionary or other mapping object used to store an object's\n (writable) attributes.\n\ninstance.__class__\n\n The class to which a class instance belongs.\n\nclass.__bases__\n\n The tuple of base classes of a class object.\n\nclass.__name__\n\n The name of the class or type.\n\nThe following attributes are only supported by *new-style class*es.\n\nclass.__mro__\n\n This attribute is a tuple of classes that are considered when\n looking for base classes during method resolution.\n\nclass.mro()\n\n This method can be overridden by a metaclass to customize the\n method resolution order for its instances. It is called at class\n instantiation, and its result is stored in ``__mro__``.\n\nclass.__subclasses__()\n\n Each new-style class keeps a list of weak references to its\n immediate subclasses. This method returns a list of all those\n references still alive. Example:\n\n >>> int.__subclasses__()\n []\n\n-[ Footnotes ]-\n\n[1] Additional information on these special methods may be found in\n the Python Reference Manual (*Basic customization*).\n\n[2] As a consequence, the list ``[1, 2]`` is considered equal to\n ``[1.0, 2.0]``, and similarly for tuples.\n\n[3] They must have since the parser can't tell the type of the\n operands.\n\n[4] To format only a tuple you should therefore provide a singleton\n tuple whose only element is the tuple to be formatted.\n", 'specialnames': '\nSpecial method names\n********************\n\nA class can implement certain operations that are invoked by special\nsyntax (such as arithmetic operations or subscripting and slicing) by\ndefining methods with special names. This is Python\'s approach to\n*operator overloading*, allowing classes to define their own behavior\nwith respect to language operators. For instance, if a class defines\na method named ``__getitem__()``, and ``x`` is an instance of this\nclass, then ``x[i]`` is roughly equivalent to ``type(x).__getitem__(x,\ni)``. Except where mentioned, attempts to execute an operation raise\nan exception when no appropriate method is defined (typically\n``AttributeError`` or ``TypeError``).\n\nWhen implementing a class that emulates any built-in type, it is\nimportant that the emulation only be implemented to the degree that it\nmakes sense for the object being modelled. For example, some\nsequences may work well with retrieval of individual elements, but\nextracting a slice may not make sense. (One example of this is the\n``NodeList`` interface in the W3C\'s Document Object Model.)\n\n\nBasic customization\n===================\n\nobject.__new__(cls[, ...])\n\n Called to create a new instance of class *cls*. ``__new__()`` is a\n static method (special-cased so you need not declare it as such)\n that takes the class of which an instance was requested as its\n first argument. The remaining arguments are those passed to the\n object constructor expression (the call to the class). The return\n value of ``__new__()`` should be the new object instance (usually\n an instance of *cls*).\n\n Typical implementations create a new instance of the class by\n invoking the superclass\'s ``__new__()`` method using\n ``super(currentclass, cls).__new__(cls[, ...])`` with appropriate\n arguments and then modifying the newly-created instance as\n necessary before returning it.\n\n If ``__new__()`` returns an instance of *cls*, then the new\n instance\'s ``__init__()`` method will be invoked like\n ``__init__(self[, ...])``, where *self* is the new instance and the\n remaining arguments are the same as were passed to ``__new__()``.\n\n If ``__new__()`` does not return an instance of *cls*, then the new\n instance\'s ``__init__()`` method will not be invoked.\n\n ``__new__()`` is intended mainly to allow subclasses of immutable\n types (like int, str, or tuple) to customize instance creation. It\n is also commonly overridden in custom metaclasses in order to\n customize class creation.\n\nobject.__init__(self[, ...])\n\n Called when the instance is created. The arguments are those\n passed to the class constructor expression. If a base class has an\n ``__init__()`` method, the derived class\'s ``__init__()`` method,\n if any, must explicitly call it to ensure proper initialization of\n the base class part of the instance; for example:\n ``BaseClass.__init__(self, [args...])``. As a special constraint\n on constructors, no value may be returned; doing so will cause a\n ``TypeError`` to be raised at runtime.\n\nobject.__del__(self)\n\n Called when the instance is about to be destroyed. This is also\n called a destructor. If a base class has a ``__del__()`` method,\n the derived class\'s ``__del__()`` method, if any, must explicitly\n call it to ensure proper deletion of the base class part of the\n instance. Note that it is possible (though not recommended!) for\n the ``__del__()`` method to postpone destruction of the instance by\n creating a new reference to it. It may then be called at a later\n time when this new reference is deleted. It is not guaranteed that\n ``__del__()`` methods are called for objects that still exist when\n the interpreter exits.\n\n Note: ``del x`` doesn\'t directly call ``x.__del__()`` --- the former\n decrements the reference count for ``x`` by one, and the latter\n is only called when ``x``\'s reference count reaches zero. Some\n common situations that may prevent the reference count of an\n object from going to zero include: circular references between\n objects (e.g., a doubly-linked list or a tree data structure with\n parent and child pointers); a reference to the object on the\n stack frame of a function that caught an exception (the traceback\n stored in ``sys.exc_info()[2]`` keeps the stack frame alive); or\n a reference to the object on the stack frame that raised an\n unhandled exception in interactive mode (the traceback stored in\n ``sys.last_traceback`` keeps the stack frame alive). The first\n situation can only be remedied by explicitly breaking the cycles;\n the latter two situations can be resolved by storing ``None`` in\n ``sys.last_traceback``. Circular references which are garbage are\n detected when the option cycle detector is enabled (it\'s on by\n default), but can only be cleaned up if there are no Python-\n level ``__del__()`` methods involved. Refer to the documentation\n for the ``gc`` module for more information about how\n ``__del__()`` methods are handled by the cycle detector,\n particularly the description of the ``garbage`` value.\n\n Warning: Due to the precarious circumstances under which ``__del__()``\n methods are invoked, exceptions that occur during their execution\n are ignored, and a warning is printed to ``sys.stderr`` instead.\n Also, when ``__del__()`` is invoked in response to a module being\n deleted (e.g., when execution of the program is done), other\n globals referenced by the ``__del__()`` method may already have\n been deleted or in the process of being torn down (e.g. the\n import machinery shutting down). For this reason, ``__del__()``\n methods should do the absolute minimum needed to maintain\n external invariants. Starting with version 1.5, Python\n guarantees that globals whose name begins with a single\n underscore are deleted from their module before other globals are\n deleted; if no other references to such globals exist, this may\n help in assuring that imported modules are still available at the\n time when the ``__del__()`` method is called.\n\nobject.__repr__(self)\n\n Called by the ``repr()`` built-in function to compute the\n "official" string representation of an object. If at all possible,\n this should look like a valid Python expression that could be used\n to recreate an object with the same value (given an appropriate\n environment). If this is not possible, a string of the form\n ``<...some useful description...>`` should be returned. The return\n value must be a string object. If a class defines ``__repr__()``\n but not ``__str__()``, then ``__repr__()`` is also used when an\n "informal" string representation of instances of that class is\n required.\n\n This is typically used for debugging, so it is important that the\n representation is information-rich and unambiguous.\n\nobject.__str__(self)\n\n Called by the ``str()`` built-in function and by the ``print()``\n function to compute the "informal" string representation of an\n object. This differs from ``__repr__()`` in that it does not have\n to be a valid Python expression: a more convenient or concise\n representation may be used instead. The return value must be a\n string object.\n\nobject.__format__(self, format_spec)\n\n Called by the ``format()`` built-in function (and by extension, the\n ``format()`` method of class ``str``) to produce a "formatted"\n string representation of an object. The ``format_spec`` argument is\n a string that contains a description of the formatting options\n desired. The interpretation of the ``format_spec`` argument is up\n to the type implementing ``__format__()``, however most classes\n will either delegate formatting to one of the built-in types, or\n use a similar formatting option syntax.\n\n See *Format Specification Mini-Language* for a description of the\n standard formatting syntax.\n\n The return value must be a string object.\n\nobject.__lt__(self, other)\nobject.__le__(self, other)\nobject.__eq__(self, other)\nobject.__ne__(self, other)\nobject.__gt__(self, other)\nobject.__ge__(self, other)\n\n These are the so-called "rich comparison" methods. The\n correspondence between operator symbols and method names is as\n follows: ``xy`` calls ``x.__gt__(y)``, and ``x>=y`` calls\n ``x.__ge__(y)``.\n\n A rich comparison method may return the singleton\n ``NotImplemented`` if it does not implement the operation for a\n given pair of arguments. By convention, ``False`` and ``True`` are\n returned for a successful comparison. However, these methods can\n return any value, so if the comparison operator is used in a\n Boolean context (e.g., in the condition of an ``if`` statement),\n Python will call ``bool()`` on the value to determine if the result\n is true or false.\n\n There are no implied relationships among the comparison operators.\n The truth of ``x==y`` does not imply that ``x!=y`` is false.\n Accordingly, when defining ``__eq__()``, one should also define\n ``__ne__()`` so that the operators will behave as expected. See\n the paragraph on ``__hash__()`` for some important notes on\n creating *hashable* objects which support custom comparison\n operations and are usable as dictionary keys.\n\n There are no swapped-argument versions of these methods (to be used\n when the left argument does not support the operation but the right\n argument does); rather, ``__lt__()`` and ``__gt__()`` are each\n other\'s reflection, ``__le__()`` and ``__ge__()`` are each other\'s\n reflection, and ``__eq__()`` and ``__ne__()`` are their own\n reflection.\n\n Arguments to rich comparison methods are never coerced.\n\n To automatically generate ordering operations from a single root\n operation, see ``functools.total_ordering()``.\n\nobject.__hash__(self)\n\n Called by built-in function ``hash()`` and for operations on\n members of hashed collections including ``set``, ``frozenset``, and\n ``dict``. ``__hash__()`` should return an integer. The only\n required property is that objects which compare equal have the same\n hash value; it is advised to somehow mix together (e.g. using\n exclusive or) the hash values for the components of the object that\n also play a part in comparison of objects.\n\n If a class does not define an ``__eq__()`` method it should not\n define a ``__hash__()`` operation either; if it defines\n ``__eq__()`` but not ``__hash__()``, its instances will not be\n usable as items in hashable collections. If a class defines\n mutable objects and implements an ``__eq__()`` method, it should\n not implement ``__hash__()``, since the implementation of hashable\n collections requires that a key\'s hash value is immutable (if the\n object\'s hash value changes, it will be in the wrong hash bucket).\n\n User-defined classes have ``__eq__()`` and ``__hash__()`` methods\n by default; with them, all objects compare unequal (except with\n themselves) and ``x.__hash__()`` returns ``id(x)``.\n\n Classes which inherit a ``__hash__()`` method from a parent class\n but change the meaning of ``__eq__()`` such that the hash value\n returned is no longer appropriate (e.g. by switching to a value-\n based concept of equality instead of the default identity based\n equality) can explicitly flag themselves as being unhashable by\n setting ``__hash__ = None`` in the class definition. Doing so means\n that not only will instances of the class raise an appropriate\n ``TypeError`` when a program attempts to retrieve their hash value,\n but they will also be correctly identified as unhashable when\n checking ``isinstance(obj, collections.Hashable)`` (unlike classes\n which define their own ``__hash__()`` to explicitly raise\n ``TypeError``).\n\n If a class that overrides ``__eq__()`` needs to retain the\n implementation of ``__hash__()`` from a parent class, the\n interpreter must be told this explicitly by setting ``__hash__ =\n .__hash__``. Otherwise the inheritance of\n ``__hash__()`` will be blocked, just as if ``__hash__`` had been\n explicitly set to ``None``.\n\nobject.__bool__(self)\n\n Called to implement truth value testing and the built-in operation\n ``bool()``; should return ``False`` or ``True``. When this method\n is not defined, ``__len__()`` is called, if it is defined, and the\n object is considered true if its result is nonzero. If a class\n defines neither ``__len__()`` nor ``__bool__()``, all its instances\n are considered true.\n\n\nCustomizing attribute access\n============================\n\nThe following methods can be defined to customize the meaning of\nattribute access (use of, assignment to, or deletion of ``x.name``)\nfor class instances.\n\nobject.__getattr__(self, name)\n\n Called when an attribute lookup has not found the attribute in the\n usual places (i.e. it is not an instance attribute nor is it found\n in the class tree for ``self``). ``name`` is the attribute name.\n This method should return the (computed) attribute value or raise\n an ``AttributeError`` exception.\n\n Note that if the attribute is found through the normal mechanism,\n ``__getattr__()`` is not called. (This is an intentional asymmetry\n between ``__getattr__()`` and ``__setattr__()``.) This is done both\n for efficiency reasons and because otherwise ``__getattr__()``\n would have no way to access other attributes of the instance. Note\n that at least for instance variables, you can fake total control by\n not inserting any values in the instance attribute dictionary (but\n instead inserting them in another object). See the\n ``__getattribute__()`` method below for a way to actually get total\n control over attribute access.\n\nobject.__getattribute__(self, name)\n\n Called unconditionally to implement attribute accesses for\n instances of the class. If the class also defines\n ``__getattr__()``, the latter will not be called unless\n ``__getattribute__()`` either calls it explicitly or raises an\n ``AttributeError``. This method should return the (computed)\n attribute value or raise an ``AttributeError`` exception. In order\n to avoid infinite recursion in this method, its implementation\n should always call the base class method with the same name to\n access any attributes it needs, for example,\n ``object.__getattribute__(self, name)``.\n\n Note: This method may still be bypassed when looking up special methods\n as the result of implicit invocation via language syntax or\n built-in functions. See *Special method lookup*.\n\nobject.__setattr__(self, name, value)\n\n Called when an attribute assignment is attempted. This is called\n instead of the normal mechanism (i.e. store the value in the\n instance dictionary). *name* is the attribute name, *value* is the\n value to be assigned to it.\n\n If ``__setattr__()`` wants to assign to an instance attribute, it\n should call the base class method with the same name, for example,\n ``object.__setattr__(self, name, value)``.\n\nobject.__delattr__(self, name)\n\n Like ``__setattr__()`` but for attribute deletion instead of\n assignment. This should only be implemented if ``del obj.name`` is\n meaningful for the object.\n\nobject.__dir__(self)\n\n Called when ``dir()`` is called on the object. A list must be\n returned.\n\n\nImplementing Descriptors\n------------------------\n\nThe following methods only apply when an instance of the class\ncontaining the method (a so-called *descriptor* class) appears in the\nclass dictionary of another class, known as the *owner* class. In the\nexamples below, "the attribute" refers to the attribute whose name is\nthe key of the property in the owner class\' ``__dict__``.\n\nobject.__get__(self, instance, owner)\n\n Called to get the attribute of the owner class (class attribute\n access) or of an instance of that class (instance attribute\n access). *owner* is always the owner class, while *instance* is the\n instance that the attribute was accessed through, or ``None`` when\n the attribute is accessed through the *owner*. This method should\n return the (computed) attribute value or raise an\n ``AttributeError`` exception.\n\nobject.__set__(self, instance, value)\n\n Called to set the attribute on an instance *instance* of the owner\n class to a new value, *value*.\n\nobject.__delete__(self, instance)\n\n Called to delete the attribute on an instance *instance* of the\n owner class.\n\n\nInvoking Descriptors\n--------------------\n\nIn general, a descriptor is an object attribute with "binding\nbehavior", one whose attribute access has been overridden by methods\nin the descriptor protocol: ``__get__()``, ``__set__()``, and\n``__delete__()``. If any of those methods are defined for an object,\nit is said to be a descriptor.\n\nThe default behavior for attribute access is to get, set, or delete\nthe attribute from an object\'s dictionary. For instance, ``a.x`` has a\nlookup chain starting with ``a.__dict__[\'x\']``, then\n``type(a).__dict__[\'x\']``, and continuing through the base classes of\n``type(a)`` excluding metaclasses.\n\nHowever, if the looked-up value is an object defining one of the\ndescriptor methods, then Python may override the default behavior and\ninvoke the descriptor method instead. Where this occurs in the\nprecedence chain depends on which descriptor methods were defined and\nhow they were called.\n\nThe starting point for descriptor invocation is a binding, ``a.x``.\nHow the arguments are assembled depends on ``a``:\n\nDirect Call\n The simplest and least common call is when user code directly\n invokes a descriptor method: ``x.__get__(a)``.\n\nInstance Binding\n If binding to an object instance, ``a.x`` is transformed into the\n call: ``type(a).__dict__[\'x\'].__get__(a, type(a))``.\n\nClass Binding\n If binding to a class, ``A.x`` is transformed into the call:\n ``A.__dict__[\'x\'].__get__(None, A)``.\n\nSuper Binding\n If ``a`` is an instance of ``super``, then the binding ``super(B,\n obj).m()`` searches ``obj.__class__.__mro__`` for the base class\n ``A`` immediately preceding ``B`` and then invokes the descriptor\n with the call: ``A.__dict__[\'m\'].__get__(obj, A)``.\n\nFor instance bindings, the precedence of descriptor invocation depends\non the which descriptor methods are defined. A descriptor can define\nany combination of ``__get__()``, ``__set__()`` and ``__delete__()``.\nIf it does not define ``__get__()``, then accessing the attribute will\nreturn the descriptor object itself unless there is a value in the\nobject\'s instance dictionary. If the descriptor defines ``__set__()``\nand/or ``__delete__()``, it is a data descriptor; if it defines\nneither, it is a non-data descriptor. Normally, data descriptors\ndefine both ``__get__()`` and ``__set__()``, while non-data\ndescriptors have just the ``__get__()`` method. Data descriptors with\n``__set__()`` and ``__get__()`` defined always override a redefinition\nin an instance dictionary. In contrast, non-data descriptors can be\noverridden by instances.\n\nPython methods (including ``staticmethod()`` and ``classmethod()``)\nare implemented as non-data descriptors. Accordingly, instances can\nredefine and override methods. This allows individual instances to\nacquire behaviors that differ from other instances of the same class.\n\nThe ``property()`` function is implemented as a data descriptor.\nAccordingly, instances cannot override the behavior of a property.\n\n\n__slots__\n---------\n\nBy default, instances of classes have a dictionary for attribute\nstorage. This wastes space for objects having very few instance\nvariables. The space consumption can become acute when creating large\nnumbers of instances.\n\nThe default can be overridden by defining *__slots__* in a class\ndefinition. The *__slots__* declaration takes a sequence of instance\nvariables and reserves just enough space in each instance to hold a\nvalue for each variable. Space is saved because *__dict__* is not\ncreated for each instance.\n\nobject.__slots__\n\n This class variable can be assigned a string, iterable, or sequence\n of strings with variable names used by instances. If defined in a\n class, *__slots__* reserves space for the declared variables and\n prevents the automatic creation of *__dict__* and *__weakref__* for\n each instance.\n\n\nNotes on using *__slots__*\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n* When inheriting from a class without *__slots__*, the *__dict__*\n attribute of that class will always be accessible, so a *__slots__*\n definition in the subclass is meaningless.\n\n* Without a *__dict__* variable, instances cannot be assigned new\n variables not listed in the *__slots__* definition. Attempts to\n assign to an unlisted variable name raises ``AttributeError``. If\n dynamic assignment of new variables is desired, then add\n ``\'__dict__\'`` to the sequence of strings in the *__slots__*\n declaration.\n\n* Without a *__weakref__* variable for each instance, classes defining\n *__slots__* do not support weak references to its instances. If weak\n reference support is needed, then add ``\'__weakref__\'`` to the\n sequence of strings in the *__slots__* declaration.\n\n* *__slots__* are implemented at the class level by creating\n descriptors (*Implementing Descriptors*) for each variable name. As\n a result, class attributes cannot be used to set default values for\n instance variables defined by *__slots__*; otherwise, the class\n attribute would overwrite the descriptor assignment.\n\n* The action of a *__slots__* declaration is limited to the class\n where it is defined. As a result, subclasses will have a *__dict__*\n unless they also define *__slots__* (which must only contain names\n of any *additional* slots).\n\n* If a class defines a slot also defined in a base class, the instance\n variable defined by the base class slot is inaccessible (except by\n retrieving its descriptor directly from the base class). This\n renders the meaning of the program undefined. In the future, a\n check may be added to prevent this.\n\n* Nonempty *__slots__* does not work for classes derived from\n "variable-length" built-in types such as ``int``, ``str`` and\n ``tuple``.\n\n* Any non-string iterable may be assigned to *__slots__*. Mappings may\n also be used; however, in the future, special meaning may be\n assigned to the values corresponding to each key.\n\n* *__class__* assignment works only if both classes have the same\n *__slots__*.\n\n\nCustomizing class creation\n==========================\n\nBy default, classes are constructed using ``type()``. A class\ndefinition is read into a separate namespace and the value of class\nname is bound to the result of ``type(name, bases, dict)``.\n\nWhen the class definition is read, if a callable ``metaclass`` keyword\nargument is passed after the bases in the class definition, the\ncallable given will be called instead of ``type()``. If other keyword\narguments are passed, they will also be passed to the metaclass. This\nallows classes or functions to be written which monitor or alter the\nclass creation process:\n\n* Modifying the class dictionary prior to the class being created.\n\n* Returning an instance of another class -- essentially performing the\n role of a factory function.\n\nThese steps will have to be performed in the metaclass\'s ``__new__()``\nmethod -- ``type.__new__()`` can then be called from this method to\ncreate a class with different properties. This example adds a new\nelement to the class dictionary before creating the class:\n\n class metacls(type):\n def __new__(mcs, name, bases, dict):\n dict[\'foo\'] = \'metacls was here\'\n return type.__new__(mcs, name, bases, dict)\n\nYou can of course also override other class methods (or add new\nmethods); for example defining a custom ``__call__()`` method in the\nmetaclass allows custom behavior when the class is called, e.g. not\nalways creating a new instance.\n\nIf the metaclass has a ``__prepare__()`` attribute (usually\nimplemented as a class or static method), it is called before the\nclass body is evaluated with the name of the class and a tuple of its\nbases for arguments. It should return an object that supports the\nmapping interface that will be used to store the namespace of the\nclass. The default is a plain dictionary. This could be used, for\nexample, to keep track of the order that class attributes are declared\nin by returning an ordered dictionary.\n\nThe appropriate metaclass is determined by the following precedence\nrules:\n\n* If the ``metaclass`` keyword argument is passed with the bases, it\n is used.\n\n* Otherwise, if there is at least one base class, its metaclass is\n used.\n\n* Otherwise, the default metaclass (``type``) is used.\n\nThe potential uses for metaclasses are boundless. Some ideas that have\nbeen explored including logging, interface checking, automatic\ndelegation, automatic property creation, proxies, frameworks, and\nautomatic resource locking/synchronization.\n\nHere is an example of a metaclass that uses an\n``collections.OrderedDict`` to remember the order that class members\nwere defined:\n\n class OrderedClass(type):\n\n @classmethod\n def __prepare__(metacls, name, bases, **kwds):\n return collections.OrderedDict()\n\n def __new__(cls, name, bases, classdict):\n result = type.__new__(cls, name, bases, dict(classdict))\n result.members = tuple(classdict)\n return result\n\n class A(metaclass=OrderedClass):\n def one(self): pass\n def two(self): pass\n def three(self): pass\n def four(self): pass\n\n >>> A.members\n (\'__module__\', \'one\', \'two\', \'three\', \'four\')\n\nWhen the class definition for *A* gets executed, the process begins\nwith calling the metaclass\'s ``__prepare__()`` method which returns an\nempty ``collections.OrderedDict``. That mapping records the methods\nand attributes of *A* as they are defined within the body of the class\nstatement. Once those definitions are executed, the ordered dictionary\nis fully populated and the metaclass\'s ``__new__()`` method gets\ninvoked. That method builds the new type and it saves the ordered\ndictionary keys in an attribute called *members*.\n\n\nCustomizing instance and subclass checks\n========================================\n\nThe following methods are used to override the default behavior of the\n``isinstance()`` and ``issubclass()`` built-in functions.\n\nIn particular, the metaclass ``abc.ABCMeta`` implements these methods\nin order to allow the addition of Abstract Base Classes (ABCs) as\n"virtual base classes" to any class or type (including built-in\ntypes), including other ABCs.\n\nclass.__instancecheck__(self, instance)\n\n Return true if *instance* should be considered a (direct or\n indirect) instance of *class*. If defined, called to implement\n ``isinstance(instance, class)``.\n\nclass.__subclasscheck__(self, subclass)\n\n Return true if *subclass* should be considered a (direct or\n indirect) subclass of *class*. If defined, called to implement\n ``issubclass(subclass, class)``.\n\nNote that these methods are looked up on the type (metaclass) of a\nclass. They cannot be defined as class methods in the actual class.\nThis is consistent with the lookup of special methods that are called\non instances, only in this case the instance is itself a class.\n\nSee also:\n\n **PEP 3119** - Introducing Abstract Base Classes\n Includes the specification for customizing ``isinstance()`` and\n ``issubclass()`` behavior through ``__instancecheck__()`` and\n ``__subclasscheck__()``, with motivation for this functionality\n in the context of adding Abstract Base Classes (see the ``abc``\n module) to the language.\n\n\nEmulating callable objects\n==========================\n\nobject.__call__(self[, args...])\n\n Called when the instance is "called" as a function; if this method\n is defined, ``x(arg1, arg2, ...)`` is a shorthand for\n ``x.__call__(arg1, arg2, ...)``.\n\n\nEmulating container types\n=========================\n\nThe following methods can be defined to implement container objects.\nContainers usually are sequences (such as lists or tuples) or mappings\n(like dictionaries), but can represent other containers as well. The\nfirst set of methods is used either to emulate a sequence or to\nemulate a mapping; the difference is that for a sequence, the\nallowable keys should be the integers *k* for which ``0 <= k < N``\nwhere *N* is the length of the sequence, or slice objects, which\ndefine a range of items. It is also recommended that mappings provide\nthe methods ``keys()``, ``values()``, ``items()``, ``get()``,\n``clear()``, ``setdefault()``, ``pop()``, ``popitem()``, ``copy()``,\nand ``update()`` behaving similar to those for Python\'s standard\ndictionary objects. The ``collections`` module provides a\n``MutableMapping`` abstract base class to help create those methods\nfrom a base set of ``__getitem__()``, ``__setitem__()``,\n``__delitem__()``, and ``keys()``. Mutable sequences should provide\nmethods ``append()``, ``count()``, ``index()``, ``extend()``,\n``insert()``, ``pop()``, ``remove()``, ``reverse()`` and ``sort()``,\nlike Python standard list objects. Finally, sequence types should\nimplement addition (meaning concatenation) and multiplication (meaning\nrepetition) by defining the methods ``__add__()``, ``__radd__()``,\n``__iadd__()``, ``__mul__()``, ``__rmul__()`` and ``__imul__()``\ndescribed below; they should not define other numerical operators. It\nis recommended that both mappings and sequences implement the\n``__contains__()`` method to allow efficient use of the ``in``\noperator; for mappings, ``in`` should search the mapping\'s keys; for\nsequences, it should search through the values. It is further\nrecommended that both mappings and sequences implement the\n``__iter__()`` method to allow efficient iteration through the\ncontainer; for mappings, ``__iter__()`` should be the same as\n``keys()``; for sequences, it should iterate through the values.\n\nobject.__len__(self)\n\n Called to implement the built-in function ``len()``. Should return\n the length of the object, an integer ``>=`` 0. Also, an object\n that doesn\'t define a ``__bool__()`` method and whose ``__len__()``\n method returns zero is considered to be false in a Boolean context.\n\nNote: Slicing is done exclusively with the following three methods. A\n call like\n\n a[1:2] = b\n\n is translated to\n\n a[slice(1, 2, None)] = b\n\n and so forth. Missing slice items are always filled in with\n ``None``.\n\nobject.__getitem__(self, key)\n\n Called to implement evaluation of ``self[key]``. For sequence\n types, the accepted keys should be integers and slice objects.\n Note that the special interpretation of negative indexes (if the\n class wishes to emulate a sequence type) is up to the\n ``__getitem__()`` method. If *key* is of an inappropriate type,\n ``TypeError`` may be raised; if of a value outside the set of\n indexes for the sequence (after any special interpretation of\n negative values), ``IndexError`` should be raised. For mapping\n types, if *key* is missing (not in the container), ``KeyError``\n should be raised.\n\n Note: ``for`` loops expect that an ``IndexError`` will be raised for\n illegal indexes to allow proper detection of the end of the\n sequence.\n\nobject.__setitem__(self, key, value)\n\n Called to implement assignment to ``self[key]``. Same note as for\n ``__getitem__()``. This should only be implemented for mappings if\n the objects support changes to the values for keys, or if new keys\n can be added, or for sequences if elements can be replaced. The\n same exceptions should be raised for improper *key* values as for\n the ``__getitem__()`` method.\n\nobject.__delitem__(self, key)\n\n Called to implement deletion of ``self[key]``. Same note as for\n ``__getitem__()``. This should only be implemented for mappings if\n the objects support removal of keys, or for sequences if elements\n can be removed from the sequence. The same exceptions should be\n raised for improper *key* values as for the ``__getitem__()``\n method.\n\nobject.__iter__(self)\n\n This method is called when an iterator is required for a container.\n This method should return a new iterator object that can iterate\n over all the objects in the container. For mappings, it should\n iterate over the keys of the container, and should also be made\n available as the method ``keys()``.\n\n Iterator objects also need to implement this method; they are\n required to return themselves. For more information on iterator\n objects, see *Iterator Types*.\n\nobject.__reversed__(self)\n\n Called (if present) by the ``reversed()`` built-in to implement\n reverse iteration. It should return a new iterator object that\n iterates over all the objects in the container in reverse order.\n\n If the ``__reversed__()`` method is not provided, the\n ``reversed()`` built-in will fall back to using the sequence\n protocol (``__len__()`` and ``__getitem__()``). Objects that\n support the sequence protocol should only provide\n ``__reversed__()`` if they can provide an implementation that is\n more efficient than the one provided by ``reversed()``.\n\nThe membership test operators (``in`` and ``not in``) are normally\nimplemented as an iteration through a sequence. However, container\nobjects can supply the following special method with a more efficient\nimplementation, which also does not require the object be a sequence.\n\nobject.__contains__(self, item)\n\n Called to implement membership test operators. Should return true\n if *item* is in *self*, false otherwise. For mapping objects, this\n should consider the keys of the mapping rather than the values or\n the key-item pairs.\n\n For objects that don\'t define ``__contains__()``, the membership\n test first tries iteration via ``__iter__()``, then the old\n sequence iteration protocol via ``__getitem__()``, see *this\n section in the language reference*.\n\n\nEmulating numeric types\n=======================\n\nThe following methods can be defined to emulate numeric objects.\nMethods corresponding to operations that are not supported by the\nparticular kind of number implemented (e.g., bitwise operations for\nnon-integral numbers) should be left undefined.\n\nobject.__add__(self, other)\nobject.__sub__(self, other)\nobject.__mul__(self, other)\nobject.__truediv__(self, other)\nobject.__floordiv__(self, other)\nobject.__mod__(self, other)\nobject.__divmod__(self, other)\nobject.__pow__(self, other[, modulo])\nobject.__lshift__(self, other)\nobject.__rshift__(self, other)\nobject.__and__(self, other)\nobject.__xor__(self, other)\nobject.__or__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``/``, ``//``, ``%``,\n ``divmod()``, ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``,\n ``|``). For instance, to evaluate the expression ``x + y``, where\n *x* is an instance of a class that has an ``__add__()`` method,\n ``x.__add__(y)`` is called. The ``__divmod__()`` method should be\n the equivalent to using ``__floordiv__()`` and ``__mod__()``; it\n should not be related to ``__truediv__()``. Note that\n ``__pow__()`` should be defined to accept an optional third\n argument if the ternary version of the built-in ``pow()`` function\n is to be supported.\n\n If one of those methods does not support the operation with the\n supplied arguments, it should return ``NotImplemented``.\n\nobject.__radd__(self, other)\nobject.__rsub__(self, other)\nobject.__rmul__(self, other)\nobject.__rtruediv__(self, other)\nobject.__rfloordiv__(self, other)\nobject.__rmod__(self, other)\nobject.__rdivmod__(self, other)\nobject.__rpow__(self, other)\nobject.__rlshift__(self, other)\nobject.__rrshift__(self, other)\nobject.__rand__(self, other)\nobject.__rxor__(self, other)\nobject.__ror__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``/``, ``//``, ``%``,\n ``divmod()``, ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``,\n ``|``) with reflected (swapped) operands. These functions are only\n called if the left operand does not support the corresponding\n operation and the operands are of different types. [2] For\n instance, to evaluate the expression ``x - y``, where *y* is an\n instance of a class that has an ``__rsub__()`` method,\n ``y.__rsub__(x)`` is called if ``x.__sub__(y)`` returns\n *NotImplemented*.\n\n Note that ternary ``pow()`` will not try calling ``__rpow__()``\n (the coercion rules would become too complicated).\n\n Note: If the right operand\'s type is a subclass of the left operand\'s\n type and that subclass provides the reflected method for the\n operation, this method will be called before the left operand\'s\n non-reflected method. This behavior allows subclasses to\n override their ancestors\' operations.\n\nobject.__iadd__(self, other)\nobject.__isub__(self, other)\nobject.__imul__(self, other)\nobject.__itruediv__(self, other)\nobject.__ifloordiv__(self, other)\nobject.__imod__(self, other)\nobject.__ipow__(self, other[, modulo])\nobject.__ilshift__(self, other)\nobject.__irshift__(self, other)\nobject.__iand__(self, other)\nobject.__ixor__(self, other)\nobject.__ior__(self, other)\n\n These methods are called to implement the augmented arithmetic\n assignments (``+=``, ``-=``, ``*=``, ``/=``, ``//=``, ``%=``,\n ``**=``, ``<<=``, ``>>=``, ``&=``, ``^=``, ``|=``). These methods\n should attempt to do the operation in-place (modifying *self*) and\n return the result (which could be, but does not have to be,\n *self*). If a specific method is not defined, the augmented\n assignment falls back to the normal methods. For instance, to\n execute the statement ``x += y``, where *x* is an instance of a\n class that has an ``__iadd__()`` method, ``x.__iadd__(y)`` is\n called. If *x* is an instance of a class that does not define a\n ``__iadd__()`` method, ``x.__add__(y)`` and ``y.__radd__(x)`` are\n considered, as with the evaluation of ``x + y``.\n\nobject.__neg__(self)\nobject.__pos__(self)\nobject.__abs__(self)\nobject.__invert__(self)\n\n Called to implement the unary arithmetic operations (``-``, ``+``,\n ``abs()`` and ``~``).\n\nobject.__complex__(self)\nobject.__int__(self)\nobject.__float__(self)\nobject.__round__(self[, n])\n\n Called to implement the built-in functions ``complex()``,\n ``int()``, ``float()`` and ``round()``. Should return a value of\n the appropriate type.\n\nobject.__index__(self)\n\n Called to implement ``operator.index()``. Also called whenever\n Python needs an integer object (such as in slicing, or in the\n built-in ``bin()``, ``hex()`` and ``oct()`` functions). Must return\n an integer.\n\n\nWith Statement Context Managers\n===============================\n\nA *context manager* is an object that defines the runtime context to\nbe established when executing a ``with`` statement. The context\nmanager handles the entry into, and the exit from, the desired runtime\ncontext for the execution of the block of code. Context managers are\nnormally invoked using the ``with`` statement (described in section\n*The with statement*), but can also be used by directly invoking their\nmethods.\n\nTypical uses of context managers include saving and restoring various\nkinds of global state, locking and unlocking resources, closing opened\nfiles, etc.\n\nFor more information on context managers, see *Context Manager Types*.\n\nobject.__enter__(self)\n\n Enter the runtime context related to this object. The ``with``\n statement will bind this method\'s return value to the target(s)\n specified in the ``as`` clause of the statement, if any.\n\nobject.__exit__(self, exc_type, exc_value, traceback)\n\n Exit the runtime context related to this object. The parameters\n describe the exception that caused the context to be exited. If the\n context was exited without an exception, all three arguments will\n be ``None``.\n\n If an exception is supplied, and the method wishes to suppress the\n exception (i.e., prevent it from being propagated), it should\n return a true value. Otherwise, the exception will be processed\n normally upon exit from this method.\n\n Note that ``__exit__()`` methods should not reraise the passed-in\n exception; this is the caller\'s responsibility.\n\nSee also:\n\n **PEP 0343** - The "with" statement\n The specification, background, and examples for the Python\n ``with`` statement.\n\n\nSpecial method lookup\n=====================\n\nFor custom classes, implicit invocations of special methods are only\nguaranteed to work correctly if defined on an object\'s type, not in\nthe object\'s instance dictionary. That behaviour is the reason why\nthe following code raises an exception:\n\n >>> class C(object):\n ... pass\n ...\n >>> c = C()\n >>> c.__len__ = lambda: 5\n >>> len(c)\n Traceback (most recent call last):\n File "", line 1, in \n TypeError: object of type \'C\' has no len()\n\nThe rationale behind this behaviour lies with a number of special\nmethods such as ``__hash__()`` and ``__repr__()`` that are implemented\nby all objects, including type objects. If the implicit lookup of\nthese methods used the conventional lookup process, they would fail\nwhen invoked on the type object itself:\n\n >>> 1 .__hash__() == hash(1)\n True\n >>> int.__hash__() == hash(int)\n Traceback (most recent call last):\n File "", line 1, in \n TypeError: descriptor \'__hash__\' of \'int\' object needs an argument\n\nIncorrectly attempting to invoke an unbound method of a class in this\nway is sometimes referred to as \'metaclass confusion\', and is avoided\nby bypassing the instance when looking up special methods:\n\n >>> type(1).__hash__(1) == hash(1)\n True\n >>> type(int).__hash__(int) == hash(int)\n True\n\nIn addition to bypassing any instance attributes in the interest of\ncorrectness, implicit special method lookup generally also bypasses\nthe ``__getattribute__()`` method even of the object\'s metaclass:\n\n >>> class Meta(type):\n ... def __getattribute__(*args):\n ... print("Metaclass getattribute invoked")\n ... return type.__getattribute__(*args)\n ...\n >>> class C(object, metaclass=Meta):\n ... def __len__(self):\n ... return 10\n ... def __getattribute__(*args):\n ... print("Class getattribute invoked")\n ... return object.__getattribute__(*args)\n ...\n >>> c = C()\n >>> c.__len__() # Explicit lookup via instance\n Class getattribute invoked\n 10\n >>> type(c).__len__(c) # Explicit lookup via type\n Metaclass getattribute invoked\n 10\n >>> len(c) # Implicit lookup\n 10\n\nBypassing the ``__getattribute__()`` machinery in this fashion\nprovides significant scope for speed optimisations within the\ninterpreter, at the cost of some flexibility in the handling of\nspecial methods (the special method *must* be set on the class object\nitself in order to be consistently invoked by the interpreter).\n\n-[ Footnotes ]-\n\n[1] It *is* possible in some cases to change an object\'s type, under\n certain controlled conditions. It generally isn\'t a good idea\n though, since it can lead to some very strange behaviour if it is\n handled incorrectly.\n\n[2] For operands of the same type, it is assumed that if the non-\n reflected method (such as ``__add__()``) fails the operation is\n not supported, which is why the reflected method is not called.\n', - 'string-methods': '\nString Methods\n**************\n\nString objects support the methods listed below.\n\nIn addition, Python\'s strings support the sequence type methods\ndescribed in the *Sequence Types --- str, bytes, bytearray, list,\ntuple, range* section. To output formatted strings, see the *String\nFormatting* section. Also, see the ``re`` module for string functions\nbased on regular expressions.\n\nstr.capitalize()\n\n Return a copy of the string with its first character capitalized\n and the rest lowercased.\n\nstr.center(width[, fillchar])\n\n Return centered in a string of length *width*. Padding is done\n using the specified *fillchar* (default is a space).\n\nstr.count(sub[, start[, end]])\n\n Return the number of non-overlapping occurrences of substring *sub*\n in the range [*start*, *end*]. Optional arguments *start* and\n *end* are interpreted as in slice notation.\n\nstr.encode(encoding=sys.getdefaultencoding(), errors="strict")\n\n Return an encoded version of the string as a bytes object. Default\n encoding is the current default string encoding. *errors* may be\n given to set a different error handling scheme. The default for\n *errors* is ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'``, ``\'xmlcharrefreplace\'``, ``\'backslashreplace\'`` and\n any other name registered via ``codecs.register_error()``, see\n section *Codec Base Classes*. For a list of possible encodings, see\n section *Standard Encodings*.\n\n Changed in version 3.1: Added support for keyword arguments added.\n\nstr.endswith(suffix[, start[, end]])\n\n Return ``True`` if the string ends with the specified *suffix*,\n otherwise return ``False``. *suffix* can also be a tuple of\n suffixes to look for. With optional *start*, test beginning at\n that position. With optional *end*, stop comparing at that\n position.\n\nstr.expandtabs([tabsize])\n\n Return a copy of the string where all tab characters are replaced\n by one or more spaces, depending on the current column and the\n given tab size. The column number is reset to zero after each\n newline occurring in the string. If *tabsize* is not given, a tab\n size of ``8`` characters is assumed. This doesn\'t understand other\n non-printing characters or escape sequences.\n\nstr.find(sub[, start[, end]])\n\n Return the lowest index in the string where substring *sub* is\n found, such that *sub* is contained in the slice ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` if *sub* is not found.\n\nstr.format(*args, **kwargs)\n\n Perform a string formatting operation. The string on which this\n method is called can contain literal text or replacement fields\n delimited by braces ``{}``. Each replacement field contains either\n the numeric index of a positional argument, or the name of a\n keyword argument. Returns a copy of the string where each\n replacement field is replaced with the string value of the\n corresponding argument.\n\n >>> "The sum of 1 + 2 is {0}".format(1+2)\n \'The sum of 1 + 2 is 3\'\n\n See *Format String Syntax* for a description of the various\n formatting options that can be specified in format strings.\n\nstr.index(sub[, start[, end]])\n\n Like ``find()``, but raise ``ValueError`` when the substring is not\n found.\n\nstr.isalnum()\n\n Return true if all characters in the string are alphanumeric and\n there is at least one character, false otherwise.\n\nstr.isalpha()\n\n Return true if all characters in the string are alphabetic and\n there is at least one character, false otherwise.\n\nstr.isdecimal()\n\n Return true if all characters in the string are decimal characters\n and there is at least one character, false otherwise. Decimal\n characters include digit characters, and all characters that that\n can be used to form decimal-radix numbers, e.g. U+0660, ARABIC-\n INDIC DIGIT ZERO.\n\nstr.isdigit()\n\n Return true if all characters in the string are digits and there is\n at least one character, false otherwise.\n\nstr.isidentifier()\n\n Return true if the string is a valid identifier according to the\n language definition, section *Identifiers and keywords*.\n\nstr.islower()\n\n Return true if all cased characters in the string are lowercase and\n there is at least one cased character, false otherwise.\n\nstr.isnumeric()\n\n Return true if all characters in the string are numeric characters,\n and there is at least one character, false otherwise. Numeric\n characters include digit characters, and all characters that have\n the Unicode numeric value property, e.g. U+2155, VULGAR FRACTION\n ONE FIFTH.\n\nstr.isprintable()\n\n Return true if all characters in the string are printable or the\n string is empty, false otherwise. Nonprintable characters are\n those characters defined in the Unicode character database as\n "Other" or "Separator", excepting the ASCII space (0x20) which is\n considered printable. (Note that printable characters in this\n context are those which should not be escaped when ``repr()`` is\n invoked on a string. It has no bearing on the handling of strings\n written to ``sys.stdout`` or ``sys.stderr``.)\n\nstr.isspace()\n\n Return true if there are only whitespace characters in the string\n and there is at least one character, false otherwise.\n\nstr.istitle()\n\n Return true if the string is a titlecased string and there is at\n least one character, for example uppercase characters may only\n follow uncased characters and lowercase characters only cased ones.\n Return false otherwise.\n\nstr.isupper()\n\n Return true if all cased characters in the string are uppercase and\n there is at least one cased character, false otherwise.\n\nstr.join(iterable)\n\n Return a string which is the concatenation of the strings in the\n *iterable* *iterable*. A ``TypeError`` will be raised if there are\n any non-string values in *seq*, including ``bytes`` objects. The\n separator between elements is the string providing this method.\n\nstr.ljust(width[, fillchar])\n\n Return the string left justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\nstr.lower()\n\n Return a copy of the string converted to lowercase.\n\nstr.lstrip([chars])\n\n Return a copy of the string with leading characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a prefix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.lstrip()\n \'spacious \'\n >>> \'www.example.com\'.lstrip(\'cmowz.\')\n \'example.com\'\n\nstatic str.maketrans(x[, y[, z]])\n\n This static method returns a translation table usable for\n ``str.translate()``.\n\n If there is only one argument, it must be a dictionary mapping\n Unicode ordinals (integers) or characters (strings of length 1) to\n Unicode ordinals, strings (of arbitrary lengths) or None.\n Character keys will then be converted to ordinals.\n\n If there are two arguments, they must be strings of equal length,\n and in the resulting dictionary, each character in x will be mapped\n to the character at the same position in y. If there is a third\n argument, it must be a string, whose characters will be mapped to\n None in the result.\n\nstr.partition(sep)\n\n Split the string at the first occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing the string itself, followed by\n two empty strings.\n\nstr.replace(old, new[, count])\n\n Return a copy of the string with all occurrences of substring *old*\n replaced by *new*. If the optional argument *count* is given, only\n the first *count* occurrences are replaced.\n\nstr.rfind(sub[, start[, end]])\n\n Return the highest index in the string where substring *sub* is\n found, such that *sub* is contained within ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` on failure.\n\nstr.rindex(sub[, start[, end]])\n\n Like ``rfind()`` but raises ``ValueError`` when the substring *sub*\n is not found.\n\nstr.rjust(width[, fillchar])\n\n Return the string right justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\nstr.rpartition(sep)\n\n Split the string at the last occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing two empty strings, followed by\n the string itself.\n\nstr.rsplit([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit* splits\n are done, the *rightmost* ones. If *sep* is not specified or\n ``None``, any whitespace string is a separator. Except for\n splitting from the right, ``rsplit()`` behaves like ``split()``\n which is described in detail below.\n\nstr.rstrip([chars])\n\n Return a copy of the string with trailing characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a suffix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.rstrip()\n \' spacious\'\n >>> \'mississippi\'.rstrip(\'ipz\')\n \'mississ\'\n\nstr.split([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit*\n splits are done (thus, the list will have at most ``maxsplit+1``\n elements). If *maxsplit* is not specified, then there is no limit\n on the number of splits (all possible splits are made).\n\n If *sep* is given, consecutive delimiters are not grouped together\n and are deemed to delimit empty strings (for example,\n ``\'1,,2\'.split(\',\')`` returns ``[\'1\', \'\', \'2\']``). The *sep*\n argument may consist of multiple characters (for example,\n ``\'1<>2<>3\'.split(\'<>\')`` returns ``[\'1\', \'2\', \'3\']``). Splitting\n an empty string with a specified separator returns ``[\'\']``.\n\n If *sep* is not specified or is ``None``, a different splitting\n algorithm is applied: runs of consecutive whitespace are regarded\n as a single separator, and the result will contain no empty strings\n at the start or end if the string has leading or trailing\n whitespace. Consequently, splitting an empty string or a string\n consisting of just whitespace with a ``None`` separator returns\n ``[]``.\n\n For example, ``\' 1 2 3 \'.split()`` returns ``[\'1\', \'2\', \'3\']``,\n and ``\' 1 2 3 \'.split(None, 1)`` returns ``[\'1\', \'2 3 \']``.\n\nstr.splitlines([keepends])\n\n Return a list of the lines in the string, breaking at line\n boundaries. Line breaks are not included in the resulting list\n unless *keepends* is given and true.\n\nstr.startswith(prefix[, start[, end]])\n\n Return ``True`` if string starts with the *prefix*, otherwise\n return ``False``. *prefix* can also be a tuple of prefixes to look\n for. With optional *start*, test string beginning at that\n position. With optional *end*, stop comparing string at that\n position.\n\nstr.strip([chars])\n\n Return a copy of the string with the leading and trailing\n characters removed. The *chars* argument is a string specifying the\n set of characters to be removed. If omitted or ``None``, the\n *chars* argument defaults to removing whitespace. The *chars*\n argument is not a prefix or suffix; rather, all combinations of its\n values are stripped:\n\n >>> \' spacious \'.strip()\n \'spacious\'\n >>> \'www.example.com\'.strip(\'cmowz.\')\n \'example\'\n\nstr.swapcase()\n\n Return a copy of the string with uppercase characters converted to\n lowercase and vice versa.\n\nstr.title()\n\n Return a titlecased version of the string where words start with an\n uppercase character and the remaining characters are lowercase.\n\n The algorithm uses a simple language-independent definition of a\n word as groups of consecutive letters. The definition works in\n many contexts but it means that apostrophes in contractions and\n possessives form word boundaries, which may not be the desired\n result:\n\n >>> "they\'re bill\'s friends from the UK".title()\n "They\'Re Bill\'S Friends From The Uk"\n\n A workaround for apostrophes can be constructed using regular\n expressions:\n\n >>> import re\n >>> def titlecase(s):\n return re.sub(r"[A-Za-z]+(\'[A-Za-z]+)?",\n lambda mo: mo.group(0)[0].upper() +\n mo.group(0)[1:].lower(),\n s)\n\n >>> titlecase("they\'re bill\'s friends.")\n "They\'re Bill\'s Friends."\n\nstr.translate(map)\n\n Return a copy of the *s* where all characters have been mapped\n through the *map* which must be a dictionary of Unicode ordinals\n (integers) to Unicode ordinals, strings or ``None``. Unmapped\n characters are left untouched. Characters mapped to ``None`` are\n deleted.\n\n You can use ``str.maketrans()`` to create a translation map from\n character-to-character mappings in different formats.\n\n You can use the ``maketrans()`` helper function in the ``string``\n module to create a translation table. For string objects, set the\n *table* argument to ``None`` for translations that only delete\n characters:\n\n Note: An even more flexible approach is to create a custom character\n mapping codec using the ``codecs`` module (see\n ``encodings.cp1251`` for an example).\n\nstr.upper()\n\n Return a copy of the string converted to uppercase.\n\nstr.zfill(width)\n\n Return the numeric string left filled with zeros in a string of\n length *width*. A sign prefix is handled correctly. The original\n string is returned if *width* is less than ``len(s)``.\n', + 'string-methods': '\nString Methods\n**************\n\nString objects support the methods listed below.\n\nIn addition, Python\'s strings support the sequence type methods\ndescribed in the *Sequence Types --- str, bytes, bytearray, list,\ntuple, range* section. To output formatted strings, see the *String\nFormatting* section. Also, see the ``re`` module for string functions\nbased on regular expressions.\n\nstr.capitalize()\n\n Return a copy of the string with its first character capitalized\n and the rest lowercased.\n\nstr.center(width[, fillchar])\n\n Return centered in a string of length *width*. Padding is done\n using the specified *fillchar* (default is a space).\n\nstr.count(sub[, start[, end]])\n\n Return the number of non-overlapping occurrences of substring *sub*\n in the range [*start*, *end*]. Optional arguments *start* and\n *end* are interpreted as in slice notation.\n\nstr.encode(encoding=sys.getdefaultencoding(), errors="strict")\n\n Return an encoded version of the string as a bytes object. Default\n encoding is the current default string encoding. *errors* may be\n given to set a different error handling scheme. The default for\n *errors* is ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'``, ``\'xmlcharrefreplace\'``, ``\'backslashreplace\'`` and\n any other name registered via ``codecs.register_error()``, see\n section *Codec Base Classes*. For a list of possible encodings, see\n section *Standard Encodings*.\n\n Changed in version 3.1: Support for keyword arguments added.\n\nstr.endswith(suffix[, start[, end]])\n\n Return ``True`` if the string ends with the specified *suffix*,\n otherwise return ``False``. *suffix* can also be a tuple of\n suffixes to look for. With optional *start*, test beginning at\n that position. With optional *end*, stop comparing at that\n position.\n\nstr.expandtabs([tabsize])\n\n Return a copy of the string where all tab characters are replaced\n by one or more spaces, depending on the current column and the\n given tab size. The column number is reset to zero after each\n newline occurring in the string. If *tabsize* is not given, a tab\n size of ``8`` characters is assumed. This doesn\'t understand other\n non-printing characters or escape sequences.\n\nstr.find(sub[, start[, end]])\n\n Return the lowest index in the string where substring *sub* is\n found, such that *sub* is contained in the slice ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` if *sub* is not found.\n\nstr.format(*args, **kwargs)\n\n Perform a string formatting operation. The string on which this\n method is called can contain literal text or replacement fields\n delimited by braces ``{}``. Each replacement field contains either\n the numeric index of a positional argument, or the name of a\n keyword argument. Returns a copy of the string where each\n replacement field is replaced with the string value of the\n corresponding argument.\n\n >>> "The sum of 1 + 2 is {0}".format(1+2)\n \'The sum of 1 + 2 is 3\'\n\n See *Format String Syntax* for a description of the various\n formatting options that can be specified in format strings.\n\nstr.index(sub[, start[, end]])\n\n Like ``find()``, but raise ``ValueError`` when the substring is not\n found.\n\nstr.isalnum()\n\n Return true if all characters in the string are alphanumeric and\n there is at least one character, false otherwise.\n\nstr.isalpha()\n\n Return true if all characters in the string are alphabetic and\n there is at least one character, false otherwise.\n\nstr.isdecimal()\n\n Return true if all characters in the string are decimal characters\n and there is at least one character, false otherwise. Decimal\n characters include digit characters, and all characters that that\n can be used to form decimal-radix numbers, e.g. U+0660, ARABIC-\n INDIC DIGIT ZERO.\n\nstr.isdigit()\n\n Return true if all characters in the string are digits and there is\n at least one character, false otherwise.\n\nstr.isidentifier()\n\n Return true if the string is a valid identifier according to the\n language definition, section *Identifiers and keywords*.\n\nstr.islower()\n\n Return true if all cased characters in the string are lowercase and\n there is at least one cased character, false otherwise.\n\nstr.isnumeric()\n\n Return true if all characters in the string are numeric characters,\n and there is at least one character, false otherwise. Numeric\n characters include digit characters, and all characters that have\n the Unicode numeric value property, e.g. U+2155, VULGAR FRACTION\n ONE FIFTH.\n\nstr.isprintable()\n\n Return true if all characters in the string are printable or the\n string is empty, false otherwise. Nonprintable characters are\n those characters defined in the Unicode character database as\n "Other" or "Separator", excepting the ASCII space (0x20) which is\n considered printable. (Note that printable characters in this\n context are those which should not be escaped when ``repr()`` is\n invoked on a string. It has no bearing on the handling of strings\n written to ``sys.stdout`` or ``sys.stderr``.)\n\nstr.isspace()\n\n Return true if there are only whitespace characters in the string\n and there is at least one character, false otherwise.\n\nstr.istitle()\n\n Return true if the string is a titlecased string and there is at\n least one character, for example uppercase characters may only\n follow uncased characters and lowercase characters only cased ones.\n Return false otherwise.\n\nstr.isupper()\n\n Return true if all cased characters in the string are uppercase and\n there is at least one cased character, false otherwise.\n\nstr.join(iterable)\n\n Return a string which is the concatenation of the strings in the\n *iterable* *iterable*. A ``TypeError`` will be raised if there are\n any non-string values in *seq*, including ``bytes`` objects. The\n separator between elements is the string providing this method.\n\nstr.ljust(width[, fillchar])\n\n Return the string left justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\nstr.lower()\n\n Return a copy of the string converted to lowercase.\n\nstr.lstrip([chars])\n\n Return a copy of the string with leading characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a prefix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.lstrip()\n \'spacious \'\n >>> \'www.example.com\'.lstrip(\'cmowz.\')\n \'example.com\'\n\nstatic str.maketrans(x[, y[, z]])\n\n This static method returns a translation table usable for\n ``str.translate()``.\n\n If there is only one argument, it must be a dictionary mapping\n Unicode ordinals (integers) or characters (strings of length 1) to\n Unicode ordinals, strings (of arbitrary lengths) or None.\n Character keys will then be converted to ordinals.\n\n If there are two arguments, they must be strings of equal length,\n and in the resulting dictionary, each character in x will be mapped\n to the character at the same position in y. If there is a third\n argument, it must be a string, whose characters will be mapped to\n None in the result.\n\nstr.partition(sep)\n\n Split the string at the first occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing the string itself, followed by\n two empty strings.\n\nstr.replace(old, new[, count])\n\n Return a copy of the string with all occurrences of substring *old*\n replaced by *new*. If the optional argument *count* is given, only\n the first *count* occurrences are replaced.\n\nstr.rfind(sub[, start[, end]])\n\n Return the highest index in the string where substring *sub* is\n found, such that *sub* is contained within ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` on failure.\n\nstr.rindex(sub[, start[, end]])\n\n Like ``rfind()`` but raises ``ValueError`` when the substring *sub*\n is not found.\n\nstr.rjust(width[, fillchar])\n\n Return the string right justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\nstr.rpartition(sep)\n\n Split the string at the last occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing two empty strings, followed by\n the string itself.\n\nstr.rsplit([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit* splits\n are done, the *rightmost* ones. If *sep* is not specified or\n ``None``, any whitespace string is a separator. Except for\n splitting from the right, ``rsplit()`` behaves like ``split()``\n which is described in detail below.\n\nstr.rstrip([chars])\n\n Return a copy of the string with trailing characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a suffix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.rstrip()\n \' spacious\'\n >>> \'mississippi\'.rstrip(\'ipz\')\n \'mississ\'\n\nstr.split([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit*\n splits are done (thus, the list will have at most ``maxsplit+1``\n elements). If *maxsplit* is not specified, then there is no limit\n on the number of splits (all possible splits are made).\n\n If *sep* is given, consecutive delimiters are not grouped together\n and are deemed to delimit empty strings (for example,\n ``\'1,,2\'.split(\',\')`` returns ``[\'1\', \'\', \'2\']``). The *sep*\n argument may consist of multiple characters (for example,\n ``\'1<>2<>3\'.split(\'<>\')`` returns ``[\'1\', \'2\', \'3\']``). Splitting\n an empty string with a specified separator returns ``[\'\']``.\n\n If *sep* is not specified or is ``None``, a different splitting\n algorithm is applied: runs of consecutive whitespace are regarded\n as a single separator, and the result will contain no empty strings\n at the start or end if the string has leading or trailing\n whitespace. Consequently, splitting an empty string or a string\n consisting of just whitespace with a ``None`` separator returns\n ``[]``.\n\n For example, ``\' 1 2 3 \'.split()`` returns ``[\'1\', \'2\', \'3\']``,\n and ``\' 1 2 3 \'.split(None, 1)`` returns ``[\'1\', \'2 3 \']``.\n\nstr.splitlines([keepends])\n\n Return a list of the lines in the string, breaking at line\n boundaries. Line breaks are not included in the resulting list\n unless *keepends* is given and true.\n\nstr.startswith(prefix[, start[, end]])\n\n Return ``True`` if string starts with the *prefix*, otherwise\n return ``False``. *prefix* can also be a tuple of prefixes to look\n for. With optional *start*, test string beginning at that\n position. With optional *end*, stop comparing string at that\n position.\n\nstr.strip([chars])\n\n Return a copy of the string with the leading and trailing\n characters removed. The *chars* argument is a string specifying the\n set of characters to be removed. If omitted or ``None``, the\n *chars* argument defaults to removing whitespace. The *chars*\n argument is not a prefix or suffix; rather, all combinations of its\n values are stripped:\n\n >>> \' spacious \'.strip()\n \'spacious\'\n >>> \'www.example.com\'.strip(\'cmowz.\')\n \'example\'\n\nstr.swapcase()\n\n Return a copy of the string with uppercase characters converted to\n lowercase and vice versa.\n\nstr.title()\n\n Return a titlecased version of the string where words start with an\n uppercase character and the remaining characters are lowercase.\n\n The algorithm uses a simple language-independent definition of a\n word as groups of consecutive letters. The definition works in\n many contexts but it means that apostrophes in contractions and\n possessives form word boundaries, which may not be the desired\n result:\n\n >>> "they\'re bill\'s friends from the UK".title()\n "They\'Re Bill\'S Friends From The Uk"\n\n A workaround for apostrophes can be constructed using regular\n expressions:\n\n >>> import re\n >>> def titlecase(s):\n return re.sub(r"[A-Za-z]+(\'[A-Za-z]+)?",\n lambda mo: mo.group(0)[0].upper() +\n mo.group(0)[1:].lower(),\n s)\n\n >>> titlecase("they\'re bill\'s friends.")\n "They\'re Bill\'s Friends."\n\nstr.translate(map)\n\n Return a copy of the *s* where all characters have been mapped\n through the *map* which must be a dictionary of Unicode ordinals\n (integers) to Unicode ordinals, strings or ``None``. Unmapped\n characters are left untouched. Characters mapped to ``None`` are\n deleted.\n\n You can use ``str.maketrans()`` to create a translation map from\n character-to-character mappings in different formats.\n\n Note: An even more flexible approach is to create a custom character\n mapping codec using the ``codecs`` module (see\n ``encodings.cp1251`` for an example).\n\nstr.upper()\n\n Return a copy of the string converted to uppercase.\n\nstr.zfill(width)\n\n Return the numeric string left filled with zeros in a string of\n length *width*. A sign prefix is handled correctly. The original\n string is returned if *width* is less than ``len(s)``.\n', 'strings': '\nString and Bytes literals\n*************************\n\nString literals are described by the following lexical definitions:\n\n stringliteral ::= [stringprefix](shortstring | longstring)\n stringprefix ::= "r" | "R"\n shortstring ::= "\'" shortstringitem* "\'" | \'"\' shortstringitem* \'"\'\n longstring ::= "\'\'\'" longstringitem* "\'\'\'" | \'"""\' longstringitem* \'"""\'\n shortstringitem ::= shortstringchar | stringescapeseq\n longstringitem ::= longstringchar | stringescapeseq\n shortstringchar ::= \n longstringchar ::= \n stringescapeseq ::= "\\" \n\n bytesliteral ::= bytesprefix(shortbytes | longbytes)\n bytesprefix ::= "b" | "B" | "br" | "Br" | "bR" | "BR"\n shortbytes ::= "\'" shortbytesitem* "\'" | \'"\' shortbytesitem* \'"\'\n longbytes ::= "\'\'\'" longbytesitem* "\'\'\'" | \'"""\' longbytesitem* \'"""\'\n shortbytesitem ::= shortbyteschar | bytesescapeseq\n longbytesitem ::= longbyteschar | bytesescapeseq\n shortbyteschar ::= \n longbyteschar ::= \n bytesescapeseq ::= "\\" \n\nOne syntactic restriction not indicated by these productions is that\nwhitespace is not allowed between the ``stringprefix`` or\n``bytesprefix`` and the rest of the literal. The source character set\nis defined by the encoding declaration; it is UTF-8 if no encoding\ndeclaration is given in the source file; see section *Encoding\ndeclarations*.\n\nIn plain English: Both types of literals can be enclosed in matching\nsingle quotes (``\'``) or double quotes (``"``). They can also be\nenclosed in matching groups of three single or double quotes (these\nare generally referred to as *triple-quoted strings*). The backslash\n(``\\``) character is used to escape characters that otherwise have a\nspecial meaning, such as newline, backslash itself, or the quote\ncharacter.\n\nBytes literals are always prefixed with ``\'b\'`` or ``\'B\'``; they\nproduce an instance of the ``bytes`` type instead of the ``str`` type.\nThey may only contain ASCII characters; bytes with a numeric value of\n128 or greater must be expressed with escapes.\n\nBoth string and bytes literals may optionally be prefixed with a\nletter ``\'r\'`` or ``\'R\'``; such strings are called *raw strings* and\ntreat backslashes as literal characters. As a result, in string\nliterals, ``\'\\U\'`` and ``\'\\u\'`` escapes in raw strings are not treated\nspecially.\n\nIn triple-quoted strings, unescaped newlines and quotes are allowed\n(and are retained), except that three unescaped quotes in a row\nterminate the string. (A "quote" is the character used to open the\nstring, i.e. either ``\'`` or ``"``.)\n\nUnless an ``\'r\'`` or ``\'R\'`` prefix is present, escape sequences in\nstrings are interpreted according to rules similar to those used by\nStandard C. The recognized escape sequences are:\n\n+-------------------+-----------------------------------+---------+\n| Escape Sequence | Meaning | Notes |\n+===================+===================================+=========+\n| ``\\newline`` | Backslash and newline ignored | |\n+-------------------+-----------------------------------+---------+\n| ``\\\\`` | Backslash (``\\``) | |\n+-------------------+-----------------------------------+---------+\n| ``\\\'`` | Single quote (``\'``) | |\n+-------------------+-----------------------------------+---------+\n| ``\\"`` | Double quote (``"``) | |\n+-------------------+-----------------------------------+---------+\n| ``\\a`` | ASCII Bell (BEL) | |\n+-------------------+-----------------------------------+---------+\n| ``\\b`` | ASCII Backspace (BS) | |\n+-------------------+-----------------------------------+---------+\n| ``\\f`` | ASCII Formfeed (FF) | |\n+-------------------+-----------------------------------+---------+\n| ``\\n`` | ASCII Linefeed (LF) | |\n+-------------------+-----------------------------------+---------+\n| ``\\r`` | ASCII Carriage Return (CR) | |\n+-------------------+-----------------------------------+---------+\n| ``\\t`` | ASCII Horizontal Tab (TAB) | |\n+-------------------+-----------------------------------+---------+\n| ``\\v`` | ASCII Vertical Tab (VT) | |\n+-------------------+-----------------------------------+---------+\n| ``\\ooo`` | Character with octal value *ooo* | (1,3) |\n+-------------------+-----------------------------------+---------+\n| ``\\xhh`` | Character with hex value *hh* | (2,3) |\n+-------------------+-----------------------------------+---------+\n\nEscape sequences only recognized in string literals are:\n\n+-------------------+-----------------------------------+---------+\n| Escape Sequence | Meaning | Notes |\n+===================+===================================+=========+\n| ``\\N{name}`` | Character named *name* in the | |\n| | Unicode database | |\n+-------------------+-----------------------------------+---------+\n| ``\\uxxxx`` | Character with 16-bit hex value | (4) |\n| | *xxxx* | |\n+-------------------+-----------------------------------+---------+\n| ``\\Uxxxxxxxx`` | Character with 32-bit hex value | (5) |\n| | *xxxxxxxx* | |\n+-------------------+-----------------------------------+---------+\n\nNotes:\n\n1. As in Standard C, up to three octal digits are accepted.\n\n2. Unlike in Standard C, exactly two hex digits are required.\n\n3. In a bytes literal, hexadecimal and octal escapes denote the byte\n with the given value. In a string literal, these escapes denote a\n Unicode character with the given value.\n\n4. Individual code units which form parts of a surrogate pair can be\n encoded using this escape sequence. Exactly four hex digits are\n required.\n\n5. Any Unicode character can be encoded this way, but characters\n outside the Basic Multilingual Plane (BMP) will be encoded using a\n surrogate pair if Python is compiled to use 16-bit code units (the\n default). Exactly eight hex digits are required.\n\nUnlike Standard C, all unrecognized escape sequences are left in the\nstring unchanged, i.e., *the backslash is left in the string*. (This\nbehavior is useful when debugging: if an escape sequence is mistyped,\nthe resulting output is more easily recognized as broken.) It is also\nimportant to note that the escape sequences only recognized in string\nliterals fall into the category of unrecognized escapes for bytes\nliterals.\n\nEven in a raw string, string quotes can be escaped with a backslash,\nbut the backslash remains in the string; for example, ``r"\\""`` is a\nvalid string literal consisting of two characters: a backslash and a\ndouble quote; ``r"\\"`` is not a valid string literal (even a raw\nstring cannot end in an odd number of backslashes). Specifically, *a\nraw string cannot end in a single backslash* (since the backslash\nwould escape the following quote character). Note also that a single\nbackslash followed by a newline is interpreted as those two characters\nas part of the string, *not* as a line continuation.\n', 'subscriptions': '\nSubscriptions\n*************\n\nA subscription selects an item of a sequence (string, tuple or list)\nor mapping (dictionary) object:\n\n subscription ::= primary "[" expression_list "]"\n\nThe primary must evaluate to an object that supports subscription,\ne.g. a list or dictionary. User-defined objects can support\nsubscription by defining a ``__getitem__()`` method.\n\nFor built-in objects, there are two types of objects that support\nsubscription:\n\nIf the primary is a mapping, the expression list must evaluate to an\nobject whose value is one of the keys of the mapping, and the\nsubscription selects the value in the mapping that corresponds to that\nkey. (The expression list is a tuple except if it has exactly one\nitem.)\n\nIf the primary is a sequence, the expression (list) must evaluate to\nan integer. If this value is negative, the length of the sequence is\nadded to it (so that, e.g., ``x[-1]`` selects the last item of ``x``.)\nThe resulting value must be a nonnegative integer less than the number\nof items in the sequence, and the subscription selects the item whose\nindex is that value (counting from zero).\n\nA string\'s items are characters. A character is not a separate data\ntype but a string of exactly one character.\n', 'truth': "\nTruth Value Testing\n*******************\n\nAny object can be tested for truth value, for use in an ``if`` or\n``while`` condition or as operand of the Boolean operations below. The\nfollowing values are considered false:\n\n* ``None``\n\n* ``False``\n\n* zero of any numeric type, for example, ``0``, ``0.0``, ``0j``.\n\n* any empty sequence, for example, ``''``, ``()``, ``[]``.\n\n* any empty mapping, for example, ``{}``.\n\n* instances of user-defined classes, if the class defines a\n ``__bool__()`` or ``__len__()`` method, when that method returns the\n integer zero or ``bool`` value ``False``. [1]\n\nAll other values are considered true --- so objects of many types are\nalways true.\n\nOperations and built-in functions that have a Boolean result always\nreturn ``0`` or ``False`` for false and ``1`` or ``True`` for true,\nunless otherwise stated. (Important exception: the Boolean operations\n``or`` and ``and`` always return one of their operands.)\n", 'try': '\nThe ``try`` statement\n*********************\n\nThe ``try`` statement specifies exception handlers and/or cleanup code\nfor a group of statements:\n\n try_stmt ::= try1_stmt | try2_stmt\n try1_stmt ::= "try" ":" suite\n ("except" [expression ["as" target]] ":" suite)+\n ["else" ":" suite]\n ["finally" ":" suite]\n try2_stmt ::= "try" ":" suite\n "finally" ":" suite\n\nThe ``except`` clause(s) specify one or more exception handlers. When\nno exception occurs in the ``try`` clause, no exception handler is\nexecuted. When an exception occurs in the ``try`` suite, a search for\nan exception handler is started. This search inspects the except\nclauses in turn until one is found that matches the exception. An\nexpression-less except clause, if present, must be last; it matches\nany exception. For an except clause with an expression, that\nexpression is evaluated, and the clause matches the exception if the\nresulting object is "compatible" with the exception. An object is\ncompatible with an exception if it is the class or a base class of the\nexception object or a tuple containing an item compatible with the\nexception.\n\nIf no except clause matches the exception, the search for an exception\nhandler continues in the surrounding code and on the invocation stack.\n[1]\n\nIf the evaluation of an expression in the header of an except clause\nraises an exception, the original search for a handler is canceled and\na search starts for the new exception in the surrounding code and on\nthe call stack (it is treated as if the entire ``try`` statement\nraised the exception).\n\nWhen a matching except clause is found, the exception is assigned to\nthe target specified after the ``as`` keyword in that except clause,\nif present, and the except clause\'s suite is executed. All except\nclauses must have an executable block. When the end of this block is\nreached, execution continues normally after the entire try statement.\n(This means that if two nested handlers exist for the same exception,\nand the exception occurs in the try clause of the inner handler, the\nouter handler will not handle the exception.)\n\nWhen an exception has been assigned using ``as target``, it is cleared\nat the end of the except clause. This is as if\n\n except E as N:\n foo\n\nwas translated to\n\n except E as N:\n try:\n foo\n finally:\n del N\n\nThis means the exception must be assigned to a different name to be\nable to refer to it after the except clause. Exceptions are cleared\nbecause with the traceback attached to them, they form a reference\ncycle with the stack frame, keeping all locals in that frame alive\nuntil the next garbage collection occurs.\n\nBefore an except clause\'s suite is executed, details about the\nexception are stored in the ``sys`` module and can be access via\n``sys.exc_info()``. ``sys.exc_info()`` returns a 3-tuple consisting\nof: ``exc_type``, the exception class; ``exc_value``, the exception\ninstance; ``exc_traceback``, a traceback object (see section *The\nstandard type hierarchy*) identifying the point in the program where\nthe exception occurred. ``sys.exc_info()`` values are restored to\ntheir previous values (before the call) when returning from a function\nthat handled an exception.\n\nThe optional ``else`` clause is executed if and when control flows off\nthe end of the ``try`` clause. [2] Exceptions in the ``else`` clause\nare not handled by the preceding ``except`` clauses.\n\nIf ``finally`` is present, it specifies a \'cleanup\' handler. The\n``try`` clause is executed, including any ``except`` and ``else``\nclauses. If an exception occurs in any of the clauses and is not\nhandled, the exception is temporarily saved. The ``finally`` clause is\nexecuted. If there is a saved exception, it is re-raised at the end\nof the ``finally`` clause. If the ``finally`` clause raises another\nexception or executes a ``return`` or ``break`` statement, the saved\nexception is lost. The exception information is not available to the\nprogram during execution of the ``finally`` clause.\n\nWhen a ``return``, ``break`` or ``continue`` statement is executed in\nthe ``try`` suite of a ``try``...``finally`` statement, the\n``finally`` clause is also executed \'on the way out.\' A ``continue``\nstatement is illegal in the ``finally`` clause. (The reason is a\nproblem with the current implementation --- this restriction may be\nlifted in the future).\n\nAdditional information on exceptions can be found in section\n*Exceptions*, and information on using the ``raise`` statement to\ngenerate exceptions may be found in section *The raise statement*.\n', 'types': '\nThe standard type hierarchy\n***************************\n\nBelow is a list of the types that are built into Python. Extension\nmodules (written in C, Java, or other languages, depending on the\nimplementation) can define additional types. Future versions of\nPython may add types to the type hierarchy (e.g., rational numbers,\nefficiently stored arrays of integers, etc.), although such additions\nwill often be provided via the standard library instead.\n\nSome of the type descriptions below contain a paragraph listing\n\'special attributes.\' These are attributes that provide access to the\nimplementation and are not intended for general use. Their definition\nmay change in the future.\n\nNone\n This type has a single value. There is a single object with this\n value. This object is accessed through the built-in name ``None``.\n It is used to signify the absence of a value in many situations,\n e.g., it is returned from functions that don\'t explicitly return\n anything. Its truth value is false.\n\nNotImplemented\n This type has a single value. There is a single object with this\n value. This object is accessed through the built-in name\n ``NotImplemented``. Numeric methods and rich comparison methods may\n return this value if they do not implement the operation for the\n operands provided. (The interpreter will then try the reflected\n operation, or some other fallback, depending on the operator.) Its\n truth value is true.\n\nEllipsis\n This type has a single value. There is a single object with this\n value. This object is accessed through the literal ``...`` or the\n built-in name ``Ellipsis``. Its truth value is true.\n\n``numbers.Number``\n These are created by numeric literals and returned as results by\n arithmetic operators and arithmetic built-in functions. Numeric\n objects are immutable; once created their value never changes.\n Python numbers are of course strongly related to mathematical\n numbers, but subject to the limitations of numerical representation\n in computers.\n\n Python distinguishes between integers, floating point numbers, and\n complex numbers:\n\n ``numbers.Integral``\n These represent elements from the mathematical set of integers\n (positive and negative).\n\n There are two types of integers:\n\n Integers (``int``)\n\n These represent numbers in an unlimited range, subject to\n available (virtual) memory only. For the purpose of shift\n and mask operations, a binary representation is assumed, and\n negative numbers are represented in a variant of 2\'s\n complement which gives the illusion of an infinite string of\n sign bits extending to the left.\n\n Booleans (``bool``)\n These represent the truth values False and True. The two\n objects representing the values False and True are the only\n Boolean objects. The Boolean type is a subtype of the integer\n type, and Boolean values behave like the values 0 and 1,\n respectively, in almost all contexts, the exception being\n that when converted to a string, the strings ``"False"`` or\n ``"True"`` are returned, respectively.\n\n The rules for integer representation are intended to give the\n most meaningful interpretation of shift and mask operations\n involving negative integers.\n\n ``numbers.Real`` (``float``)\n These represent machine-level double precision floating point\n numbers. You are at the mercy of the underlying machine\n architecture (and C or Java implementation) for the accepted\n range and handling of overflow. Python does not support single-\n precision floating point numbers; the savings in processor and\n memory usage that are usually the reason for using these is\n dwarfed by the overhead of using objects in Python, so there is\n no reason to complicate the language with two kinds of floating\n point numbers.\n\n ``numbers.Complex`` (``complex``)\n These represent complex numbers as a pair of machine-level\n double precision floating point numbers. The same caveats apply\n as for floating point numbers. The real and imaginary parts of a\n complex number ``z`` can be retrieved through the read-only\n attributes ``z.real`` and ``z.imag``.\n\nSequences\n These represent finite ordered sets indexed by non-negative\n numbers. The built-in function ``len()`` returns the number of\n items of a sequence. When the length of a sequence is *n*, the\n index set contains the numbers 0, 1, ..., *n*-1. Item *i* of\n sequence *a* is selected by ``a[i]``.\n\n Sequences also support slicing: ``a[i:j]`` selects all items with\n index *k* such that *i* ``<=`` *k* ``<`` *j*. When used as an\n expression, a slice is a sequence of the same type. This implies\n that the index set is renumbered so that it starts at 0.\n\n Some sequences also support "extended slicing" with a third "step"\n parameter: ``a[i:j:k]`` selects all items of *a* with index *x*\n where ``x = i + n*k``, *n* ``>=`` ``0`` and *i* ``<=`` *x* ``<``\n *j*.\n\n Sequences are distinguished according to their mutability:\n\n Immutable sequences\n An object of an immutable sequence type cannot change once it is\n created. (If the object contains references to other objects,\n these other objects may be mutable and may be changed; however,\n the collection of objects directly referenced by an immutable\n object cannot change.)\n\n The following types are immutable sequences:\n\n Strings\n The items of a string object are Unicode code units. A\n Unicode code unit is represented by a string object of one\n item and can hold either a 16-bit or 32-bit value\n representing a Unicode ordinal (the maximum value for the\n ordinal is given in ``sys.maxunicode``, and depends on how\n Python is configured at compile time). Surrogate pairs may\n be present in the Unicode object, and will be reported as two\n separate items. The built-in functions ``chr()`` and\n ``ord()`` convert between code units and nonnegative integers\n representing the Unicode ordinals as defined in the Unicode\n Standard 3.0. Conversion from and to other encodings are\n possible through the string method ``encode()``.\n\n Tuples\n The items of a tuple are arbitrary Python objects. Tuples of\n two or more items are formed by comma-separated lists of\n expressions. A tuple of one item (a \'singleton\') can be\n formed by affixing a comma to an expression (an expression by\n itself does not create a tuple, since parentheses must be\n usable for grouping of expressions). An empty tuple can be\n formed by an empty pair of parentheses.\n\n Bytes\n A bytes object is an immutable array. The items are 8-bit\n bytes, represented by integers in the range 0 <= x < 256.\n Bytes literals (like ``b\'abc\'`` and the built-in function\n ``bytes()`` can be used to construct bytes objects. Also,\n bytes objects can be decoded to strings via the ``decode()``\n method.\n\n Mutable sequences\n Mutable sequences can be changed after they are created. The\n subscription and slicing notations can be used as the target of\n assignment and ``del`` (delete) statements.\n\n There are currently two intrinsic mutable sequence types:\n\n Lists\n The items of a list are arbitrary Python objects. Lists are\n formed by placing a comma-separated list of expressions in\n square brackets. (Note that there are no special cases needed\n to form lists of length 0 or 1.)\n\n Byte Arrays\n A bytearray object is a mutable array. They are created by\n the built-in ``bytearray()`` constructor. Aside from being\n mutable (and hence unhashable), byte arrays otherwise provide\n the same interface and functionality as immutable bytes\n objects.\n\n The extension module ``array`` provides an additional example of\n a mutable sequence type, as does the ``collections`` module.\n\nSet types\n These represent unordered, finite sets of unique, immutable\n objects. As such, they cannot be indexed by any subscript. However,\n they can be iterated over, and the built-in function ``len()``\n returns the number of items in a set. Common uses for sets are fast\n membership testing, removing duplicates from a sequence, and\n computing mathematical operations such as intersection, union,\n difference, and symmetric difference.\n\n For set elements, the same immutability rules apply as for\n dictionary keys. Note that numeric types obey the normal rules for\n numeric comparison: if two numbers compare equal (e.g., ``1`` and\n ``1.0``), only one of them can be contained in a set.\n\n There are currently two intrinsic set types:\n\n Sets\n These represent a mutable set. They are created by the built-in\n ``set()`` constructor and can be modified afterwards by several\n methods, such as ``add()``.\n\n Frozen sets\n These represent an immutable set. They are created by the\n built-in ``frozenset()`` constructor. As a frozenset is\n immutable and *hashable*, it can be used again as an element of\n another set, or as a dictionary key.\n\nMappings\n These represent finite sets of objects indexed by arbitrary index\n sets. The subscript notation ``a[k]`` selects the item indexed by\n ``k`` from the mapping ``a``; this can be used in expressions and\n as the target of assignments or ``del`` statements. The built-in\n function ``len()`` returns the number of items in a mapping.\n\n There is currently a single intrinsic mapping type:\n\n Dictionaries\n These represent finite sets of objects indexed by nearly\n arbitrary values. The only types of values not acceptable as\n keys are values containing lists or dictionaries or other\n mutable types that are compared by value rather than by object\n identity, the reason being that the efficient implementation of\n dictionaries requires a key\'s hash value to remain constant.\n Numeric types used for keys obey the normal rules for numeric\n comparison: if two numbers compare equal (e.g., ``1`` and\n ``1.0``) then they can be used interchangeably to index the same\n dictionary entry.\n\n Dictionaries are mutable; they can be created by the ``{...}``\n notation (see section *Dictionary displays*).\n\n The extension modules ``dbm.ndbm`` and ``dbm.gnu`` provide\n additional examples of mapping types, as does the\n ``collections`` module.\n\nCallable types\n These are the types to which the function call operation (see\n section *Calls*) can be applied:\n\n User-defined functions\n A user-defined function object is created by a function\n definition (see section *Function definitions*). It should be\n called with an argument list containing the same number of items\n as the function\'s formal parameter list.\n\n Special attributes:\n\n +---------------------------+---------------------------------+-------------+\n | Attribute | Meaning | |\n +===========================+=================================+=============+\n | ``__doc__`` | The function\'s documentation | Writable |\n | | string, or ``None`` if | |\n | | unavailable | |\n +---------------------------+---------------------------------+-------------+\n | ``__name__`` | The function\'s name | Writable |\n +---------------------------+---------------------------------+-------------+\n | ``__module__`` | The name of the module the | Writable |\n | | function was defined in, or | |\n | | ``None`` if unavailable. | |\n +---------------------------+---------------------------------+-------------+\n | ``__defaults__`` | A tuple containing default | Writable |\n | | argument values for those | |\n | | arguments that have defaults, | |\n | | or ``None`` if no arguments | |\n | | have a default value | |\n +---------------------------+---------------------------------+-------------+\n | ``__code__`` | The code object representing | Writable |\n | | the compiled function body. | |\n +---------------------------+---------------------------------+-------------+\n | ``__globals__`` | A reference to the dictionary | Read-only |\n | | that holds the function\'s | |\n | | global variables --- the global | |\n | | namespace of the module in | |\n | | which the function was defined. | |\n +---------------------------+---------------------------------+-------------+\n | ``__dict__`` | The namespace supporting | Writable |\n | | arbitrary function attributes. | |\n +---------------------------+---------------------------------+-------------+\n | ``__closure__`` | ``None`` or a tuple of cells | Read-only |\n | | that contain bindings for the | |\n | | function\'s free variables. | |\n +---------------------------+---------------------------------+-------------+\n | ``__annotations__`` | A dict containing annotations | Writable |\n | | of parameters. The keys of the | |\n | | dict are the parameter names, | |\n | | or ``\'return\'`` for the return | |\n | | annotation, if provided. | |\n +---------------------------+---------------------------------+-------------+\n | ``__kwdefaults__`` | A dict containing defaults for | Writable |\n | | keyword-only parameters. | |\n +---------------------------+---------------------------------+-------------+\n\n Most of the attributes labelled "Writable" check the type of the\n assigned value.\n\n Function objects also support getting and setting arbitrary\n attributes, which can be used, for example, to attach metadata\n to functions. Regular attribute dot-notation is used to get and\n set such attributes. *Note that the current implementation only\n supports function attributes on user-defined functions. Function\n attributes on built-in functions may be supported in the\n future.*\n\n Additional information about a function\'s definition can be\n retrieved from its code object; see the description of internal\n types below.\n\n Instance methods\n An instance method object combines a class, a class instance and\n any callable object (normally a user-defined function).\n\n Special read-only attributes: ``__self__`` is the class instance\n object, ``__func__`` is the function object; ``__doc__`` is the\n method\'s documentation (same as ``__func__.__doc__``);\n ``__name__`` is the method name (same as ``__func__.__name__``);\n ``__module__`` is the name of the module the method was defined\n in, or ``None`` if unavailable.\n\n Methods also support accessing (but not setting) the arbitrary\n function attributes on the underlying function object.\n\n User-defined method objects may be created when getting an\n attribute of a class (perhaps via an instance of that class), if\n that attribute is a user-defined function object or a class\n method object.\n\n When an instance method object is created by retrieving a user-\n defined function object from a class via one of its instances,\n its ``__self__`` attribute is the instance, and the method\n object is said to be bound. The new method\'s ``__func__``\n attribute is the original function object.\n\n When a user-defined method object is created by retrieving\n another method object from a class or instance, the behaviour is\n the same as for a function object, except that the ``__func__``\n attribute of the new instance is not the original method object\n but its ``__func__`` attribute.\n\n When an instance method object is created by retrieving a class\n method object from a class or instance, its ``__self__``\n attribute is the class itself, and its ``__func__`` attribute is\n the function object underlying the class method.\n\n When an instance method object is called, the underlying\n function (``__func__``) is called, inserting the class instance\n (``__self__``) in front of the argument list. For instance,\n when ``C`` is a class which contains a definition for a function\n ``f()``, and ``x`` is an instance of ``C``, calling ``x.f(1)``\n is equivalent to calling ``C.f(x, 1)``.\n\n When an instance method object is derived from a class method\n object, the "class instance" stored in ``__self__`` will\n actually be the class itself, so that calling either ``x.f(1)``\n or ``C.f(1)`` is equivalent to calling ``f(C,1)`` where ``f`` is\n the underlying function.\n\n Note that the transformation from function object to instance\n method object happens each time the attribute is retrieved from\n the instance. In some cases, a fruitful optimization is to\n assign the attribute to a local variable and call that local\n variable. Also notice that this transformation only happens for\n user-defined functions; other callable objects (and all non-\n callable objects) are retrieved without transformation. It is\n also important to note that user-defined functions which are\n attributes of a class instance are not converted to bound\n methods; this *only* happens when the function is an attribute\n of the class.\n\n Generator functions\n A function or method which uses the ``yield`` statement (see\n section *The yield statement*) is called a *generator function*.\n Such a function, when called, always returns an iterator object\n which can be used to execute the body of the function: calling\n the iterator\'s ``__next__()`` method will cause the function to\n execute until it provides a value using the ``yield`` statement.\n When the function executes a ``return`` statement or falls off\n the end, a ``StopIteration`` exception is raised and the\n iterator will have reached the end of the set of values to be\n returned.\n\n Built-in functions\n A built-in function object is a wrapper around a C function.\n Examples of built-in functions are ``len()`` and ``math.sin()``\n (``math`` is a standard built-in module). The number and type of\n the arguments are determined by the C function. Special read-\n only attributes: ``__doc__`` is the function\'s documentation\n string, or ``None`` if unavailable; ``__name__`` is the\n function\'s name; ``__self__`` is set to ``None`` (but see the\n next item); ``__module__`` is the name of the module the\n function was defined in or ``None`` if unavailable.\n\n Built-in methods\n This is really a different disguise of a built-in function, this\n time containing an object passed to the C function as an\n implicit extra argument. An example of a built-in method is\n ``alist.append()``, assuming *alist* is a list object. In this\n case, the special read-only attribute ``__self__`` is set to the\n object denoted by *list*.\n\n Classes\n Classes are callable. These objects normally act as factories\n for new instances of themselves, but variations are possible for\n class types that override ``__new__()``. The arguments of the\n call are passed to ``__new__()`` and, in the typical case, to\n ``__init__()`` to initialize the new instance.\n\n Class Instances\n Instances of arbitrary classes can be made callable by defining\n a ``__call__()`` method in their class.\n\nModules\n Modules are imported by the ``import`` statement (see section *The\n import statement*). A module object has a namespace implemented by\n a dictionary object (this is the dictionary referenced by the\n __globals__ attribute of functions defined in the module).\n Attribute references are translated to lookups in this dictionary,\n e.g., ``m.x`` is equivalent to ``m.__dict__["x"]``. A module object\n does not contain the code object used to initialize the module\n (since it isn\'t needed once the initialization is done).\n\n Attribute assignment updates the module\'s namespace dictionary,\n e.g., ``m.x = 1`` is equivalent to ``m.__dict__["x"] = 1``.\n\n Special read-only attribute: ``__dict__`` is the module\'s namespace\n as a dictionary object.\n\n Predefined (writable) attributes: ``__name__`` is the module\'s\n name; ``__doc__`` is the module\'s documentation string, or ``None``\n if unavailable; ``__file__`` is the pathname of the file from which\n the module was loaded, if it was loaded from a file. The\n ``__file__`` attribute is not present for C modules that are\n statically linked into the interpreter; for extension modules\n loaded dynamically from a shared library, it is the pathname of the\n shared library file.\n\nCustom classes\n Custom class types are typically created by class definitions (see\n section *Class definitions*). A class has a namespace implemented\n by a dictionary object. Class attribute references are translated\n to lookups in this dictionary, e.g., ``C.x`` is translated to\n ``C.__dict__["x"]`` (although there are a number of hooks which\n allow for other means of locating attributes). When the attribute\n name is not found there, the attribute search continues in the base\n classes. This search of the base classes uses the C3 method\n resolution order which behaves correctly even in the presence of\n \'diamond\' inheritance structures where there are multiple\n inheritance paths leading back to a common ancestor. Additional\n details on the C3 MRO used by Python can be found in the\n documentation accompanying the 2.3 release at\n http://www.python.org/download/releases/2.3/mro/.\n\n When a class attribute reference (for class ``C``, say) would yield\n a class method object, it is transformed into an instance method\n object whose ``__self__`` attributes is ``C``. When it would yield\n a static method object, it is transformed into the object wrapped\n by the static method object. See section *Implementing Descriptors*\n for another way in which attributes retrieved from a class may\n differ from those actually contained in its ``__dict__``.\n\n Class attribute assignments update the class\'s dictionary, never\n the dictionary of a base class.\n\n A class object can be called (see above) to yield a class instance\n (see below).\n\n Special attributes: ``__name__`` is the class name; ``__module__``\n is the module name in which the class was defined; ``__dict__`` is\n the dictionary containing the class\'s namespace; ``__bases__`` is a\n tuple (possibly empty or a singleton) containing the base classes,\n in the order of their occurrence in the base class list;\n ``__doc__`` is the class\'s documentation string, or None if\n undefined.\n\nClass instances\n A class instance is created by calling a class object (see above).\n A class instance has a namespace implemented as a dictionary which\n is the first place in which attribute references are searched.\n When an attribute is not found there, and the instance\'s class has\n an attribute by that name, the search continues with the class\n attributes. If a class attribute is found that is a user-defined\n function object, it is transformed into an instance method object\n whose ``__self__`` attribute is the instance. Static method and\n class method objects are also transformed; see above under\n "Classes". See section *Implementing Descriptors* for another way\n in which attributes of a class retrieved via its instances may\n differ from the objects actually stored in the class\'s\n ``__dict__``. If no class attribute is found, and the object\'s\n class has a ``__getattr__()`` method, that is called to satisfy the\n lookup.\n\n Attribute assignments and deletions update the instance\'s\n dictionary, never a class\'s dictionary. If the class has a\n ``__setattr__()`` or ``__delattr__()`` method, this is called\n instead of updating the instance dictionary directly.\n\n Class instances can pretend to be numbers, sequences, or mappings\n if they have methods with certain special names. See section\n *Special method names*.\n\n Special attributes: ``__dict__`` is the attribute dictionary;\n ``__class__`` is the instance\'s class.\n\nI/O objects (also known as file objects)\n A file object represents an open file. Various shortcuts are\n available to create file objects: the ``open()`` built-in function,\n and also ``os.popen()``, ``os.fdopen()``, and the ``makefile()``\n method of socket objects (and perhaps by other functions or methods\n provided by extension modules).\n\n The objects ``sys.stdin``, ``sys.stdout`` and ``sys.stderr`` are\n initialized to file objects corresponding to the interpreter\'s\n standard input, output and error streams; they are all open in text\n mode and therefore follow the interface defined by the\n ``io.TextIOBase`` abstract class.\n\nInternal types\n A few types used internally by the interpreter are exposed to the\n user. Their definitions may change with future versions of the\n interpreter, but they are mentioned here for completeness.\n\n Code objects\n Code objects represent *byte-compiled* executable Python code,\n or *bytecode*. The difference between a code object and a\n function object is that the function object contains an explicit\n reference to the function\'s globals (the module in which it was\n defined), while a code object contains no context; also the\n default argument values are stored in the function object, not\n in the code object (because they represent values calculated at\n run-time). Unlike function objects, code objects are immutable\n and contain no references (directly or indirectly) to mutable\n objects.\n\n Special read-only attributes: ``co_name`` gives the function\n name; ``co_argcount`` is the number of positional arguments\n (including arguments with default values); ``co_nlocals`` is the\n number of local variables used by the function (including\n arguments); ``co_varnames`` is a tuple containing the names of\n the local variables (starting with the argument names);\n ``co_cellvars`` is a tuple containing the names of local\n variables that are referenced by nested functions;\n ``co_freevars`` is a tuple containing the names of free\n variables; ``co_code`` is a string representing the sequence of\n bytecode instructions; ``co_consts`` is a tuple containing the\n literals used by the bytecode; ``co_names`` is a tuple\n containing the names used by the bytecode; ``co_filename`` is\n the filename from which the code was compiled;\n ``co_firstlineno`` is the first line number of the function;\n ``co_lnotab`` is a string encoding the mapping from bytecode\n offsets to line numbers (for details see the source code of the\n interpreter); ``co_stacksize`` is the required stack size\n (including local variables); ``co_flags`` is an integer encoding\n a number of flags for the interpreter.\n\n The following flag bits are defined for ``co_flags``: bit\n ``0x04`` is set if the function uses the ``*arguments`` syntax\n to accept an arbitrary number of positional arguments; bit\n ``0x08`` is set if the function uses the ``**keywords`` syntax\n to accept arbitrary keyword arguments; bit ``0x20`` is set if\n the function is a generator.\n\n Future feature declarations (``from __future__ import\n division``) also use bits in ``co_flags`` to indicate whether a\n code object was compiled with a particular feature enabled: bit\n ``0x2000`` is set if the function was compiled with future\n division enabled; bits ``0x10`` and ``0x1000`` were used in\n earlier versions of Python.\n\n Other bits in ``co_flags`` are reserved for internal use.\n\n If a code object represents a function, the first item in\n ``co_consts`` is the documentation string of the function, or\n ``None`` if undefined.\n\n Frame objects\n Frame objects represent execution frames. They may occur in\n traceback objects (see below).\n\n Special read-only attributes: ``f_back`` is to the previous\n stack frame (towards the caller), or ``None`` if this is the\n bottom stack frame; ``f_code`` is the code object being executed\n in this frame; ``f_locals`` is the dictionary used to look up\n local variables; ``f_globals`` is used for global variables;\n ``f_builtins`` is used for built-in (intrinsic) names;\n ``f_lasti`` gives the precise instruction (this is an index into\n the bytecode string of the code object).\n\n Special writable attributes: ``f_trace``, if not ``None``, is a\n function called at the start of each source code line (this is\n used by the debugger); ``f_lineno`` is the current line number\n of the frame --- writing to this from within a trace function\n jumps to the given line (only for the bottom-most frame). A\n debugger can implement a Jump command (aka Set Next Statement)\n by writing to f_lineno.\n\n Traceback objects\n Traceback objects represent a stack trace of an exception. A\n traceback object is created when an exception occurs. When the\n search for an exception handler unwinds the execution stack, at\n each unwound level a traceback object is inserted in front of\n the current traceback. When an exception handler is entered,\n the stack trace is made available to the program. (See section\n *The try statement*.) It is accessible as the third item of the\n tuple returned by ``sys.exc_info()``. When the program contains\n no suitable handler, the stack trace is written (nicely\n formatted) to the standard error stream; if the interpreter is\n interactive, it is also made available to the user as\n ``sys.last_traceback``.\n\n Special read-only attributes: ``tb_next`` is the next level in\n the stack trace (towards the frame where the exception\n occurred), or ``None`` if there is no next level; ``tb_frame``\n points to the execution frame of the current level;\n ``tb_lineno`` gives the line number where the exception\n occurred; ``tb_lasti`` indicates the precise instruction. The\n line number and last instruction in the traceback may differ\n from the line number of its frame object if the exception\n occurred in a ``try`` statement with no matching except clause\n or with a finally clause.\n\n Slice objects\n Slice objects are used to represent slices for ``__getitem__()``\n methods. They are also created by the built-in ``slice()``\n function.\n\n Special read-only attributes: ``start`` is the lower bound;\n ``stop`` is the upper bound; ``step`` is the step value; each is\n ``None`` if omitted. These attributes can have any type.\n\n Slice objects support one method:\n\n slice.indices(self, length)\n\n This method takes a single integer argument *length* and\n computes information about the slice that the slice object\n would describe if applied to a sequence of *length* items.\n It returns a tuple of three integers; respectively these are\n the *start* and *stop* indices and the *step* or stride\n length of the slice. Missing or out-of-bounds indices are\n handled in a manner consistent with regular slices.\n\n Static method objects\n Static method objects provide a way of defeating the\n transformation of function objects to method objects described\n above. A static method object is a wrapper around any other\n object, usually a user-defined method object. When a static\n method object is retrieved from a class or a class instance, the\n object actually returned is the wrapped object, which is not\n subject to any further transformation. Static method objects are\n not themselves callable, although the objects they wrap usually\n are. Static method objects are created by the built-in\n ``staticmethod()`` constructor.\n\n Class method objects\n A class method object, like a static method object, is a wrapper\n around another object that alters the way in which that object\n is retrieved from classes and class instances. The behaviour of\n class method objects upon such retrieval is described above,\n under "User-defined methods". Class method objects are created\n by the built-in ``classmethod()`` constructor.\n', 'typesfunctions': '\nFunctions\n*********\n\nFunction objects are created by function definitions. The only\noperation on a function object is to call it: ``func(argument-list)``.\n\nThere are really two flavors of function objects: built-in functions\nand user-defined functions. Both support the same operation (to call\nthe function), but the implementation is different, hence the\ndifferent object types.\n\nSee *Function definitions* for more information.\n', - 'typesmapping': '\nMapping Types --- ``dict``\n**************************\n\nA *mapping* object maps *hashable* values to arbitrary objects.\nMappings are mutable objects. There is currently only one standard\nmapping type, the *dictionary*. (For other containers see the built\nin ``list``, ``set``, and ``tuple`` classes, and the ``collections``\nmodule.)\n\nA dictionary\'s keys are *almost* arbitrary values. Values that are\nnot *hashable*, that is, values containing lists, dictionaries or\nother mutable types (that are compared by value rather than by object\nidentity) may not be used as keys. Numeric types used for keys obey\nthe normal rules for numeric comparison: if two numbers compare equal\n(such as ``1`` and ``1.0``) then they can be used interchangeably to\nindex the same dictionary entry. (Note however, that since computers\nstore floating-point numbers as approximations it is usually unwise to\nuse them as dictionary keys.)\n\nDictionaries can be created by placing a comma-separated list of\n``key: value`` pairs within braces, for example: ``{\'jack\': 4098,\n\'sjoerd\': 4127}`` or ``{4098: \'jack\', 4127: \'sjoerd\'}``, or by the\n``dict`` constructor.\n\nclass class dict([arg])\n\n Return a new dictionary initialized from an optional positional\n argument or from a set of keyword arguments. If no arguments are\n given, return a new empty dictionary. If the positional argument\n *arg* is a mapping object, return a dictionary mapping the same\n keys to the same values as does the mapping object. Otherwise the\n positional argument must be a sequence, a container that supports\n iteration, or an iterator object. The elements of the argument\n must each also be of one of those kinds, and each must in turn\n contain exactly two objects. The first is used as a key in the new\n dictionary, and the second as the key\'s value. If a given key is\n seen more than once, the last value associated with it is retained\n in the new dictionary.\n\n If keyword arguments are given, the keywords themselves with their\n associated values are added as items to the dictionary. If a key\n is specified both in the positional argument and as a keyword\n argument, the value associated with the keyword is retained in the\n dictionary. For example, these all return a dictionary equal to\n ``{"one": 2, "two": 3}``:\n\n * ``dict(one=2, two=3)``\n\n * ``dict({\'one\': 2, \'two\': 3})``\n\n * ``dict(zip((\'one\', \'two\'), (2, 3)))``\n\n * ``dict([[\'two\', 3], [\'one\', 2]])``\n\n The first example only works for keys that are valid Python\n identifiers; the others work with any valid keys.\n\n These are the operations that dictionaries support (and therefore,\n custom mapping types should support too):\n\n len(d)\n\n Return the number of items in the dictionary *d*.\n\n d[key]\n\n Return the item of *d* with key *key*. Raises a ``KeyError`` if\n *key* is not in the map.\n\n If a subclass of dict defines a method ``__missing__()``, if the\n key *key* is not present, the ``d[key]`` operation calls that\n method with the key *key* as argument. The ``d[key]`` operation\n then returns or raises whatever is returned or raised by the\n ``__missing__(key)`` call if the key is not present. No other\n operations or methods invoke ``__missing__()``. If\n ``__missing__()`` is not defined, ``KeyError`` is raised.\n ``__missing__()`` must be a method; it cannot be an instance\n variable. For an example, see ``collections.defaultdict``.\n\n d[key] = value\n\n Set ``d[key]`` to *value*.\n\n del d[key]\n\n Remove ``d[key]`` from *d*. Raises a ``KeyError`` if *key* is\n not in the map.\n\n key in d\n\n Return ``True`` if *d* has a key *key*, else ``False``.\n\n key not in d\n\n Equivalent to ``not key in d``.\n\n iter(d)\n\n Return an iterator over the keys of the dictionary. This is a\n shortcut for ``iter(d.keys())``.\n\n clear()\n\n Remove all items from the dictionary.\n\n copy()\n\n Return a shallow copy of the dictionary.\n\n classmethod fromkeys(seq[, value])\n\n Create a new dictionary with keys from *seq* and values set to\n *value*.\n\n ``fromkeys()`` is a class method that returns a new dictionary.\n *value* defaults to ``None``.\n\n get(key[, default])\n\n Return the value for *key* if *key* is in the dictionary, else\n *default*. If *default* is not given, it defaults to ``None``,\n so that this method never raises a ``KeyError``.\n\n items()\n\n Return a new view of the dictionary\'s items (``(key, value)``\n pairs). See below for documentation of view objects.\n\n keys()\n\n Return a new view of the dictionary\'s keys. See below for\n documentation of view objects.\n\n pop(key[, default])\n\n If *key* is in the dictionary, remove it and return its value,\n else return *default*. If *default* is not given and *key* is\n not in the dictionary, a ``KeyError`` is raised.\n\n popitem()\n\n Remove and return an arbitrary ``(key, value)`` pair from the\n dictionary.\n\n ``popitem()`` is useful to destructively iterate over a\n dictionary, as often used in set algorithms. If the dictionary\n is empty, calling ``popitem()`` raises a ``KeyError``.\n\n setdefault(key[, default])\n\n If *key* is in the dictionary, return its value. If not, insert\n *key* with a value of *default* and return *default*. *default*\n defaults to ``None``.\n\n update([other])\n\n Update the dictionary with the key/value pairs from *other*,\n overwriting existing keys. Return ``None``.\n\n ``update()`` accepts either another dictionary object or an\n iterable of key/value pairs (as a tuple or other iterable of\n length two). If keyword arguments are specified, the\n dictionary is then updated with those key/value pairs:\n ``d.update(red=1, blue=2)``.\n\n values()\n\n Return a new view of the dictionary\'s values. See below for\n documentation of view objects.\n\n\nDictionary view objects\n=======================\n\nThe objects returned by ``dict.keys()``, ``dict.values()`` and\n``dict.items()`` are *view objects*. They provide a dynamic view on\nthe dictionary\'s entries, which means that when the dictionary\nchanges, the view reflects these changes.\n\nDictionary views can be iterated over to yield their respective data,\nand support membership tests:\n\nlen(dictview)\n\n Return the number of entries in the dictionary.\n\niter(dictview)\n\n Return an iterator over the keys, values or items (represented as\n tuples of ``(key, value)``) in the dictionary.\n\n Keys and values are iterated over in an arbitrary order which is\n non-random, varies across Python implementations, and depends on\n the dictionary\'s history of insertions and deletions. If keys,\n values and items views are iterated over with no intervening\n modifications to the dictionary, the order of items will directly\n correspond. This allows the creation of ``(value, key)`` pairs\n using ``zip()``: ``pairs = zip(d.values(), d.keys())``. Another\n way to create the same list is ``pairs = [(v, k) for (k, v) in\n d.items()]``.\n\n Iterating views while adding or deleting entries in the dictionary\n may raise a ``RuntimeError`` or fail to iterate over all entries.\n\nx in dictview\n\n Return ``True`` if *x* is in the underlying dictionary\'s keys,\n values or items (in the latter case, *x* should be a ``(key,\n value)`` tuple).\n\nKeys views are set-like since their entries are unique and hashable.\nIf all values are hashable, so that (key, value) pairs are unique and\nhashable, then the items view is also set-like. (Values views are not\ntreated as set-like since the entries are generally not unique.) Then\nthese set operations are available ("other" refers either to another\nview or a set):\n\ndictview & other\n\n Return the intersection of the dictview and the other object as a\n new set.\n\ndictview | other\n\n Return the union of the dictview and the other object as a new set.\n\ndictview - other\n\n Return the difference between the dictview and the other object\n (all elements in *dictview* that aren\'t in *other*) as a new set.\n\ndictview ^ other\n\n Return the symmetric difference (all elements either in *dictview*\n or *other*, but not in both) of the dictview and the other object\n as a new set.\n\nAn example of dictionary view usage:\n\n >>> dishes = {\'eggs\': 2, \'sausage\': 1, \'bacon\': 1, \'spam\': 500}\n >>> keys = dishes.keys()\n >>> values = dishes.values()\n\n >>> # iteration\n >>> n = 0\n >>> for val in values:\n ... n += val\n >>> print(n)\n 504\n\n >>> # keys and values are iterated over in the same order\n >>> list(keys)\n [\'eggs\', \'bacon\', \'sausage\', \'spam\']\n >>> list(values)\n [2, 1, 1, 500]\n\n >>> # view objects are dynamic and reflect dict changes\n >>> del dishes[\'eggs\']\n >>> del dishes[\'sausage\']\n >>> list(keys)\n [\'spam\', \'bacon\']\n\n >>> # set operations\n >>> keys & {\'eggs\', \'bacon\', \'salad\'}\n {\'bacon\'}\n', + 'typesmapping': '\nMapping Types --- ``dict``\n**************************\n\nA *mapping* object maps *hashable* values to arbitrary objects.\nMappings are mutable objects. There is currently only one standard\nmapping type, the *dictionary*. (For other containers see the built\nin ``list``, ``set``, and ``tuple`` classes, and the ``collections``\nmodule.)\n\nA dictionary\'s keys are *almost* arbitrary values. Values that are\nnot *hashable*, that is, values containing lists, dictionaries or\nother mutable types (that are compared by value rather than by object\nidentity) may not be used as keys. Numeric types used for keys obey\nthe normal rules for numeric comparison: if two numbers compare equal\n(such as ``1`` and ``1.0``) then they can be used interchangeably to\nindex the same dictionary entry. (Note however, that since computers\nstore floating-point numbers as approximations it is usually unwise to\nuse them as dictionary keys.)\n\nDictionaries can be created by placing a comma-separated list of\n``key: value`` pairs within braces, for example: ``{\'jack\': 4098,\n\'sjoerd\': 4127}`` or ``{4098: \'jack\', 4127: \'sjoerd\'}``, or by the\n``dict`` constructor.\n\nclass class dict([arg])\n\n Return a new dictionary initialized from an optional positional\n argument or from a set of keyword arguments. If no arguments are\n given, return a new empty dictionary. If the positional argument\n *arg* is a mapping object, return a dictionary mapping the same\n keys to the same values as does the mapping object. Otherwise the\n positional argument must be a sequence, a container that supports\n iteration, or an iterator object. The elements of the argument\n must each also be of one of those kinds, and each must in turn\n contain exactly two objects. The first is used as a key in the new\n dictionary, and the second as the key\'s value. If a given key is\n seen more than once, the last value associated with it is retained\n in the new dictionary.\n\n If keyword arguments are given, the keywords themselves with their\n associated values are added as items to the dictionary. If a key\n is specified both in the positional argument and as a keyword\n argument, the value associated with the keyword is retained in the\n dictionary. For example, these all return a dictionary equal to\n ``{"one": 2, "two": 3}``:\n\n * ``dict(one=2, two=3)``\n\n * ``dict({\'one\': 2, \'two\': 3})``\n\n * ``dict(zip((\'one\', \'two\'), (2, 3)))``\n\n * ``dict([[\'two\', 3], [\'one\', 2]])``\n\n The first example only works for keys that are valid Python\n identifiers; the others work with any valid keys.\n\n These are the operations that dictionaries support (and therefore,\n custom mapping types should support too):\n\n len(d)\n\n Return the number of items in the dictionary *d*.\n\n d[key]\n\n Return the item of *d* with key *key*. Raises a ``KeyError`` if\n *key* is not in the map.\n\n If a subclass of dict defines a method ``__missing__()``, if the\n key *key* is not present, the ``d[key]`` operation calls that\n method with the key *key* as argument. The ``d[key]`` operation\n then returns or raises whatever is returned or raised by the\n ``__missing__(key)`` call if the key is not present. No other\n operations or methods invoke ``__missing__()``. If\n ``__missing__()`` is not defined, ``KeyError`` is raised.\n ``__missing__()`` must be a method; it cannot be an instance\n variable. For an example, see ``collections.defaultdict``.\n\n d[key] = value\n\n Set ``d[key]`` to *value*.\n\n del d[key]\n\n Remove ``d[key]`` from *d*. Raises a ``KeyError`` if *key* is\n not in the map.\n\n key in d\n\n Return ``True`` if *d* has a key *key*, else ``False``.\n\n key not in d\n\n Equivalent to ``not key in d``.\n\n iter(d)\n\n Return an iterator over the keys of the dictionary. This is a\n shortcut for ``iter(d.keys())``.\n\n clear()\n\n Remove all items from the dictionary.\n\n copy()\n\n Return a shallow copy of the dictionary.\n\n classmethod fromkeys(seq[, value])\n\n Create a new dictionary with keys from *seq* and values set to\n *value*.\n\n ``fromkeys()`` is a class method that returns a new dictionary.\n *value* defaults to ``None``.\n\n get(key[, default])\n\n Return the value for *key* if *key* is in the dictionary, else\n *default*. If *default* is not given, it defaults to ``None``,\n so that this method never raises a ``KeyError``.\n\n items()\n\n Return a new view of the dictionary\'s items (``(key, value)``\n pairs). See below for documentation of view objects.\n\n keys()\n\n Return a new view of the dictionary\'s keys. See below for\n documentation of view objects.\n\n pop(key[, default])\n\n If *key* is in the dictionary, remove it and return its value,\n else return *default*. If *default* is not given and *key* is\n not in the dictionary, a ``KeyError`` is raised.\n\n popitem()\n\n Remove and return an arbitrary ``(key, value)`` pair from the\n dictionary.\n\n ``popitem()`` is useful to destructively iterate over a\n dictionary, as often used in set algorithms. If the dictionary\n is empty, calling ``popitem()`` raises a ``KeyError``.\n\n setdefault(key[, default])\n\n If *key* is in the dictionary, return its value. If not, insert\n *key* with a value of *default* and return *default*. *default*\n defaults to ``None``.\n\n update([other])\n\n Update the dictionary with the key/value pairs from *other*,\n overwriting existing keys. Return ``None``.\n\n ``update()`` accepts either another dictionary object or an\n iterable of key/value pairs (as a tuple or other iterable of\n length two). If keyword arguments are specified, the dictionary\n is then updated with those key/value pairs: ``d.update(red=1,\n blue=2)``.\n\n values()\n\n Return a new view of the dictionary\'s values. See below for\n documentation of view objects.\n\n\nDictionary view objects\n=======================\n\nThe objects returned by ``dict.keys()``, ``dict.values()`` and\n``dict.items()`` are *view objects*. They provide a dynamic view on\nthe dictionary\'s entries, which means that when the dictionary\nchanges, the view reflects these changes.\n\nDictionary views can be iterated over to yield their respective data,\nand support membership tests:\n\nlen(dictview)\n\n Return the number of entries in the dictionary.\n\niter(dictview)\n\n Return an iterator over the keys, values or items (represented as\n tuples of ``(key, value)``) in the dictionary.\n\n Keys and values are iterated over in an arbitrary order which is\n non-random, varies across Python implementations, and depends on\n the dictionary\'s history of insertions and deletions. If keys,\n values and items views are iterated over with no intervening\n modifications to the dictionary, the order of items will directly\n correspond. This allows the creation of ``(value, key)`` pairs\n using ``zip()``: ``pairs = zip(d.values(), d.keys())``. Another\n way to create the same list is ``pairs = [(v, k) for (k, v) in\n d.items()]``.\n\n Iterating views while adding or deleting entries in the dictionary\n may raise a ``RuntimeError`` or fail to iterate over all entries.\n\nx in dictview\n\n Return ``True`` if *x* is in the underlying dictionary\'s keys,\n values or items (in the latter case, *x* should be a ``(key,\n value)`` tuple).\n\nKeys views are set-like since their entries are unique and hashable.\nIf all values are hashable, so that (key, value) pairs are unique and\nhashable, then the items view is also set-like. (Values views are not\ntreated as set-like since the entries are generally not unique.) Then\nthese set operations are available ("other" refers either to another\nview or a set):\n\ndictview & other\n\n Return the intersection of the dictview and the other object as a\n new set.\n\ndictview | other\n\n Return the union of the dictview and the other object as a new set.\n\ndictview - other\n\n Return the difference between the dictview and the other object\n (all elements in *dictview* that aren\'t in *other*) as a new set.\n\ndictview ^ other\n\n Return the symmetric difference (all elements either in *dictview*\n or *other*, but not in both) of the dictview and the other object\n as a new set.\n\ndictview.isdisjoint(other)\n\n Return True if the view has no elements in common with *other*.\n Sets are disjoint if and only if their intersection is the empty\n set.\n\nAn example of dictionary view usage:\n\n >>> dishes = {\'eggs\': 2, \'sausage\': 1, \'bacon\': 1, \'spam\': 500}\n >>> keys = dishes.keys()\n >>> values = dishes.values()\n\n >>> # iteration\n >>> n = 0\n >>> for val in values:\n ... n += val\n >>> print(n)\n 504\n\n >>> # keys and values are iterated over in the same order\n >>> list(keys)\n [\'eggs\', \'bacon\', \'sausage\', \'spam\']\n >>> list(values)\n [2, 1, 1, 500]\n\n >>> # view objects are dynamic and reflect dict changes\n >>> del dishes[\'eggs\']\n >>> del dishes[\'sausage\']\n >>> list(keys)\n [\'spam\', \'bacon\']\n\n >>> # set operations\n >>> keys & {\'eggs\', \'bacon\', \'salad\'}\n {\'bacon\'}\n', 'typesmethods': "\nMethods\n*******\n\nMethods are functions that are called using the attribute notation.\nThere are two flavors: built-in methods (such as ``append()`` on\nlists) and class instance methods. Built-in methods are described\nwith the types that support them.\n\nIf you access a method (a function defined in a class namespace)\nthrough an instance, you get a special object: a *bound method* (also\ncalled *instance method*) object. When called, it will add the\n``self`` argument to the argument list. Bound methods have two\nspecial read-only attributes: ``m.__self__`` is the object on which\nthe method operates, and ``m.__func__`` is the function implementing\nthe method. Calling ``m(arg-1, arg-2, ..., arg-n)`` is completely\nequivalent to calling ``m.__func__(m.__self__, arg-1, arg-2, ...,\narg-n)``.\n\nLike function objects, bound method objects support getting arbitrary\nattributes. However, since method attributes are actually stored on\nthe underlying function object (``meth.__func__``), setting method\nattributes on bound methods is disallowed. Attempting to set a method\nattribute results in a ``TypeError`` being raised. In order to set a\nmethod attribute, you need to explicitly set it on the underlying\nfunction object:\n\n class C:\n def method(self):\n pass\n\n c = C()\n c.method.__func__.whoami = 'my name is c'\n\nSee *The standard type hierarchy* for more information.\n", 'typesmodules': "\nModules\n*******\n\nThe only special operation on a module is attribute access:\n``m.name``, where *m* is a module and *name* accesses a name defined\nin *m*'s symbol table. Module attributes can be assigned to. (Note\nthat the ``import`` statement is not, strictly speaking, an operation\non a module object; ``import foo`` does not require a module object\nnamed *foo* to exist, rather it requires an (external) *definition*\nfor a module named *foo* somewhere.)\n\nA special member of every module is ``__dict__``. This is the\ndictionary containing the module's symbol table. Modifying this\ndictionary will actually change the module's symbol table, but direct\nassignment to the ``__dict__`` attribute is not possible (you can\nwrite ``m.__dict__['a'] = 1``, which defines ``m.a`` to be ``1``, but\nyou can't write ``m.__dict__ = {}``). Modifying ``__dict__`` directly\nis not recommended.\n\nModules built into the interpreter are written like this: ````. If loaded from a file, they are written as\n````.\n", - 'typesseq': '\nSequence Types --- ``str``, ``bytes``, ``bytearray``, ``list``, ``tuple``, ``range``\n************************************************************************************\n\nThere are six sequence types: strings, byte sequences (``bytes``\nobjects), byte arrays (``bytearray`` objects), lists, tuples, and\nrange objects. For other containers see the built in ``dict`` and\n``set`` classes, and the ``collections`` module.\n\nStrings contain Unicode characters. Their literals are written in\nsingle or double quotes: ``\'xyzzy\'``, ``"frobozz"``. See *String and\nBytes literals* for more about string literals. In addition to the\nfunctionality described here, there are also string-specific methods\ndescribed in the *String Methods* section.\n\nBytes and bytearray objects contain single bytes -- the former is\nimmutable while the latter is a mutable sequence. Bytes objects can\nbe constructed the constructor, ``bytes()``, and from literals; use a\n``b`` prefix with normal string syntax: ``b\'xyzzy\'``. To construct\nbyte arrays, use the ``bytearray()`` function.\n\nWarning: While string objects are sequences of characters (represented by\n strings of length 1), bytes and bytearray objects are sequences of\n *integers* (between 0 and 255), representing the ASCII value of\n single bytes. That means that for a bytes or bytearray object *b*,\n ``b[0]`` will be an integer, while ``b[0:1]`` will be a bytes or\n bytearray object of length 1. The representation of bytes objects\n uses the literal format (``b\'...\'``) since it is generally more\n useful than e.g. ``bytes([50, 19, 100])``. You can always convert a\n bytes object into a list of integers using ``list(b)``.Also, while\n in previous Python versions, byte strings and Unicode strings could\n be exchanged for each other rather freely (barring encoding issues),\n strings and bytes are now completely separate concepts. There\'s no\n implicit en-/decoding if you pass an object of the wrong type. A\n string always compares unequal to a bytes or bytearray object.\n\nLists are constructed with square brackets, separating items with\ncommas: ``[a, b, c]``. Tuples are constructed by the comma operator\n(not within square brackets), with or without enclosing parentheses,\nbut an empty tuple must have the enclosing parentheses, such as ``a,\nb, c`` or ``()``. A single item tuple must have a trailing comma,\nsuch as ``(d,)``.\n\nObjects of type range are created using the ``range()`` function.\nThey don\'t support slicing, concatenation or repetition, and using\n``in``, ``not in``, ``min()`` or ``max()`` on them is inefficient.\n\nMost sequence types support the following operations. The ``in`` and\n``not in`` operations have the same priorities as the comparison\noperations. The ``+`` and ``*`` operations have the same priority as\nthe corresponding numeric operations. [3] Additional methods are\nprovided for *Mutable Sequence Types*.\n\nThis table lists the sequence operations sorted in ascending priority\n(operations in the same box have the same priority). In the table,\n*s* and *t* are sequences of the same type; *n*, *i* and *j* are\nintegers:\n\n+--------------------+----------------------------------+------------+\n| Operation | Result | Notes |\n+====================+==================================+============+\n| ``x in s`` | ``True`` if an item of *s* is | (1) |\n| | equal to *x*, else ``False`` | |\n+--------------------+----------------------------------+------------+\n| ``x not in s`` | ``False`` if an item of *s* is | (1) |\n| | equal to *x*, else ``True`` | |\n+--------------------+----------------------------------+------------+\n| ``s + t`` | the concatenation of *s* and *t* | (6) |\n+--------------------+----------------------------------+------------+\n| ``s * n, n * s`` | *n* shallow copies of *s* | (2) |\n| | concatenated | |\n+--------------------+----------------------------------+------------+\n| ``s[i]`` | *i*\'th item of *s*, origin 0 | (3) |\n+--------------------+----------------------------------+------------+\n| ``s[i:j]`` | slice of *s* from *i* to *j* | (3)(4) |\n+--------------------+----------------------------------+------------+\n| ``s[i:j:k]`` | slice of *s* from *i* to *j* | (3)(5) |\n| | with step *k* | |\n+--------------------+----------------------------------+------------+\n| ``len(s)`` | length of *s* | |\n+--------------------+----------------------------------+------------+\n| ``min(s)`` | smallest item of *s* | |\n+--------------------+----------------------------------+------------+\n| ``max(s)`` | largest item of *s* | |\n+--------------------+----------------------------------+------------+\n\nSequence types also support comparisons. In particular, tuples and\nlists are compared lexicographically by comparing corresponding\nelements. This means that to compare equal, every element must\ncompare equal and the two sequences must be of the same type and have\nthe same length. (For full details see *Comparisons* in the language\nreference.)\n\nNotes:\n\n1. When *s* is a string object, the ``in`` and ``not in`` operations\n act like a substring test.\n\n2. Values of *n* less than ``0`` are treated as ``0`` (which yields an\n empty sequence of the same type as *s*). Note also that the copies\n are shallow; nested structures are not copied. This often haunts\n new Python programmers; consider:\n\n >>> lists = [[]] * 3\n >>> lists\n [[], [], []]\n >>> lists[0].append(3)\n >>> lists\n [[3], [3], [3]]\n\n What has happened is that ``[[]]`` is a one-element list containing\n an empty list, so all three elements of ``[[]] * 3`` are (pointers\n to) this single empty list. Modifying any of the elements of\n ``lists`` modifies this single list. You can create a list of\n different lists this way:\n\n >>> lists = [[] for i in range(3)]\n >>> lists[0].append(3)\n >>> lists[1].append(5)\n >>> lists[2].append(7)\n >>> lists\n [[3], [5], [7]]\n\n3. If *i* or *j* is negative, the index is relative to the end of the\n string: ``len(s) + i`` or ``len(s) + j`` is substituted. But note\n that ``-0`` is still ``0``.\n\n4. The slice of *s* from *i* to *j* is defined as the sequence of\n items with index *k* such that ``i <= k < j``. If *i* or *j* is\n greater than ``len(s)``, use ``len(s)``. If *i* is omitted or\n ``None``, use ``0``. If *j* is omitted or ``None``, use\n ``len(s)``. If *i* is greater than or equal to *j*, the slice is\n empty.\n\n5. The slice of *s* from *i* to *j* with step *k* is defined as the\n sequence of items with index ``x = i + n*k`` such that ``0 <= n <\n (j-i)/k``. In other words, the indices are ``i``, ``i+k``,\n ``i+2*k``, ``i+3*k`` and so on, stopping when *j* is reached (but\n never including *j*). If *i* or *j* is greater than ``len(s)``,\n use ``len(s)``. If *i* or *j* are omitted or ``None``, they become\n "end" values (which end depends on the sign of *k*). Note, *k*\n cannot be zero. If *k* is ``None``, it is treated like ``1``.\n\n6. **CPython implementation detail:** If *s* and *t* are both strings,\n some Python implementations such as CPython can usually perform an\n in-place optimization for assignments of the form ``s = s + t`` or\n ``s += t``. When applicable, this optimization makes quadratic\n run-time much less likely. This optimization is both version and\n implementation dependent. For performance sensitive code, it is\n preferable to use the ``str.join()`` method which assures\n consistent linear concatenation performance across versions and\n implementations.\n\n\nString Methods\n==============\n\nString objects support the methods listed below.\n\nIn addition, Python\'s strings support the sequence type methods\ndescribed in the *Sequence Types --- str, bytes, bytearray, list,\ntuple, range* section. To output formatted strings, see the *String\nFormatting* section. Also, see the ``re`` module for string functions\nbased on regular expressions.\n\nstr.capitalize()\n\n Return a copy of the string with its first character capitalized\n and the rest lowercased.\n\nstr.center(width[, fillchar])\n\n Return centered in a string of length *width*. Padding is done\n using the specified *fillchar* (default is a space).\n\nstr.count(sub[, start[, end]])\n\n Return the number of non-overlapping occurrences of substring *sub*\n in the range [*start*, *end*]. Optional arguments *start* and\n *end* are interpreted as in slice notation.\n\nstr.encode(encoding=sys.getdefaultencoding(), errors="strict")\n\n Return an encoded version of the string as a bytes object. Default\n encoding is the current default string encoding. *errors* may be\n given to set a different error handling scheme. The default for\n *errors* is ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'``, ``\'xmlcharrefreplace\'``, ``\'backslashreplace\'`` and\n any other name registered via ``codecs.register_error()``, see\n section *Codec Base Classes*. For a list of possible encodings, see\n section *Standard Encodings*.\n\n Changed in version 3.1: Added support for keyword arguments added.\n\nstr.endswith(suffix[, start[, end]])\n\n Return ``True`` if the string ends with the specified *suffix*,\n otherwise return ``False``. *suffix* can also be a tuple of\n suffixes to look for. With optional *start*, test beginning at\n that position. With optional *end*, stop comparing at that\n position.\n\nstr.expandtabs([tabsize])\n\n Return a copy of the string where all tab characters are replaced\n by one or more spaces, depending on the current column and the\n given tab size. The column number is reset to zero after each\n newline occurring in the string. If *tabsize* is not given, a tab\n size of ``8`` characters is assumed. This doesn\'t understand other\n non-printing characters or escape sequences.\n\nstr.find(sub[, start[, end]])\n\n Return the lowest index in the string where substring *sub* is\n found, such that *sub* is contained in the slice ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` if *sub* is not found.\n\nstr.format(*args, **kwargs)\n\n Perform a string formatting operation. The string on which this\n method is called can contain literal text or replacement fields\n delimited by braces ``{}``. Each replacement field contains either\n the numeric index of a positional argument, or the name of a\n keyword argument. Returns a copy of the string where each\n replacement field is replaced with the string value of the\n corresponding argument.\n\n >>> "The sum of 1 + 2 is {0}".format(1+2)\n \'The sum of 1 + 2 is 3\'\n\n See *Format String Syntax* for a description of the various\n formatting options that can be specified in format strings.\n\nstr.index(sub[, start[, end]])\n\n Like ``find()``, but raise ``ValueError`` when the substring is not\n found.\n\nstr.isalnum()\n\n Return true if all characters in the string are alphanumeric and\n there is at least one character, false otherwise.\n\nstr.isalpha()\n\n Return true if all characters in the string are alphabetic and\n there is at least one character, false otherwise.\n\nstr.isdecimal()\n\n Return true if all characters in the string are decimal characters\n and there is at least one character, false otherwise. Decimal\n characters include digit characters, and all characters that that\n can be used to form decimal-radix numbers, e.g. U+0660, ARABIC-\n INDIC DIGIT ZERO.\n\nstr.isdigit()\n\n Return true if all characters in the string are digits and there is\n at least one character, false otherwise.\n\nstr.isidentifier()\n\n Return true if the string is a valid identifier according to the\n language definition, section *Identifiers and keywords*.\n\nstr.islower()\n\n Return true if all cased characters in the string are lowercase and\n there is at least one cased character, false otherwise.\n\nstr.isnumeric()\n\n Return true if all characters in the string are numeric characters,\n and there is at least one character, false otherwise. Numeric\n characters include digit characters, and all characters that have\n the Unicode numeric value property, e.g. U+2155, VULGAR FRACTION\n ONE FIFTH.\n\nstr.isprintable()\n\n Return true if all characters in the string are printable or the\n string is empty, false otherwise. Nonprintable characters are\n those characters defined in the Unicode character database as\n "Other" or "Separator", excepting the ASCII space (0x20) which is\n considered printable. (Note that printable characters in this\n context are those which should not be escaped when ``repr()`` is\n invoked on a string. It has no bearing on the handling of strings\n written to ``sys.stdout`` or ``sys.stderr``.)\n\nstr.isspace()\n\n Return true if there are only whitespace characters in the string\n and there is at least one character, false otherwise.\n\nstr.istitle()\n\n Return true if the string is a titlecased string and there is at\n least one character, for example uppercase characters may only\n follow uncased characters and lowercase characters only cased ones.\n Return false otherwise.\n\nstr.isupper()\n\n Return true if all cased characters in the string are uppercase and\n there is at least one cased character, false otherwise.\n\nstr.join(iterable)\n\n Return a string which is the concatenation of the strings in the\n *iterable* *iterable*. A ``TypeError`` will be raised if there are\n any non-string values in *seq*, including ``bytes`` objects. The\n separator between elements is the string providing this method.\n\nstr.ljust(width[, fillchar])\n\n Return the string left justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\nstr.lower()\n\n Return a copy of the string converted to lowercase.\n\nstr.lstrip([chars])\n\n Return a copy of the string with leading characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a prefix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.lstrip()\n \'spacious \'\n >>> \'www.example.com\'.lstrip(\'cmowz.\')\n \'example.com\'\n\nstatic str.maketrans(x[, y[, z]])\n\n This static method returns a translation table usable for\n ``str.translate()``.\n\n If there is only one argument, it must be a dictionary mapping\n Unicode ordinals (integers) or characters (strings of length 1) to\n Unicode ordinals, strings (of arbitrary lengths) or None.\n Character keys will then be converted to ordinals.\n\n If there are two arguments, they must be strings of equal length,\n and in the resulting dictionary, each character in x will be mapped\n to the character at the same position in y. If there is a third\n argument, it must be a string, whose characters will be mapped to\n None in the result.\n\nstr.partition(sep)\n\n Split the string at the first occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing the string itself, followed by\n two empty strings.\n\nstr.replace(old, new[, count])\n\n Return a copy of the string with all occurrences of substring *old*\n replaced by *new*. If the optional argument *count* is given, only\n the first *count* occurrences are replaced.\n\nstr.rfind(sub[, start[, end]])\n\n Return the highest index in the string where substring *sub* is\n found, such that *sub* is contained within ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` on failure.\n\nstr.rindex(sub[, start[, end]])\n\n Like ``rfind()`` but raises ``ValueError`` when the substring *sub*\n is not found.\n\nstr.rjust(width[, fillchar])\n\n Return the string right justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\nstr.rpartition(sep)\n\n Split the string at the last occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing two empty strings, followed by\n the string itself.\n\nstr.rsplit([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit* splits\n are done, the *rightmost* ones. If *sep* is not specified or\n ``None``, any whitespace string is a separator. Except for\n splitting from the right, ``rsplit()`` behaves like ``split()``\n which is described in detail below.\n\nstr.rstrip([chars])\n\n Return a copy of the string with trailing characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a suffix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.rstrip()\n \' spacious\'\n >>> \'mississippi\'.rstrip(\'ipz\')\n \'mississ\'\n\nstr.split([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit*\n splits are done (thus, the list will have at most ``maxsplit+1``\n elements). If *maxsplit* is not specified, then there is no limit\n on the number of splits (all possible splits are made).\n\n If *sep* is given, consecutive delimiters are not grouped together\n and are deemed to delimit empty strings (for example,\n ``\'1,,2\'.split(\',\')`` returns ``[\'1\', \'\', \'2\']``). The *sep*\n argument may consist of multiple characters (for example,\n ``\'1<>2<>3\'.split(\'<>\')`` returns ``[\'1\', \'2\', \'3\']``). Splitting\n an empty string with a specified separator returns ``[\'\']``.\n\n If *sep* is not specified or is ``None``, a different splitting\n algorithm is applied: runs of consecutive whitespace are regarded\n as a single separator, and the result will contain no empty strings\n at the start or end if the string has leading or trailing\n whitespace. Consequently, splitting an empty string or a string\n consisting of just whitespace with a ``None`` separator returns\n ``[]``.\n\n For example, ``\' 1 2 3 \'.split()`` returns ``[\'1\', \'2\', \'3\']``,\n and ``\' 1 2 3 \'.split(None, 1)`` returns ``[\'1\', \'2 3 \']``.\n\nstr.splitlines([keepends])\n\n Return a list of the lines in the string, breaking at line\n boundaries. Line breaks are not included in the resulting list\n unless *keepends* is given and true.\n\nstr.startswith(prefix[, start[, end]])\n\n Return ``True`` if string starts with the *prefix*, otherwise\n return ``False``. *prefix* can also be a tuple of prefixes to look\n for. With optional *start*, test string beginning at that\n position. With optional *end*, stop comparing string at that\n position.\n\nstr.strip([chars])\n\n Return a copy of the string with the leading and trailing\n characters removed. The *chars* argument is a string specifying the\n set of characters to be removed. If omitted or ``None``, the\n *chars* argument defaults to removing whitespace. The *chars*\n argument is not a prefix or suffix; rather, all combinations of its\n values are stripped:\n\n >>> \' spacious \'.strip()\n \'spacious\'\n >>> \'www.example.com\'.strip(\'cmowz.\')\n \'example\'\n\nstr.swapcase()\n\n Return a copy of the string with uppercase characters converted to\n lowercase and vice versa.\n\nstr.title()\n\n Return a titlecased version of the string where words start with an\n uppercase character and the remaining characters are lowercase.\n\n The algorithm uses a simple language-independent definition of a\n word as groups of consecutive letters. The definition works in\n many contexts but it means that apostrophes in contractions and\n possessives form word boundaries, which may not be the desired\n result:\n\n >>> "they\'re bill\'s friends from the UK".title()\n "They\'Re Bill\'S Friends From The Uk"\n\n A workaround for apostrophes can be constructed using regular\n expressions:\n\n >>> import re\n >>> def titlecase(s):\n return re.sub(r"[A-Za-z]+(\'[A-Za-z]+)?",\n lambda mo: mo.group(0)[0].upper() +\n mo.group(0)[1:].lower(),\n s)\n\n >>> titlecase("they\'re bill\'s friends.")\n "They\'re Bill\'s Friends."\n\nstr.translate(map)\n\n Return a copy of the *s* where all characters have been mapped\n through the *map* which must be a dictionary of Unicode ordinals\n (integers) to Unicode ordinals, strings or ``None``. Unmapped\n characters are left untouched. Characters mapped to ``None`` are\n deleted.\n\n You can use ``str.maketrans()`` to create a translation map from\n character-to-character mappings in different formats.\n\n You can use the ``maketrans()`` helper function in the ``string``\n module to create a translation table. For string objects, set the\n *table* argument to ``None`` for translations that only delete\n characters:\n\n Note: An even more flexible approach is to create a custom character\n mapping codec using the ``codecs`` module (see\n ``encodings.cp1251`` for an example).\n\nstr.upper()\n\n Return a copy of the string converted to uppercase.\n\nstr.zfill(width)\n\n Return the numeric string left filled with zeros in a string of\n length *width*. A sign prefix is handled correctly. The original\n string is returned if *width* is less than ``len(s)``.\n\n\nOld String Formatting Operations\n================================\n\nNote: The formatting operations described here are obsolete and may go\n away in future versions of Python. Use the new *String Formatting*\n in new code.\n\nString objects have one unique built-in operation: the ``%`` operator\n(modulo). This is also known as the string *formatting* or\n*interpolation* operator. Given ``format % values`` (where *format* is\na string), ``%`` conversion specifications in *format* are replaced\nwith zero or more elements of *values*. The effect is similar to the\nusing ``sprintf()`` in the C language.\n\nIf *format* requires a single argument, *values* may be a single non-\ntuple object. [4] Otherwise, *values* must be a tuple with exactly\nthe number of items specified by the format string, or a single\nmapping object (for example, a dictionary).\n\nA conversion specifier contains two or more characters and has the\nfollowing components, which must occur in this order:\n\n1. The ``\'%\'`` character, which marks the start of the specifier.\n\n2. Mapping key (optional), consisting of a parenthesised sequence of\n characters (for example, ``(somename)``).\n\n3. Conversion flags (optional), which affect the result of some\n conversion types.\n\n4. Minimum field width (optional). If specified as an ``\'*\'``\n (asterisk), the actual width is read from the next element of the\n tuple in *values*, and the object to convert comes after the\n minimum field width and optional precision.\n\n5. Precision (optional), given as a ``\'.\'`` (dot) followed by the\n precision. If specified as ``\'*\'`` (an asterisk), the actual width\n is read from the next element of the tuple in *values*, and the\n value to convert comes after the precision.\n\n6. Length modifier (optional).\n\n7. Conversion type.\n\nWhen the right argument is a dictionary (or other mapping type), then\nthe formats in the string *must* include a parenthesised mapping key\ninto that dictionary inserted immediately after the ``\'%\'`` character.\nThe mapping key selects the value to be formatted from the mapping.\nFor example:\n\n>>> print(\'%(language)s has %(#)03d quote types.\' % \\\n... {\'language\': "Python", "#": 2})\nPython has 002 quote types.\n\nIn this case no ``*`` specifiers may occur in a format (since they\nrequire a sequential parameter list).\n\nThe conversion flag characters are:\n\n+-----------+-----------------------------------------------------------------------+\n| Flag | Meaning |\n+===========+=======================================================================+\n| ``\'#\'`` | The value conversion will use the "alternate form" (where defined |\n| | below). |\n+-----------+-----------------------------------------------------------------------+\n| ``\'0\'`` | The conversion will be zero padded for numeric values. |\n+-----------+-----------------------------------------------------------------------+\n| ``\'-\'`` | The converted value is left adjusted (overrides the ``\'0\'`` |\n| | conversion if both are given). |\n+-----------+-----------------------------------------------------------------------+\n| ``\' \'`` | (a space) A blank should be left before a positive number (or empty |\n| | string) produced by a signed conversion. |\n+-----------+-----------------------------------------------------------------------+\n| ``\'+\'`` | A sign character (``\'+\'`` or ``\'-\'``) will precede the conversion |\n| | (overrides a "space" flag). |\n+-----------+-----------------------------------------------------------------------+\n\nA length modifier (``h``, ``l``, or ``L``) may be present, but is\nignored as it is not necessary for Python -- so e.g. ``%ld`` is\nidentical to ``%d``.\n\nThe conversion types are:\n\n+--------------+-------------------------------------------------------+---------+\n| Conversion | Meaning | Notes |\n+==============+=======================================================+=========+\n| ``\'d\'`` | Signed integer decimal. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'i\'`` | Signed integer decimal. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'o\'`` | Signed octal value. | (1) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'u\'`` | Obsolete type -- it is identical to ``\'d\'``. | (7) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'x\'`` | Signed hexadecimal (lowercase). | (2) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'X\'`` | Signed hexadecimal (uppercase). | (2) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'e\'`` | Floating point exponential format (lowercase). | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'E\'`` | Floating point exponential format (uppercase). | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'f\'`` | Floating point decimal format. | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'F\'`` | Floating point decimal format. | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'g\'`` | Floating point format. Uses lowercase exponential | (4) |\n| | format if exponent is less than -4 or not less than | |\n| | precision, decimal format otherwise. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'G\'`` | Floating point format. Uses uppercase exponential | (4) |\n| | format if exponent is less than -4 or not less than | |\n| | precision, decimal format otherwise. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'c\'`` | Single character (accepts integer or single character | |\n| | string). | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'r\'`` | String (converts any Python object using ``repr()``). | (5) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'s\'`` | String (converts any Python object using ``str()``). | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'%\'`` | No argument is converted, results in a ``\'%\'`` | |\n| | character in the result. | |\n+--------------+-------------------------------------------------------+---------+\n\nNotes:\n\n1. The alternate form causes a leading zero (``\'0\'``) to be inserted\n between left-hand padding and the formatting of the number if the\n leading character of the result is not already a zero.\n\n2. The alternate form causes a leading ``\'0x\'`` or ``\'0X\'`` (depending\n on whether the ``\'x\'`` or ``\'X\'`` format was used) to be inserted\n between left-hand padding and the formatting of the number if the\n leading character of the result is not already a zero.\n\n3. The alternate form causes the result to always contain a decimal\n point, even if no digits follow it.\n\n The precision determines the number of digits after the decimal\n point and defaults to 6.\n\n4. The alternate form causes the result to always contain a decimal\n point, and trailing zeroes are not removed as they would otherwise\n be.\n\n The precision determines the number of significant digits before\n and after the decimal point and defaults to 6.\n\n5. The precision determines the maximal number of characters used.\n\n1. See **PEP 237**.\n\nSince Python strings have an explicit length, ``%s`` conversions do\nnot assume that ``\'\\0\'`` is the end of the string.\n\nChanged in version 3.1: ``%f`` conversions for numbers whose absolute\nvalue is over 1e50 are no longer replaced by ``%g`` conversions.\n\nAdditional string operations are defined in standard modules\n``string`` and ``re``.\n\n\nRange Type\n==========\n\nThe ``range`` type is an immutable sequence which is commonly used for\nlooping. The advantage of the ``range`` type is that an ``range``\nobject will always take the same amount of memory, no matter the size\nof the range it represents. There are no consistent performance\nadvantages.\n\nRange objects have very little behavior: they only support indexing,\niteration, and the ``len()`` function.\n\n\nMutable Sequence Types\n======================\n\nList and bytearray objects support additional operations that allow\nin-place modification of the object. Other mutable sequence types\n(when added to the language) should also support these operations.\nStrings and tuples are immutable sequence types: such objects cannot\nbe modified once created. The following operations are defined on\nmutable sequence types (where *x* is an arbitrary object).\n\nNote that while lists allow their items to be of any type, bytearray\nobject "items" are all integers in the range 0 <= x < 256.\n\n+--------------------------------+----------------------------------+-----------------------+\n| Operation | Result | Notes |\n+================================+==================================+=======================+\n| ``s[i] = x`` | item *i* of *s* is replaced by | |\n| | *x* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j] = t`` | slice of *s* from *i* to *j* is | |\n| | replaced by the contents of the | |\n| | iterable *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j]`` | same as ``s[i:j] = []`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j:k] = t`` | the elements of ``s[i:j:k]`` are | (1) |\n| | replaced by those of *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j:k]`` | removes the elements of | |\n| | ``s[i:j:k]`` from the list | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.append(x)`` | same as ``s[len(s):len(s)] = | |\n| | [x]`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.extend(x)`` | same as ``s[len(s):len(s)] = x`` | (2) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.count(x)`` | return number of *i*\'s for which | |\n| | ``s[i] == x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.index(x[, i[, j]])`` | return smallest *k* such that | (3) |\n| | ``s[k] == x`` and ``i <= k < j`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.insert(i, x)`` | same as ``s[i:i] = [x]`` | (4) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.pop([i])`` | same as ``x = s[i]; del s[i]; | (5) |\n| | return x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.remove(x)`` | same as ``del s[s.index(x)]`` | (3) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.reverse()`` | reverses the items of *s* in | (6) |\n| | place | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.sort([key[, reverse]])`` | sort the items of *s* in place | (6), (7), (8) |\n+--------------------------------+----------------------------------+-----------------------+\n\nNotes:\n\n1. *t* must have the same length as the slice it is replacing.\n\n2. *x* can be any iterable object.\n\n3. Raises ``ValueError`` when *x* is not found in *s*. When a negative\n index is passed as the second or third parameter to the ``index()``\n method, the sequence length is added, as for slice indices. If it\n is still negative, it is truncated to zero, as for slice indices.\n\n4. When a negative index is passed as the first parameter to the\n ``insert()`` method, the sequence length is added, as for slice\n indices. If it is still negative, it is truncated to zero, as for\n slice indices.\n\n5. The optional argument *i* defaults to ``-1``, so that by default\n the last item is removed and returned.\n\n6. The ``sort()`` and ``reverse()`` methods modify the sequence in\n place for economy of space when sorting or reversing a large\n sequence. To remind you that they operate by side effect, they\n don\'t return the sorted or reversed sequence.\n\n7. The ``sort()`` method takes optional arguments for controlling the\n comparisons. Each must be specified as a keyword argument.\n\n *key* specifies a function of one argument that is used to extract\n a comparison key from each list element: ``key=str.lower``. The\n default value is ``None``. Use ``functools.cmp_to_key()`` to\n convert an old-style *cmp* function to a *key* function.\n\n *reverse* is a boolean value. If set to ``True``, then the list\n elements are sorted as if each comparison were reversed.\n\n The ``sort()`` method is guaranteed to be stable. A sort is stable\n if it guarantees not to change the relative order of elements that\n compare equal --- this is helpful for sorting in multiple passes\n (for example, sort by department, then by salary grade).\n\n **CPython implementation detail:** While a list is being sorted,\n the effect of attempting to mutate, or even inspect, the list is\n undefined. The C implementation of Python makes the list appear\n empty for the duration, and raises ``ValueError`` if it can detect\n that the list has been mutated during a sort.\n\n8. ``sort()`` is not supported by ``bytearray`` objects.\n\n\nBytes and Byte Array Methods\n============================\n\nBytes and bytearray objects, being "strings of bytes", have all\nmethods found on strings, with the exception of ``encode()``,\n``format()`` and ``isidentifier()``, which do not make sense with\nthese types. For converting the objects to strings, they have a\n``decode()`` method.\n\nWherever one of these methods needs to interpret the bytes as\ncharacters (e.g. the ``is...()`` methods), the ASCII character set is\nassumed.\n\nNote: The methods on bytes and bytearray objects don\'t accept strings as\n their arguments, just as the methods on strings don\'t accept bytes\n as their arguments. For example, you have to write\n\n a = "abc"\n b = a.replace("a", "f")\n\n and\n\n a = b"abc"\n b = a.replace(b"a", b"f")\n\nbytes.decode(encoding=sys.getdefaultencoding(), errors="strict")\nbytearray.decode(encoding=sys.getdefaultencoding(), errors="strict")\n\n Return a string decoded from the given bytes. Default encoding is\n the current default string encoding. *errors* may be given to set\n a different error handling scheme. The default for *errors* is\n ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'`` and any other name registered via\n ``codecs.register_error()``, see section *Codec Base Classes*. For\n a list of possible encodings, see section *Standard Encodings*.\n\n Changed in version 3.1: Added support for keyword arguments.\n\nThe bytes and bytearray types have an additional class method:\n\nclassmethod bytes.fromhex(string)\nclassmethod bytearray.fromhex(string)\n\n This ``bytes`` class method returns a bytes or bytearray object,\n decoding the given string object. The string must contain two\n hexadecimal digits per byte, spaces are ignored.\n\n >>> bytes.fromhex(\'f0 f1f2 \')\n b\'\\xf0\\xf1\\xf2\'\n\nThe maketrans and translate methods differ in semantics from the\nversions available on strings:\n\nbytes.translate(table[, delete])\nbytearray.translate(table[, delete])\n\n Return a copy of the bytes or bytearray object where all bytes\n occurring in the optional argument *delete* are removed, and the\n remaining bytes have been mapped through the given translation\n table, which must be a bytes object of length 256.\n\n You can use the ``bytes.maketrans()`` method to create a\n translation table.\n\n Set the *table* argument to ``None`` for translations that only\n delete characters:\n\n >>> b\'read this short text\'.translate(None, b\'aeiou\')\n b\'rd ths shrt txt\'\n\nstatic bytes.maketrans(from, to)\nstatic bytearray.maketrans(from, to)\n\n This static method returns a translation table usable for\n ``bytes.translate()`` that will map each character in *from* into\n the character at the same position in *to*; *from* and *to* must be\n bytes objects and have the same length.\n\n New in version 3.1.\n', + 'typesseq': '\nSequence Types --- ``str``, ``bytes``, ``bytearray``, ``list``, ``tuple``, ``range``\n************************************************************************************\n\nThere are six sequence types: strings, byte sequences (``bytes``\nobjects), byte arrays (``bytearray`` objects), lists, tuples, and\nrange objects. For other containers see the built in ``dict`` and\n``set`` classes, and the ``collections`` module.\n\nStrings contain Unicode characters. Their literals are written in\nsingle or double quotes: ``\'xyzzy\'``, ``"frobozz"``. See *String and\nBytes literals* for more about string literals. In addition to the\nfunctionality described here, there are also string-specific methods\ndescribed in the *String Methods* section.\n\nBytes and bytearray objects contain single bytes -- the former is\nimmutable while the latter is a mutable sequence. Bytes objects can\nbe constructed the constructor, ``bytes()``, and from literals; use a\n``b`` prefix with normal string syntax: ``b\'xyzzy\'``. To construct\nbyte arrays, use the ``bytearray()`` function.\n\nWarning: While string objects are sequences of characters (represented by\n strings of length 1), bytes and bytearray objects are sequences of\n *integers* (between 0 and 255), representing the ASCII value of\n single bytes. That means that for a bytes or bytearray object *b*,\n ``b[0]`` will be an integer, while ``b[0:1]`` will be a bytes or\n bytearray object of length 1. The representation of bytes objects\n uses the literal format (``b\'...\'``) since it is generally more\n useful than e.g. ``bytes([50, 19, 100])``. You can always convert a\n bytes object into a list of integers using ``list(b)``.Also, while\n in previous Python versions, byte strings and Unicode strings could\n be exchanged for each other rather freely (barring encoding issues),\n strings and bytes are now completely separate concepts. There\'s no\n implicit en-/decoding if you pass an object of the wrong type. A\n string always compares unequal to a bytes or bytearray object.\n\nLists are constructed with square brackets, separating items with\ncommas: ``[a, b, c]``. Tuples are constructed by the comma operator\n(not within square brackets), with or without enclosing parentheses,\nbut an empty tuple must have the enclosing parentheses, such as ``a,\nb, c`` or ``()``. A single item tuple must have a trailing comma,\nsuch as ``(d,)``.\n\nObjects of type range are created using the ``range()`` function.\nThey don\'t support slicing, concatenation or repetition, and using\n``in``, ``not in``, ``min()`` or ``max()`` on them is inefficient.\n\nMost sequence types support the following operations. The ``in`` and\n``not in`` operations have the same priorities as the comparison\noperations. The ``+`` and ``*`` operations have the same priority as\nthe corresponding numeric operations. [3] Additional methods are\nprovided for *Mutable Sequence Types*.\n\nThis table lists the sequence operations sorted in ascending priority\n(operations in the same box have the same priority). In the table,\n*s* and *t* are sequences of the same type; *n*, *i* and *j* are\nintegers:\n\n+--------------------+----------------------------------+------------+\n| Operation | Result | Notes |\n+====================+==================================+============+\n| ``x in s`` | ``True`` if an item of *s* is | (1) |\n| | equal to *x*, else ``False`` | |\n+--------------------+----------------------------------+------------+\n| ``x not in s`` | ``False`` if an item of *s* is | (1) |\n| | equal to *x*, else ``True`` | |\n+--------------------+----------------------------------+------------+\n| ``s + t`` | the concatenation of *s* and *t* | (6) |\n+--------------------+----------------------------------+------------+\n| ``s * n, n * s`` | *n* shallow copies of *s* | (2) |\n| | concatenated | |\n+--------------------+----------------------------------+------------+\n| ``s[i]`` | *i*\'th item of *s*, origin 0 | (3) |\n+--------------------+----------------------------------+------------+\n| ``s[i:j]`` | slice of *s* from *i* to *j* | (3)(4) |\n+--------------------+----------------------------------+------------+\n| ``s[i:j:k]`` | slice of *s* from *i* to *j* | (3)(5) |\n| | with step *k* | |\n+--------------------+----------------------------------+------------+\n| ``len(s)`` | length of *s* | |\n+--------------------+----------------------------------+------------+\n| ``min(s)`` | smallest item of *s* | |\n+--------------------+----------------------------------+------------+\n| ``max(s)`` | largest item of *s* | |\n+--------------------+----------------------------------+------------+\n\nSequence types also support comparisons. In particular, tuples and\nlists are compared lexicographically by comparing corresponding\nelements. This means that to compare equal, every element must\ncompare equal and the two sequences must be of the same type and have\nthe same length. (For full details see *Comparisons* in the language\nreference.)\n\nNotes:\n\n1. When *s* is a string object, the ``in`` and ``not in`` operations\n act like a substring test.\n\n2. Values of *n* less than ``0`` are treated as ``0`` (which yields an\n empty sequence of the same type as *s*). Note also that the copies\n are shallow; nested structures are not copied. This often haunts\n new Python programmers; consider:\n\n >>> lists = [[]] * 3\n >>> lists\n [[], [], []]\n >>> lists[0].append(3)\n >>> lists\n [[3], [3], [3]]\n\n What has happened is that ``[[]]`` is a one-element list containing\n an empty list, so all three elements of ``[[]] * 3`` are (pointers\n to) this single empty list. Modifying any of the elements of\n ``lists`` modifies this single list. You can create a list of\n different lists this way:\n\n >>> lists = [[] for i in range(3)]\n >>> lists[0].append(3)\n >>> lists[1].append(5)\n >>> lists[2].append(7)\n >>> lists\n [[3], [5], [7]]\n\n3. If *i* or *j* is negative, the index is relative to the end of the\n string: ``len(s) + i`` or ``len(s) + j`` is substituted. But note\n that ``-0`` is still ``0``.\n\n4. The slice of *s* from *i* to *j* is defined as the sequence of\n items with index *k* such that ``i <= k < j``. If *i* or *j* is\n greater than ``len(s)``, use ``len(s)``. If *i* is omitted or\n ``None``, use ``0``. If *j* is omitted or ``None``, use\n ``len(s)``. If *i* is greater than or equal to *j*, the slice is\n empty.\n\n5. The slice of *s* from *i* to *j* with step *k* is defined as the\n sequence of items with index ``x = i + n*k`` such that ``0 <= n <\n (j-i)/k``. In other words, the indices are ``i``, ``i+k``,\n ``i+2*k``, ``i+3*k`` and so on, stopping when *j* is reached (but\n never including *j*). If *i* or *j* is greater than ``len(s)``,\n use ``len(s)``. If *i* or *j* are omitted or ``None``, they become\n "end" values (which end depends on the sign of *k*). Note, *k*\n cannot be zero. If *k* is ``None``, it is treated like ``1``.\n\n6. **CPython implementation detail:** If *s* and *t* are both strings,\n some Python implementations such as CPython can usually perform an\n in-place optimization for assignments of the form ``s = s + t`` or\n ``s += t``. When applicable, this optimization makes quadratic\n run-time much less likely. This optimization is both version and\n implementation dependent. For performance sensitive code, it is\n preferable to use the ``str.join()`` method which assures\n consistent linear concatenation performance across versions and\n implementations.\n\n\nString Methods\n==============\n\nString objects support the methods listed below.\n\nIn addition, Python\'s strings support the sequence type methods\ndescribed in the *Sequence Types --- str, bytes, bytearray, list,\ntuple, range* section. To output formatted strings, see the *String\nFormatting* section. Also, see the ``re`` module for string functions\nbased on regular expressions.\n\nstr.capitalize()\n\n Return a copy of the string with its first character capitalized\n and the rest lowercased.\n\nstr.center(width[, fillchar])\n\n Return centered in a string of length *width*. Padding is done\n using the specified *fillchar* (default is a space).\n\nstr.count(sub[, start[, end]])\n\n Return the number of non-overlapping occurrences of substring *sub*\n in the range [*start*, *end*]. Optional arguments *start* and\n *end* are interpreted as in slice notation.\n\nstr.encode(encoding=sys.getdefaultencoding(), errors="strict")\n\n Return an encoded version of the string as a bytes object. Default\n encoding is the current default string encoding. *errors* may be\n given to set a different error handling scheme. The default for\n *errors* is ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'``, ``\'xmlcharrefreplace\'``, ``\'backslashreplace\'`` and\n any other name registered via ``codecs.register_error()``, see\n section *Codec Base Classes*. For a list of possible encodings, see\n section *Standard Encodings*.\n\n Changed in version 3.1: Support for keyword arguments added.\n\nstr.endswith(suffix[, start[, end]])\n\n Return ``True`` if the string ends with the specified *suffix*,\n otherwise return ``False``. *suffix* can also be a tuple of\n suffixes to look for. With optional *start*, test beginning at\n that position. With optional *end*, stop comparing at that\n position.\n\nstr.expandtabs([tabsize])\n\n Return a copy of the string where all tab characters are replaced\n by one or more spaces, depending on the current column and the\n given tab size. The column number is reset to zero after each\n newline occurring in the string. If *tabsize* is not given, a tab\n size of ``8`` characters is assumed. This doesn\'t understand other\n non-printing characters or escape sequences.\n\nstr.find(sub[, start[, end]])\n\n Return the lowest index in the string where substring *sub* is\n found, such that *sub* is contained in the slice ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` if *sub* is not found.\n\nstr.format(*args, **kwargs)\n\n Perform a string formatting operation. The string on which this\n method is called can contain literal text or replacement fields\n delimited by braces ``{}``. Each replacement field contains either\n the numeric index of a positional argument, or the name of a\n keyword argument. Returns a copy of the string where each\n replacement field is replaced with the string value of the\n corresponding argument.\n\n >>> "The sum of 1 + 2 is {0}".format(1+2)\n \'The sum of 1 + 2 is 3\'\n\n See *Format String Syntax* for a description of the various\n formatting options that can be specified in format strings.\n\nstr.index(sub[, start[, end]])\n\n Like ``find()``, but raise ``ValueError`` when the substring is not\n found.\n\nstr.isalnum()\n\n Return true if all characters in the string are alphanumeric and\n there is at least one character, false otherwise.\n\nstr.isalpha()\n\n Return true if all characters in the string are alphabetic and\n there is at least one character, false otherwise.\n\nstr.isdecimal()\n\n Return true if all characters in the string are decimal characters\n and there is at least one character, false otherwise. Decimal\n characters include digit characters, and all characters that that\n can be used to form decimal-radix numbers, e.g. U+0660, ARABIC-\n INDIC DIGIT ZERO.\n\nstr.isdigit()\n\n Return true if all characters in the string are digits and there is\n at least one character, false otherwise.\n\nstr.isidentifier()\n\n Return true if the string is a valid identifier according to the\n language definition, section *Identifiers and keywords*.\n\nstr.islower()\n\n Return true if all cased characters in the string are lowercase and\n there is at least one cased character, false otherwise.\n\nstr.isnumeric()\n\n Return true if all characters in the string are numeric characters,\n and there is at least one character, false otherwise. Numeric\n characters include digit characters, and all characters that have\n the Unicode numeric value property, e.g. U+2155, VULGAR FRACTION\n ONE FIFTH.\n\nstr.isprintable()\n\n Return true if all characters in the string are printable or the\n string is empty, false otherwise. Nonprintable characters are\n those characters defined in the Unicode character database as\n "Other" or "Separator", excepting the ASCII space (0x20) which is\n considered printable. (Note that printable characters in this\n context are those which should not be escaped when ``repr()`` is\n invoked on a string. It has no bearing on the handling of strings\n written to ``sys.stdout`` or ``sys.stderr``.)\n\nstr.isspace()\n\n Return true if there are only whitespace characters in the string\n and there is at least one character, false otherwise.\n\nstr.istitle()\n\n Return true if the string is a titlecased string and there is at\n least one character, for example uppercase characters may only\n follow uncased characters and lowercase characters only cased ones.\n Return false otherwise.\n\nstr.isupper()\n\n Return true if all cased characters in the string are uppercase and\n there is at least one cased character, false otherwise.\n\nstr.join(iterable)\n\n Return a string which is the concatenation of the strings in the\n *iterable* *iterable*. A ``TypeError`` will be raised if there are\n any non-string values in *seq*, including ``bytes`` objects. The\n separator between elements is the string providing this method.\n\nstr.ljust(width[, fillchar])\n\n Return the string left justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\nstr.lower()\n\n Return a copy of the string converted to lowercase.\n\nstr.lstrip([chars])\n\n Return a copy of the string with leading characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a prefix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.lstrip()\n \'spacious \'\n >>> \'www.example.com\'.lstrip(\'cmowz.\')\n \'example.com\'\n\nstatic str.maketrans(x[, y[, z]])\n\n This static method returns a translation table usable for\n ``str.translate()``.\n\n If there is only one argument, it must be a dictionary mapping\n Unicode ordinals (integers) or characters (strings of length 1) to\n Unicode ordinals, strings (of arbitrary lengths) or None.\n Character keys will then be converted to ordinals.\n\n If there are two arguments, they must be strings of equal length,\n and in the resulting dictionary, each character in x will be mapped\n to the character at the same position in y. If there is a third\n argument, it must be a string, whose characters will be mapped to\n None in the result.\n\nstr.partition(sep)\n\n Split the string at the first occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing the string itself, followed by\n two empty strings.\n\nstr.replace(old, new[, count])\n\n Return a copy of the string with all occurrences of substring *old*\n replaced by *new*. If the optional argument *count* is given, only\n the first *count* occurrences are replaced.\n\nstr.rfind(sub[, start[, end]])\n\n Return the highest index in the string where substring *sub* is\n found, such that *sub* is contained within ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` on failure.\n\nstr.rindex(sub[, start[, end]])\n\n Like ``rfind()`` but raises ``ValueError`` when the substring *sub*\n is not found.\n\nstr.rjust(width[, fillchar])\n\n Return the string right justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\nstr.rpartition(sep)\n\n Split the string at the last occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing two empty strings, followed by\n the string itself.\n\nstr.rsplit([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit* splits\n are done, the *rightmost* ones. If *sep* is not specified or\n ``None``, any whitespace string is a separator. Except for\n splitting from the right, ``rsplit()`` behaves like ``split()``\n which is described in detail below.\n\nstr.rstrip([chars])\n\n Return a copy of the string with trailing characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a suffix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.rstrip()\n \' spacious\'\n >>> \'mississippi\'.rstrip(\'ipz\')\n \'mississ\'\n\nstr.split([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit*\n splits are done (thus, the list will have at most ``maxsplit+1``\n elements). If *maxsplit* is not specified, then there is no limit\n on the number of splits (all possible splits are made).\n\n If *sep* is given, consecutive delimiters are not grouped together\n and are deemed to delimit empty strings (for example,\n ``\'1,,2\'.split(\',\')`` returns ``[\'1\', \'\', \'2\']``). The *sep*\n argument may consist of multiple characters (for example,\n ``\'1<>2<>3\'.split(\'<>\')`` returns ``[\'1\', \'2\', \'3\']``). Splitting\n an empty string with a specified separator returns ``[\'\']``.\n\n If *sep* is not specified or is ``None``, a different splitting\n algorithm is applied: runs of consecutive whitespace are regarded\n as a single separator, and the result will contain no empty strings\n at the start or end if the string has leading or trailing\n whitespace. Consequently, splitting an empty string or a string\n consisting of just whitespace with a ``None`` separator returns\n ``[]``.\n\n For example, ``\' 1 2 3 \'.split()`` returns ``[\'1\', \'2\', \'3\']``,\n and ``\' 1 2 3 \'.split(None, 1)`` returns ``[\'1\', \'2 3 \']``.\n\nstr.splitlines([keepends])\n\n Return a list of the lines in the string, breaking at line\n boundaries. Line breaks are not included in the resulting list\n unless *keepends* is given and true.\n\nstr.startswith(prefix[, start[, end]])\n\n Return ``True`` if string starts with the *prefix*, otherwise\n return ``False``. *prefix* can also be a tuple of prefixes to look\n for. With optional *start*, test string beginning at that\n position. With optional *end*, stop comparing string at that\n position.\n\nstr.strip([chars])\n\n Return a copy of the string with the leading and trailing\n characters removed. The *chars* argument is a string specifying the\n set of characters to be removed. If omitted or ``None``, the\n *chars* argument defaults to removing whitespace. The *chars*\n argument is not a prefix or suffix; rather, all combinations of its\n values are stripped:\n\n >>> \' spacious \'.strip()\n \'spacious\'\n >>> \'www.example.com\'.strip(\'cmowz.\')\n \'example\'\n\nstr.swapcase()\n\n Return a copy of the string with uppercase characters converted to\n lowercase and vice versa.\n\nstr.title()\n\n Return a titlecased version of the string where words start with an\n uppercase character and the remaining characters are lowercase.\n\n The algorithm uses a simple language-independent definition of a\n word as groups of consecutive letters. The definition works in\n many contexts but it means that apostrophes in contractions and\n possessives form word boundaries, which may not be the desired\n result:\n\n >>> "they\'re bill\'s friends from the UK".title()\n "They\'Re Bill\'S Friends From The Uk"\n\n A workaround for apostrophes can be constructed using regular\n expressions:\n\n >>> import re\n >>> def titlecase(s):\n return re.sub(r"[A-Za-z]+(\'[A-Za-z]+)?",\n lambda mo: mo.group(0)[0].upper() +\n mo.group(0)[1:].lower(),\n s)\n\n >>> titlecase("they\'re bill\'s friends.")\n "They\'re Bill\'s Friends."\n\nstr.translate(map)\n\n Return a copy of the *s* where all characters have been mapped\n through the *map* which must be a dictionary of Unicode ordinals\n (integers) to Unicode ordinals, strings or ``None``. Unmapped\n characters are left untouched. Characters mapped to ``None`` are\n deleted.\n\n You can use ``str.maketrans()`` to create a translation map from\n character-to-character mappings in different formats.\n\n Note: An even more flexible approach is to create a custom character\n mapping codec using the ``codecs`` module (see\n ``encodings.cp1251`` for an example).\n\nstr.upper()\n\n Return a copy of the string converted to uppercase.\n\nstr.zfill(width)\n\n Return the numeric string left filled with zeros in a string of\n length *width*. A sign prefix is handled correctly. The original\n string is returned if *width* is less than ``len(s)``.\n\n\nOld String Formatting Operations\n================================\n\nNote: The formatting operations described here are obsolete and may go\n away in future versions of Python. Use the new *String Formatting*\n in new code.\n\nString objects have one unique built-in operation: the ``%`` operator\n(modulo). This is also known as the string *formatting* or\n*interpolation* operator. Given ``format % values`` (where *format* is\na string), ``%`` conversion specifications in *format* are replaced\nwith zero or more elements of *values*. The effect is similar to the\nusing ``sprintf()`` in the C language.\n\nIf *format* requires a single argument, *values* may be a single non-\ntuple object. [4] Otherwise, *values* must be a tuple with exactly\nthe number of items specified by the format string, or a single\nmapping object (for example, a dictionary).\n\nA conversion specifier contains two or more characters and has the\nfollowing components, which must occur in this order:\n\n1. The ``\'%\'`` character, which marks the start of the specifier.\n\n2. Mapping key (optional), consisting of a parenthesised sequence of\n characters (for example, ``(somename)``).\n\n3. Conversion flags (optional), which affect the result of some\n conversion types.\n\n4. Minimum field width (optional). If specified as an ``\'*\'``\n (asterisk), the actual width is read from the next element of the\n tuple in *values*, and the object to convert comes after the\n minimum field width and optional precision.\n\n5. Precision (optional), given as a ``\'.\'`` (dot) followed by the\n precision. If specified as ``\'*\'`` (an asterisk), the actual width\n is read from the next element of the tuple in *values*, and the\n value to convert comes after the precision.\n\n6. Length modifier (optional).\n\n7. Conversion type.\n\nWhen the right argument is a dictionary (or other mapping type), then\nthe formats in the string *must* include a parenthesised mapping key\ninto that dictionary inserted immediately after the ``\'%\'`` character.\nThe mapping key selects the value to be formatted from the mapping.\nFor example:\n\n>>> print(\'%(language)s has %(#)03d quote types.\' % \\\n... {\'language\': "Python", "#": 2})\nPython has 002 quote types.\n\nIn this case no ``*`` specifiers may occur in a format (since they\nrequire a sequential parameter list).\n\nThe conversion flag characters are:\n\n+-----------+-----------------------------------------------------------------------+\n| Flag | Meaning |\n+===========+=======================================================================+\n| ``\'#\'`` | The value conversion will use the "alternate form" (where defined |\n| | below). |\n+-----------+-----------------------------------------------------------------------+\n| ``\'0\'`` | The conversion will be zero padded for numeric values. |\n+-----------+-----------------------------------------------------------------------+\n| ``\'-\'`` | The converted value is left adjusted (overrides the ``\'0\'`` |\n| | conversion if both are given). |\n+-----------+-----------------------------------------------------------------------+\n| ``\' \'`` | (a space) A blank should be left before a positive number (or empty |\n| | string) produced by a signed conversion. |\n+-----------+-----------------------------------------------------------------------+\n| ``\'+\'`` | A sign character (``\'+\'`` or ``\'-\'``) will precede the conversion |\n| | (overrides a "space" flag). |\n+-----------+-----------------------------------------------------------------------+\n\nA length modifier (``h``, ``l``, or ``L``) may be present, but is\nignored as it is not necessary for Python -- so e.g. ``%ld`` is\nidentical to ``%d``.\n\nThe conversion types are:\n\n+--------------+-------------------------------------------------------+---------+\n| Conversion | Meaning | Notes |\n+==============+=======================================================+=========+\n| ``\'d\'`` | Signed integer decimal. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'i\'`` | Signed integer decimal. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'o\'`` | Signed octal value. | (1) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'u\'`` | Obsolete type -- it is identical to ``\'d\'``. | (7) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'x\'`` | Signed hexadecimal (lowercase). | (2) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'X\'`` | Signed hexadecimal (uppercase). | (2) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'e\'`` | Floating point exponential format (lowercase). | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'E\'`` | Floating point exponential format (uppercase). | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'f\'`` | Floating point decimal format. | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'F\'`` | Floating point decimal format. | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'g\'`` | Floating point format. Uses lowercase exponential | (4) |\n| | format if exponent is less than -4 or not less than | |\n| | precision, decimal format otherwise. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'G\'`` | Floating point format. Uses uppercase exponential | (4) |\n| | format if exponent is less than -4 or not less than | |\n| | precision, decimal format otherwise. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'c\'`` | Single character (accepts integer or single character | |\n| | string). | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'r\'`` | String (converts any Python object using ``repr()``). | (5) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'s\'`` | String (converts any Python object using ``str()``). | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'%\'`` | No argument is converted, results in a ``\'%\'`` | |\n| | character in the result. | |\n+--------------+-------------------------------------------------------+---------+\n\nNotes:\n\n1. The alternate form causes a leading zero (``\'0\'``) to be inserted\n between left-hand padding and the formatting of the number if the\n leading character of the result is not already a zero.\n\n2. The alternate form causes a leading ``\'0x\'`` or ``\'0X\'`` (depending\n on whether the ``\'x\'`` or ``\'X\'`` format was used) to be inserted\n between left-hand padding and the formatting of the number if the\n leading character of the result is not already a zero.\n\n3. The alternate form causes the result to always contain a decimal\n point, even if no digits follow it.\n\n The precision determines the number of digits after the decimal\n point and defaults to 6.\n\n4. The alternate form causes the result to always contain a decimal\n point, and trailing zeroes are not removed as they would otherwise\n be.\n\n The precision determines the number of significant digits before\n and after the decimal point and defaults to 6.\n\n5. The precision determines the maximal number of characters used.\n\n1. See **PEP 237**.\n\nSince Python strings have an explicit length, ``%s`` conversions do\nnot assume that ``\'\\0\'`` is the end of the string.\n\nChanged in version 3.1: ``%f`` conversions for numbers whose absolute\nvalue is over 1e50 are no longer replaced by ``%g`` conversions.\n\nAdditional string operations are defined in standard modules\n``string`` and ``re``.\n\n\nRange Type\n==========\n\nThe ``range`` type is an immutable sequence which is commonly used for\nlooping. The advantage of the ``range`` type is that an ``range``\nobject will always take the same amount of memory, no matter the size\nof the range it represents. There are no consistent performance\nadvantages.\n\nRange objects have very little behavior: they only support indexing,\niteration, and the ``len()`` function.\n\n\nMutable Sequence Types\n======================\n\nList and bytearray objects support additional operations that allow\nin-place modification of the object. Other mutable sequence types\n(when added to the language) should also support these operations.\nStrings and tuples are immutable sequence types: such objects cannot\nbe modified once created. The following operations are defined on\nmutable sequence types (where *x* is an arbitrary object).\n\nNote that while lists allow their items to be of any type, bytearray\nobject "items" are all integers in the range 0 <= x < 256.\n\n+--------------------------------+----------------------------------+-----------------------+\n| Operation | Result | Notes |\n+================================+==================================+=======================+\n| ``s[i] = x`` | item *i* of *s* is replaced by | |\n| | *x* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j] = t`` | slice of *s* from *i* to *j* is | |\n| | replaced by the contents of the | |\n| | iterable *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j]`` | same as ``s[i:j] = []`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j:k] = t`` | the elements of ``s[i:j:k]`` are | (1) |\n| | replaced by those of *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j:k]`` | removes the elements of | |\n| | ``s[i:j:k]`` from the list | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.append(x)`` | same as ``s[len(s):len(s)] = | |\n| | [x]`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.extend(x)`` | same as ``s[len(s):len(s)] = x`` | (2) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.count(x)`` | return number of *i*\'s for which | |\n| | ``s[i] == x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.index(x[, i[, j]])`` | return smallest *k* such that | (3) |\n| | ``s[k] == x`` and ``i <= k < j`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.insert(i, x)`` | same as ``s[i:i] = [x]`` | (4) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.pop([i])`` | same as ``x = s[i]; del s[i]; | (5) |\n| | return x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.remove(x)`` | same as ``del s[s.index(x)]`` | (3) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.reverse()`` | reverses the items of *s* in | (6) |\n| | place | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.sort([key[, reverse]])`` | sort the items of *s* in place | (6), (7), (8) |\n+--------------------------------+----------------------------------+-----------------------+\n\nNotes:\n\n1. *t* must have the same length as the slice it is replacing.\n\n2. *x* can be any iterable object.\n\n3. Raises ``ValueError`` when *x* is not found in *s*. When a negative\n index is passed as the second or third parameter to the ``index()``\n method, the sequence length is added, as for slice indices. If it\n is still negative, it is truncated to zero, as for slice indices.\n\n4. When a negative index is passed as the first parameter to the\n ``insert()`` method, the sequence length is added, as for slice\n indices. If it is still negative, it is truncated to zero, as for\n slice indices.\n\n5. The optional argument *i* defaults to ``-1``, so that by default\n the last item is removed and returned.\n\n6. The ``sort()`` and ``reverse()`` methods modify the sequence in\n place for economy of space when sorting or reversing a large\n sequence. To remind you that they operate by side effect, they\n don\'t return the sorted or reversed sequence.\n\n7. The ``sort()`` method takes optional arguments for controlling the\n comparisons. Each must be specified as a keyword argument.\n\n *key* specifies a function of one argument that is used to extract\n a comparison key from each list element: ``key=str.lower``. The\n default value is ``None``. Use ``functools.cmp_to_key()`` to\n convert an old-style *cmp* function to a *key* function.\n\n *reverse* is a boolean value. If set to ``True``, then the list\n elements are sorted as if each comparison were reversed.\n\n The ``sort()`` method is guaranteed to be stable. A sort is stable\n if it guarantees not to change the relative order of elements that\n compare equal --- this is helpful for sorting in multiple passes\n (for example, sort by department, then by salary grade).\n\n **CPython implementation detail:** While a list is being sorted,\n the effect of attempting to mutate, or even inspect, the list is\n undefined. The C implementation of Python makes the list appear\n empty for the duration, and raises ``ValueError`` if it can detect\n that the list has been mutated during a sort.\n\n8. ``sort()`` is not supported by ``bytearray`` objects.\n\n\nBytes and Byte Array Methods\n============================\n\nBytes and bytearray objects, being "strings of bytes", have all\nmethods found on strings, with the exception of ``encode()``,\n``format()`` and ``isidentifier()``, which do not make sense with\nthese types. For converting the objects to strings, they have a\n``decode()`` method.\n\nWherever one of these methods needs to interpret the bytes as\ncharacters (e.g. the ``is...()`` methods), the ASCII character set is\nassumed.\n\nNote: The methods on bytes and bytearray objects don\'t accept strings as\n their arguments, just as the methods on strings don\'t accept bytes\n as their arguments. For example, you have to write\n\n a = "abc"\n b = a.replace("a", "f")\n\n and\n\n a = b"abc"\n b = a.replace(b"a", b"f")\n\nbytes.decode(encoding=sys.getdefaultencoding(), errors="strict")\nbytearray.decode(encoding=sys.getdefaultencoding(), errors="strict")\n\n Return a string decoded from the given bytes. Default encoding is\n the current default string encoding. *errors* may be given to set\n a different error handling scheme. The default for *errors* is\n ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'`` and any other name registered via\n ``codecs.register_error()``, see section *Codec Base Classes*. For\n a list of possible encodings, see section *Standard Encodings*.\n\n Changed in version 3.1: Added support for keyword arguments.\n\nThe bytes and bytearray types have an additional class method:\n\nclassmethod bytes.fromhex(string)\nclassmethod bytearray.fromhex(string)\n\n This ``bytes`` class method returns a bytes or bytearray object,\n decoding the given string object. The string must contain two\n hexadecimal digits per byte, spaces are ignored.\n\n >>> bytes.fromhex(\'f0 f1f2 \')\n b\'\\xf0\\xf1\\xf2\'\n\nThe maketrans and translate methods differ in semantics from the\nversions available on strings:\n\nbytes.translate(table[, delete])\nbytearray.translate(table[, delete])\n\n Return a copy of the bytes or bytearray object where all bytes\n occurring in the optional argument *delete* are removed, and the\n remaining bytes have been mapped through the given translation\n table, which must be a bytes object of length 256.\n\n You can use the ``bytes.maketrans()`` method to create a\n translation table.\n\n Set the *table* argument to ``None`` for translations that only\n delete characters:\n\n >>> b\'read this short text\'.translate(None, b\'aeiou\')\n b\'rd ths shrt txt\'\n\nstatic bytes.maketrans(from, to)\nstatic bytearray.maketrans(from, to)\n\n This static method returns a translation table usable for\n ``bytes.translate()`` that will map each character in *from* into\n the character at the same position in *to*; *from* and *to* must be\n bytes objects and have the same length.\n\n New in version 3.1.\n', 'typesseq-mutable': '\nMutable Sequence Types\n**********************\n\nList and bytearray objects support additional operations that allow\nin-place modification of the object. Other mutable sequence types\n(when added to the language) should also support these operations.\nStrings and tuples are immutable sequence types: such objects cannot\nbe modified once created. The following operations are defined on\nmutable sequence types (where *x* is an arbitrary object).\n\nNote that while lists allow their items to be of any type, bytearray\nobject "items" are all integers in the range 0 <= x < 256.\n\n+--------------------------------+----------------------------------+-----------------------+\n| Operation | Result | Notes |\n+================================+==================================+=======================+\n| ``s[i] = x`` | item *i* of *s* is replaced by | |\n| | *x* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j] = t`` | slice of *s* from *i* to *j* is | |\n| | replaced by the contents of the | |\n| | iterable *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j]`` | same as ``s[i:j] = []`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j:k] = t`` | the elements of ``s[i:j:k]`` are | (1) |\n| | replaced by those of *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j:k]`` | removes the elements of | |\n| | ``s[i:j:k]`` from the list | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.append(x)`` | same as ``s[len(s):len(s)] = | |\n| | [x]`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.extend(x)`` | same as ``s[len(s):len(s)] = x`` | (2) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.count(x)`` | return number of *i*\'s for which | |\n| | ``s[i] == x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.index(x[, i[, j]])`` | return smallest *k* such that | (3) |\n| | ``s[k] == x`` and ``i <= k < j`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.insert(i, x)`` | same as ``s[i:i] = [x]`` | (4) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.pop([i])`` | same as ``x = s[i]; del s[i]; | (5) |\n| | return x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.remove(x)`` | same as ``del s[s.index(x)]`` | (3) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.reverse()`` | reverses the items of *s* in | (6) |\n| | place | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.sort([key[, reverse]])`` | sort the items of *s* in place | (6), (7), (8) |\n+--------------------------------+----------------------------------+-----------------------+\n\nNotes:\n\n1. *t* must have the same length as the slice it is replacing.\n\n2. *x* can be any iterable object.\n\n3. Raises ``ValueError`` when *x* is not found in *s*. When a negative\n index is passed as the second or third parameter to the ``index()``\n method, the sequence length is added, as for slice indices. If it\n is still negative, it is truncated to zero, as for slice indices.\n\n4. When a negative index is passed as the first parameter to the\n ``insert()`` method, the sequence length is added, as for slice\n indices. If it is still negative, it is truncated to zero, as for\n slice indices.\n\n5. The optional argument *i* defaults to ``-1``, so that by default\n the last item is removed and returned.\n\n6. The ``sort()`` and ``reverse()`` methods modify the sequence in\n place for economy of space when sorting or reversing a large\n sequence. To remind you that they operate by side effect, they\n don\'t return the sorted or reversed sequence.\n\n7. The ``sort()`` method takes optional arguments for controlling the\n comparisons. Each must be specified as a keyword argument.\n\n *key* specifies a function of one argument that is used to extract\n a comparison key from each list element: ``key=str.lower``. The\n default value is ``None``. Use ``functools.cmp_to_key()`` to\n convert an old-style *cmp* function to a *key* function.\n\n *reverse* is a boolean value. If set to ``True``, then the list\n elements are sorted as if each comparison were reversed.\n\n The ``sort()`` method is guaranteed to be stable. A sort is stable\n if it guarantees not to change the relative order of elements that\n compare equal --- this is helpful for sorting in multiple passes\n (for example, sort by department, then by salary grade).\n\n **CPython implementation detail:** While a list is being sorted,\n the effect of attempting to mutate, or even inspect, the list is\n undefined. The C implementation of Python makes the list appear\n empty for the duration, and raises ``ValueError`` if it can detect\n that the list has been mutated during a sort.\n\n8. ``sort()`` is not supported by ``bytearray`` objects.\n', 'unary': '\nUnary arithmetic and bitwise operations\n***************************************\n\nAll unary arithmetic and bitwise operations have the same priority:\n\n u_expr ::= power | "-" u_expr | "+" u_expr | "~" u_expr\n\nThe unary ``-`` (minus) operator yields the negation of its numeric\nargument.\n\nThe unary ``+`` (plus) operator yields its numeric argument unchanged.\n\nThe unary ``~`` (invert) operator yields the bitwise inversion of its\ninteger argument. The bitwise inversion of ``x`` is defined as\n``-(x+1)``. It only applies to integral numbers.\n\nIn all three cases, if the argument does not have the proper type, a\n``TypeError`` exception is raised.\n', 'while': '\nThe ``while`` statement\n***********************\n\nThe ``while`` statement is used for repeated execution as long as an\nexpression is true:\n\n while_stmt ::= "while" expression ":" suite\n ["else" ":" suite]\n\nThis repeatedly tests the expression and, if it is true, executes the\nfirst suite; if the expression is false (which may be the first time\nit is tested) the suite of the ``else`` clause, if present, is\nexecuted and the loop terminates.\n\nA ``break`` statement executed in the first suite terminates the loop\nwithout executing the ``else`` clause\'s suite. A ``continue``\nstatement executed in the first suite skips the rest of the suite and\ngoes back to testing the expression.\n', - 'with': '\nThe ``with`` statement\n**********************\n\nThe ``with`` statement is used to wrap the execution of a block with\nmethods defined by a context manager (see section *With Statement\nContext Managers*). This allows common\n``try``...``except``...``finally`` usage patterns to be encapsulated\nfor convenient reuse.\n\n with_stmt ::= "with" with_item ("," with_item)* ":" suite\n with_item ::= expression ["as" target]\n\nThe execution of the ``with`` statement with one "item" proceeds as\nfollows:\n\n1. The context expression is evaluated to obtain a context manager.\n\n2. The context manager\'s ``__exit__()`` is loaded for later use.\n\n3. The context manager\'s ``__enter__()`` method is invoked.\n\n4. If a target was included in the ``with`` statement, the return\n value from ``__enter__()`` is assigned to it.\n\n Note: The ``with`` statement guarantees that if the ``__enter__()``\n method returns without an error, then ``__exit__()`` will always\n be called. Thus, if an error occurs during the assignment to the\n target list, it will be treated the same as an error occurring\n within the suite would be. See step 6 below.\n\n5. The suite is executed.\n\n6. The context manager\'s ``__exit__()`` method is invoked. If an\n exception caused the suite to be exited, its type, value, and\n traceback are passed as arguments to ``__exit__()``. Otherwise,\n three ``None`` arguments are supplied.\n\n If the suite was exited due to an exception, and the return value\n from the ``__exit__()`` method was false, the exception is\n reraised. If the return value was true, the exception is\n suppressed, and execution continues with the statement following\n the ``with`` statement.\n\n If the suite was exited for any reason other than an exception, the\n return value from ``__exit__()`` is ignored, and execution proceeds\n at the normal location for the kind of exit that was taken.\n\nWith more than one item, the context managers are processed as if\nmultiple ``with`` statements were nested:\n\n with A() as a, B() as b:\n suite\n\nis equivalent to\n\n with A() as a:\n with B() as b:\n suite\n\nChanged in version 3.1: Support for multiple context expressions.\n\nSee also:\n\n **PEP 0343** - The "with" statement\n The specification, background, and examples for the Python\n ``with`` statement.\n', + 'with': '\nThe ``with`` statement\n**********************\n\nThe ``with`` statement is used to wrap the execution of a block with\nmethods defined by a context manager (see section *With Statement\nContext Managers*). This allows common\n``try``...``except``...``finally`` usage patterns to be encapsulated\nfor convenient reuse.\n\n with_stmt ::= "with" with_item ("," with_item)* ":" suite\n with_item ::= expression ["as" target]\n\nThe execution of the ``with`` statement with one "item" proceeds as\nfollows:\n\n1. The context expression (the expression given in the ``with_item``)\n is evaluated to obtain a context manager.\n\n2. The context manager\'s ``__exit__()`` is loaded for later use.\n\n3. The context manager\'s ``__enter__()`` method is invoked.\n\n4. If a target was included in the ``with`` statement, the return\n value from ``__enter__()`` is assigned to it.\n\n Note: The ``with`` statement guarantees that if the ``__enter__()``\n method returns without an error, then ``__exit__()`` will always\n be called. Thus, if an error occurs during the assignment to the\n target list, it will be treated the same as an error occurring\n within the suite would be. See step 6 below.\n\n5. The suite is executed.\n\n6. The context manager\'s ``__exit__()`` method is invoked. If an\n exception caused the suite to be exited, its type, value, and\n traceback are passed as arguments to ``__exit__()``. Otherwise,\n three ``None`` arguments are supplied.\n\n If the suite was exited due to an exception, and the return value\n from the ``__exit__()`` method was false, the exception is\n reraised. If the return value was true, the exception is\n suppressed, and execution continues with the statement following\n the ``with`` statement.\n\n If the suite was exited for any reason other than an exception, the\n return value from ``__exit__()`` is ignored, and execution proceeds\n at the normal location for the kind of exit that was taken.\n\nWith more than one item, the context managers are processed as if\nmultiple ``with`` statements were nested:\n\n with A() as a, B() as b:\n suite\n\nis equivalent to\n\n with A() as a:\n with B() as b:\n suite\n\nChanged in version 3.1: Support for multiple context expressions.\n\nSee also:\n\n **PEP 0343** - The "with" statement\n The specification, background, and examples for the Python\n ``with`` statement.\n', 'yield': '\nThe ``yield`` statement\n***********************\n\n yield_stmt ::= yield_expression\n\nThe ``yield`` statement is only used when defining a generator\nfunction, and is only used in the body of the generator function.\nUsing a ``yield`` statement in a function definition is sufficient to\ncause that definition to create a generator function instead of a\nnormal function. When a generator function is called, it returns an\niterator known as a generator iterator, or more commonly, a generator.\nThe body of the generator function is executed by calling the\n``next()`` function on the generator repeatedly until it raises an\nexception.\n\nWhen a ``yield`` statement is executed, the state of the generator is\nfrozen and the value of ``expression_list`` is returned to\n``next()``\'s caller. By "frozen" we mean that all local state is\nretained, including the current bindings of local variables, the\ninstruction pointer, and the internal evaluation stack: enough\ninformation is saved so that the next time ``next()`` is invoked, the\nfunction can proceed exactly as if the ``yield`` statement were just\nanother external call.\n\nThe ``yield`` statement is allowed in the ``try`` clause of a ``try``\n... ``finally`` construct. If the generator is not resumed before it\nis finalized (by reaching a zero reference count or by being garbage\ncollected), the generator-iterator\'s ``close()`` method will be\ncalled, allowing any pending ``finally`` clauses to execute.\n\nSee also:\n\n **PEP 0255** - Simple Generators\n The proposal for adding generators and the ``yield`` statement\n to Python.\n\n **PEP 0342** - Coroutines via Enhanced Generators\n The proposal that, among other generator enhancements, proposed\n allowing ``yield`` to appear inside a ``try`` ... ``finally``\n block.\n'} From python-checkins at python.org Sun Sep 5 10:30:10 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 10:30:10 +0200 (CEST) Subject: [Python-checkins] r84521 - sandbox/trunk/release/release.py Message-ID: <20100905083010.1CA2AEE9C0@mail.python.org> Author: georg.brandl Date: Sun Sep 5 10:30:09 2010 New Revision: 84521 Log: Fix constant name. Modified: sandbox/trunk/release/release.py Modified: sandbox/trunk/release/release.py ============================================================================== --- sandbox/trunk/release/release.py (original) +++ sandbox/trunk/release/release.py Sun Sep 5 10:30:09 2010 @@ -148,7 +148,7 @@ print('File was renamed; please commit') run_cmd(['svn', 'commit']) new = '%define version ' + tag.text + \ - '\n%define libver ' + tag.basic_version + '\n%define libvers ' + tag.basic_version constant_replace(wanted_file, new, '#', '') print('done') From python-checkins at python.org Sun Sep 5 10:30:40 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 10:30:40 +0200 (CEST) Subject: [Python-checkins] r84522 - in python/branches/py3k: Include/patchlevel.h Lib/distutils/__init__.py Lib/idlelib/idlever.py Misc/NEWS Misc/RPM/python-3.2.spec README Message-ID: <20100905083040.C3E7FEE999@mail.python.org> Author: georg.brandl Date: Sun Sep 5 10:30:40 2010 New Revision: 84522 Log: Bump to 3.2a2. Modified: python/branches/py3k/Include/patchlevel.h python/branches/py3k/Lib/distutils/__init__.py python/branches/py3k/Lib/idlelib/idlever.py python/branches/py3k/Misc/NEWS python/branches/py3k/Misc/RPM/python-3.2.spec python/branches/py3k/README Modified: python/branches/py3k/Include/patchlevel.h ============================================================================== --- python/branches/py3k/Include/patchlevel.h (original) +++ python/branches/py3k/Include/patchlevel.h Sun Sep 5 10:30:40 2010 @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 2 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 1 +#define PY_RELEASE_SERIAL 2 /* Version as a string */ -#define PY_VERSION "3.2a1+" +#define PY_VERSION "3.2a2" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository) */ Modified: python/branches/py3k/Lib/distutils/__init__.py ============================================================================== --- python/branches/py3k/Lib/distutils/__init__.py (original) +++ python/branches/py3k/Lib/distutils/__init__.py Sun Sep 5 10:30:40 2010 @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a1" +__version__ = "3.2a2" #--end constants-- Modified: python/branches/py3k/Lib/idlelib/idlever.py ============================================================================== --- python/branches/py3k/Lib/idlelib/idlever.py (original) +++ python/branches/py3k/Lib/idlelib/idlever.py Sun Sep 5 10:30:40 2010 @@ -1 +1 @@ -IDLE_VERSION = "3.2a1" +IDLE_VERSION = "3.2a2" Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 5 10:30:40 2010 @@ -7,7 +7,7 @@ What's New in Python 3.2 Alpha 2? ================================= -*Release date: XX-Sep-2010* +*Release date: 05-Sep-2010* Core and Builtins ----------------- Modified: python/branches/py3k/Misc/RPM/python-3.2.spec ============================================================================== --- python/branches/py3k/Misc/RPM/python-3.2.spec (original) +++ python/branches/py3k/Misc/RPM/python-3.2.spec Sun Sep 5 10:30:40 2010 @@ -39,7 +39,7 @@ %define name python #--start constants-- -%define version 3.2a1 +%define version 3.2a2 %define libvers 3.2 #--end constants-- %define release 1pydotorg Modified: python/branches/py3k/README ============================================================================== --- python/branches/py3k/README (original) +++ python/branches/py3k/README Sun Sep 5 10:30:40 2010 @@ -1,4 +1,4 @@ -This is Python version 3.2 alpha 1 +This is Python version 3.2 alpha 2 ================================== Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 From python-checkins at python.org Sun Sep 5 10:35:38 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 10:35:38 +0200 (CEST) Subject: [Python-checkins] r84523 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100905083538.6ADF5FB8D@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 10:35:38 2010 New Revision: 84523 Log: Add PEP 391 to whatsnew Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 10:35:38 2010 @@ -51,6 +51,49 @@ This article explains the new features in Python 3.2, compared to 3.1. +PEP 391: Dictionary Based Configuration for Logging +=================================================== + +The :mod:`logging` module had two ways of configuring the module, either +calling functions for each option or by reading an external file saved +in a ConfigParser format. Those options did not provide the flexibility +to create configurations from JSON or YAML files and they did not support +incremental configuration which is needed for specifying logger options +from a command line. + +To support a more flexible style, the module now offers +:func:`logging.config.dictConfig` to use dictionaries to specify logger +configurations (including formatters, handlers, filters, and loggers). +For example:: + + >>> import logging.config + >>> logging.config.dictConfig(json.load(open('log.cfg', 'rb'))) + +The above fragment configures logging from a JSON encoded dictionary stored in +file called "log.cfg". Here's a working example of a configuration dictionary:: + + {"version": 1, + "formatters": {"brief": {"format": "%(levelname)-8s: %(name)-15s: %(message)s"}, + "full": {"format": "%(asctime)s %(name)-15s %(levelname)-8s %(message)s"}, + }, + "handlers": {"console": { + "class": "logging.StreamHandler", + "formatter": "brief", + "level": "INFO", + "stream": "ext://sys.stdout"}, + "console_priority": { + "class": "logging.StreamHandler", + "formatter": "full", + "level": "ERROR", + "stream": "ext://sys.stderr"}, + }, + "root": {"level": "DEBUG", "handlers": ["console", "console_priority"]}} + +.. seealso:: + + :pep:`391` - Dictionary Based Configuration for Logging + PEP written by Vinay Sajip. + PEP 3147: PYC Repository Directories ===================================== From python-checkins at python.org Sun Sep 5 10:46:36 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 10:46:36 +0200 (CEST) Subject: [Python-checkins] r84524 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100905084636.55287EE9AA@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 10:46:36 2010 New Revision: 84524 Log: Add example of ftplib's new context manager. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 10:46:36 2010 @@ -216,7 +216,7 @@ :issue:`8814`.) * The :mod:`abc` module now supports :func:`abstractclassmethod` and - :func:`staticmethod`. + :func:`abstractstaticmethod`. (:issue:`5867`) @@ -261,6 +261,20 @@ `appspot issue 53094 `_.) * The :class:`ftplib.FTP` class now supports the context manager protocol + to unconditionally consume :exc:`socket.error` exceptions and to close + the ftp connection when done:: + + >>> from ftplib import FTP + >>> with FTP("ftp1.at.proftpd.org") as ftp: + ... ftp.login() + ... ftp.dir() + ... + '230 Anonymous login ok, restrictions apply.' + dr-xr-xr-x 9 ftp ftp 154 May 6 10:43 . + dr-xr-xr-x 9 ftp ftp 154 May 6 10:43 .. + dr-xr-xr-x 5 ftp ftp 4096 May 6 10:43 CentOS + dr-xr-xr-x 3 ftp ftp 18 Jul 10 2008 Fedora + (Contributed by Tarek Ziad? and Giampaolo Rodol?; :issue:`4972`.) * A warning message will now get printed at interpreter shutdown if @@ -270,16 +284,17 @@ * The :mod:`os` module now has the :const:`ST_RDONLY` and :const:`ST_NOSUID` constants, for use with the :func:`~os.statvfs` function. + (Patch by Adam Jackson; :issue:`7647`.) * The :func:`shutil.copytree` function has two new options: - * *ignore_dangling_symlinks*: when ``symlinks=False`` (meaning that the + * *ignore_dangling_symlinks*: when ``symlinks=False`` dp that the function copies the file pointed to by the symlink, not the symlink - itself) this option will silence the error raised if the file doesn't + itself. This option will silence the error raised if the file doesn't exist. - * *copy_function*: a callable that will be used to copy files. + * *copy_function*: is a callable that will be used to copy files. :func:`shutil.copy2` is used by default. (Contributed by Tarek Ziad?.) From python-checkins at python.org Sun Sep 5 10:54:32 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 5 Sep 2010 10:54:32 +0200 (CEST) Subject: [Python-checkins] r84525 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100905085432.70434F860@mail.python.org> Author: raymond.hettinger Date: Sun Sep 5 10:54:32 2010 New Revision: 84525 Log: Fill-in sqlite3 stubs Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 10:54:32 2010 @@ -305,16 +305,17 @@ (Added by Antoine Pitrou; :issue:`8524`.) -* The :mod:`sqlite3` module has some new features: +* The :mod:`sqlite3` module has two new capabilities. - * XXX *enable_load_extension* + The :attr:`Connection.in_transit` attribute is true if there is an + active transaction for uncommitted changes. - * XXX *load_extension* + The :meth:`Connection.enable_load_extension` and + :meth:`Connection.load_extension` methods allows you to load SQLite extensions + from ".so" files. One well-known extension is the fulltext-search extension + distributed with SQLite. - * New :class:`~sqlite3.Connection` attribute - :attr:`~sqlite3.Connection.in_transaction` is :const:`True` when there - are uncommitted changes, and :const:`False` otherwise. (Contributed - by R. David Murray and Shashwat Anand, :issue:`8845`.) + (Contributed by R. David Murray and Shashwat Anand, :issue:`8845`.) * The :mod:`ssl` module has a new class, :class:`~ssl.SSLContext` which serves as a container for various persistent SSL data, such as protocol From python-checkins at python.org Sun Sep 5 11:06:42 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 11:06:42 +0200 (CEST) Subject: [Python-checkins] r84526 - sandbox/trunk/release/release.py Message-ID: <20100905090642.02377EE999@mail.python.org> Author: georg.brandl Date: Sun Sep 5 11:06:41 2010 New Revision: 84526 Log: Fix a few nits and unbound locals. Modified: sandbox/trunk/release/release.py Modified: sandbox/trunk/release/release.py ============================================================================== --- sandbox/trunk/release/release.py (original) +++ sandbox/trunk/release/release.py Sun Sep 5 11:06:41 2010 @@ -3,7 +3,7 @@ """An assistant for making Python releases. Original code by Benjamin Peterson -Additions by Barry Warsaw and Benjamin Peterson +Additions by Barry Warsaw, Georg Brandl and Benjamin Peterson """ import sys @@ -13,7 +13,6 @@ import re import subprocess import shutil -import tempfile from contextlib import contextmanager from string import Template @@ -44,6 +43,8 @@ code = subprocess.call(cmd, shell=True) except OSError: error('%s failed' % cmd) + else: + return code def check_env(): @@ -249,7 +250,6 @@ def export(tag): make_dist() - old_cur = os.getcwd() with changed_dir('dist'): print('Exporting tag:', tag.text) archivename = 'Python-%s' % tag.text @@ -310,7 +310,7 @@ """scp everything to dinsdale""" address ='"%s at dinsdale.python.org:' % username def scp(from_loc, to_loc): - run_cmd(['scp %s %s' % (from_loc, to_loc)]) + run_cmd(['scp %s %s' % (from_loc, address + to_loc)]) with changed_dir('dist'): print("Uploading source tarballs") scp('src', '/data/python-releases/%s' % tag.nickname) @@ -325,7 +325,7 @@ def __init__(self, tag_name): result = tag_cre.search(tag_name) if result is None: - error('tag %s is not valid' % tag) + error('tag %s is not valid' % tag_name) data = list(result.groups()) if data[3] is None: # A final release. From python-checkins at python.org Sun Sep 5 11:27:23 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 11:27:23 +0200 (CEST) Subject: [Python-checkins] r84527 - sandbox/trunk/release/release.py Message-ID: <20100905092723.AE5F9FBFF@mail.python.org> Author: georg.brandl Date: Sun Sep 5 11:27:23 2010 New Revision: 84527 Log: Use new-style string formatting. Modified: sandbox/trunk/release/release.py Modified: sandbox/trunk/release/release.py ============================================================================== --- sandbox/trunk/release/release.py (original) +++ sandbox/trunk/release/release.py Sun Sep 5 11:27:23 2010 @@ -15,7 +15,6 @@ import shutil from contextlib import contextmanager -from string import Template from urllib.parse import urlsplit, urlunsplit COMMASPACE = ', ' @@ -108,27 +107,23 @@ def tweak_patchlevel(tag, done=False): print('Updating Include/patchlevel.h...', end=' ') - template = Template("""\ -#define PY_MAJOR_VERSION\t$major -#define PY_MINOR_VERSION\t$minor -#define PY_MICRO_VERSION\t$patch -#define PY_RELEASE_LEVEL\t$level -#define PY_RELEASE_SERIAL\t$serial + template = """\ +#define PY_MAJOR_VERSION\t{tag.major} +#define PY_MINOR_VERSION\t{tag.minor} +#define PY_MICRO_VERSION\t{tag.patch} +#define PY_RELEASE_LEVEL\t{level_def} +#define PY_RELEASE_SERIAL\t{tag.serial} /* Version as a string */ -#define PY_VERSION \t\"$text\"""") - substitutions = {} - for what in ('major', 'minor', 'patch', 'serial', 'text'): - substitutions[what] = getattr(tag, what) - substitutions['level'] = dict( +#define PY_VERSION \t\"{tag.text}{plus}\"""" + level_def = dict( a = 'PY_RELEASE_LEVEL_ALPHA', b = 'PY_RELEASE_LEVEL_BETA', rc = 'PY_RELEASE_LEVEL_GAMMA', f = 'PY_RELEASE_LEVEL_FINAL', )[tag.level] - if done: - substitutions['text'] += '+' - new_constants = template.substitute(substitutions) + new_constants = template.format(tag=tag, level_def=level_def, + plus=done and '+' or '') constant_replace('Include/patchlevel.h', new_constants) print('done') From python-checkins at python.org Sun Sep 5 13:28:33 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 13:28:33 +0200 (CEST) Subject: [Python-checkins] r84528 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100905112833.97F68F629@mail.python.org> Author: georg.brandl Date: Sun Sep 5 13:28:33 2010 New Revision: 84528 Log: Rewrap and consistency fixes. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 13:28:33 2010 @@ -36,14 +36,12 @@ necessary (especially when a final release is some months away). * Credit the author of a patch or bugfix. Just the name is - sufficient; the e-mail address isn't necessary. + sufficient; the e-mail address isn't necessary. It's helpful to + add the issue number: - * It's helpful to add the bug/patch number as a comment: - - % Patch 12345 XXX Describe the transmogrify() function added to the socket module. - (Contributed by P.Y. Developer.) + (Contributed by P.Y. Developer; :issue:`12345`.) This saves the maintainer the effort of going through the SVN log when researching a change. @@ -54,46 +52,46 @@ PEP 391: Dictionary Based Configuration for Logging =================================================== -The :mod:`logging` module had two ways of configuring the module, either -calling functions for each option or by reading an external file saved -in a ConfigParser format. Those options did not provide the flexibility -to create configurations from JSON or YAML files and they did not support -incremental configuration which is needed for specifying logger options -from a command line. +The :mod:`logging` module had two ways of configuring the module, either calling +functions for each option or by reading an external file saved in a ConfigParser +format. Those options did not provide the flexibility to create configurations +from JSON or YAML files and they did not support incremental configuration which +is needed for specifying logger options from a command line. To support a more flexible style, the module now offers :func:`logging.config.dictConfig` to use dictionaries to specify logger -configurations (including formatters, handlers, filters, and loggers). -For example:: +configurations (including formatters, handlers, filters, and loggers). For +example: - >>> import logging.config - >>> logging.config.dictConfig(json.load(open('log.cfg', 'rb'))) +>>> import logging.config +>>> logging.config.dictConfig(json.load(open('log.cfg', 'rb'))) The above fragment configures logging from a JSON encoded dictionary stored in file called "log.cfg". Here's a working example of a configuration dictionary:: - {"version": 1, - "formatters": {"brief": {"format": "%(levelname)-8s: %(name)-15s: %(message)s"}, - "full": {"format": "%(asctime)s %(name)-15s %(levelname)-8s %(message)s"}, - }, - "handlers": {"console": { - "class": "logging.StreamHandler", - "formatter": "brief", - "level": "INFO", - "stream": "ext://sys.stdout"}, - "console_priority": { - "class": "logging.StreamHandler", - "formatter": "full", - "level": "ERROR", - "stream": "ext://sys.stderr"}, - }, - "root": {"level": "DEBUG", "handlers": ["console", "console_priority"]}} + {"version": 1, + "formatters": {"brief": {"format": "%(levelname)-8s: %(name)-15s: %(message)s"}, + "full": {"format": "%(asctime)s %(name)-15s %(levelname)-8s %(message)s"}, + }, + "handlers": {"console": { + "class": "logging.StreamHandler", + "formatter": "brief", + "level": "INFO", + "stream": "ext://sys.stdout"}, + "console_priority": { + "class": "logging.StreamHandler", + "formatter": "full", + "level": "ERROR", + "stream": "ext://sys.stderr"}, + }, + "root": {"level": "DEBUG", "handlers": ["console", "console_priority"]}} .. seealso:: :pep:`391` - Dictionary Based Configuration for Logging PEP written by Vinay Sajip. + PEP 3147: PYC Repository Directories ===================================== @@ -117,28 +115,28 @@ Aside from the filenames and target directories, the new scheme has a few aspects that are visible to the programmer: -* Imported modules now have a :attr:`__cached__` attribute which stores the - name of the actual file that was imported:: +* Imported modules now have a :attr:`__cached__` attribute which stores the name + of the actual file that was imported: - >>> import collections - >>> collections.__cached__ - 'c:/py32/lib/__pycache__/collections.cpython-32.pyc' + >>> import collections + >>> collections.__cached__ + 'c:/py32/lib/__pycache__/collections.cpython-32.pyc' * The tag that is unique to each interpreter is accessible from the :mod:`imp` - module:: + module: - >>> import imp - >>> imp.get_tag() - 'cpython-32' + >>> import imp + >>> imp.get_tag() + 'cpython-32' * Scripts that try to deduce source filename from the imported file now need to be smarter. It is no longer sufficient to simply strip the "c" from a ".pyc" filename. Instead, use the new functions in the :mod:`imp` module: - >>> imp.source_from_cache('c:/py32/lib/__pycache__/collections.cpython-32.pyc') - 'c:/py32/lib/collections.py' - >>> imp.cache_from_source('c:/py32/lib/collections.py') - 'c:/py32/lib/__pycache__/collections.cpython-32.pyc' + >>> imp.source_from_cache('c:/py32/lib/__pycache__/collections.cpython-32.pyc') + 'c:/py32/lib/collections.py' + >>> imp.cache_from_source('c:/py32/lib/collections.py') + 'c:/py32/lib/__pycache__/collections.cpython-32.pyc' * The :mod:`py_compile` and :mod:`compileall` modules have been updated to reflect the new naming convention and target directory. @@ -148,6 +146,7 @@ :pep:`3147` - PYC Repository Directories PEP written by Barry Warsaw. + PEP 3149 ABI Version Tagged .so Files ===================================== @@ -184,27 +183,27 @@ Some smaller changes made to the core Python language are: -* The :func:`hasattr` function used to catch and suppress any Exception. - Now, it only catches :exc:`AttributeError`. Under the hood, :func:`hasattr` - works by calling :func:`getattr` and throwing away the results. This is - necessary because dynamic attribute creation is possible using - :meth:`__getattribute__` or :meth:`__getattr`. If :func:`hasattr` were to - just scan instance and class dictionaries it would miss the dynmaic methods - and make it difficult to implement proxy objects. +* The :func:`hasattr` function used to catch and suppress any Exception. Now, + it only catches :exc:`AttributeError`. Under the hood, :func:`hasattr` works + by calling :func:`getattr` and throwing away the results. This is necessary + because dynamic attribute creation is possible using :meth:`__getattribute__` + or :meth:`__getattr`. If :func:`hasattr` were to just scan instance and class + dictionaries it would miss the dynmaic methods and make it difficult to + implement proxy objects. (Discovered by Yury Selivanov and fixed by Benjamin Peterson; :issue:`9666`.) * The :func:`str` of a float or complex number is now the same as it :func:`repr`. Previously, the :func:`str` form was shorter but that just caused confusion and is no longer needed now that we the shortest possible - :func:`repr` is displayed by default:: + :func:`repr` is displayed by default: - >>> repr(math.pi) - '3.141592653589793' - >>> str(math.pi) - '3.141592653589793' + >>> repr(math.pi) + '3.141592653589793' + >>> str(math.pi) + '3.141592653589793' - (Proposed and implemented by Mark Dickinson; :issue:`9337`). + (Proposed and implemented by Mark Dickinson; :issue:`9337`.) * The :func:`functools.wraps` decorator now adds a :attr:`__wrapped__` attribute pointing to the original callable function. This allows wrapped functions to @@ -218,68 +217,70 @@ * The :mod:`abc` module now supports :func:`abstractclassmethod` and :func:`abstractstaticmethod`. - (:issue:`5867`) + (:issue:`5867`.) + New, Improved, and Deprecated Modules ===================================== -* The :mod:`functools` module now includes a new decorator for caching - function calls. :func:`functools.lru_cache` can save repeated queries to an - external resource whenever the results are expected to be the same. +* The :mod:`functools` module now includes a new decorator for caching function + calls. :func:`functools.lru_cache` can save repeated queries to an external + resource whenever the results are expected to be the same. For example, adding a caching decorator to a database query function can save database accesses for popular searches:: - @functools.lru_cache(maxsize=300) - def get_phone_number(name): - c = conn.cursor() - c.execute('SELECT phonenumber FROM phonelist WHERE name=?', (name,)) - return c.fetchone()[0] + @functools.lru_cache(maxsize=300) + def get_phone_number(name): + c = conn.cursor() + c.execute('SELECT phonenumber FROM phonelist WHERE name=?', (name,)) + return c.fetchone()[0] To help with choosing an effective cache size, the wrapped function is - instrumented with two attributes *cache_hits* and *cache_misses*:: + instrumented with two attributes *cache_hits* and *cache_misses*: - >>> for name in user_requests: - ... get_phone_number(name) - >>> print(get_phone_number.cache_hits, get_phone_number.cache_misses) - 4805 980 + >>> for name in user_requests: + ... get_phone_number(name) + >>> print(get_phone_number.cache_hits, get_phone_number.cache_misses) + 4805 980 If the phonelist table gets updated, the outdated contents of the cache can be - cleared with:: + cleared with: - >>> get_phone_number.cache_clear() + >>> get_phone_number.cache_clear() - (Contributed by Raymond Hettinger) + (Contributed by Raymond Hettinger.) -* The previously deprecated :func:`contextlib.nested` function has been - removed in favor of a plain :keyword:`with` statement which can - accept multiple context managers. The latter technique is faster - (because it is built-in), and it does a better job finalizing multiple - context managers when one of them raises an exception. +* The previously deprecated :func:`contextlib.nested` function has been removed + in favor of a plain :keyword:`with` statement which can accept multiple + context managers. The latter technique is faster (because it is built-in), + and it does a better job finalizing multiple context managers when one of them + raises an exception. (Contributed by Georg Brandl and Mattias Br?ndstr?m; `appspot issue 53094 `_.) -* The :class:`ftplib.FTP` class now supports the context manager protocol - to unconditionally consume :exc:`socket.error` exceptions and to close - the ftp connection when done:: - - >>> from ftplib import FTP - >>> with FTP("ftp1.at.proftpd.org") as ftp: - ... ftp.login() - ... ftp.dir() - ... - '230 Anonymous login ok, restrictions apply.' - dr-xr-xr-x 9 ftp ftp 154 May 6 10:43 . - dr-xr-xr-x 9 ftp ftp 154 May 6 10:43 .. - dr-xr-xr-x 5 ftp ftp 4096 May 6 10:43 CentOS - dr-xr-xr-x 3 ftp ftp 18 Jul 10 2008 Fedora +* The :class:`ftplib.FTP` class now supports the context manager protocol to + unconditionally consume :exc:`socket.error` exceptions and to close the ftp + connection when done: + + >>> from ftplib import FTP + >>> with FTP("ftp1.at.proftpd.org") as ftp: + ... ftp.login() + ... ftp.dir() + ... + '230 Anonymous login ok, restrictions apply.' + dr-xr-xr-x 9 ftp ftp 154 May 6 10:43 . + dr-xr-xr-x 9 ftp ftp 154 May 6 10:43 .. + dr-xr-xr-x 5 ftp ftp 4096 May 6 10:43 CentOS + dr-xr-xr-x 3 ftp ftp 18 Jul 10 2008 Fedora (Contributed by Tarek Ziad? and Giampaolo Rodol?; :issue:`4972`.) -* A warning message will now get printed at interpreter shutdown if - the :data:`gc.garbage` list isn't empty. This is meant to make the - programmer aware that his code contains object finalization issues. +* A warning message will now get printed at interpreter shutdown if the + :data:`gc.garbage` list isn't empty. This is meant to make the programmer + aware that his code contains object finalization issues. + (Added by Antoine Pitrou; :issue:`477863`.) * The :mod:`os` module now has the :const:`ST_RDONLY` and :const:`ST_NOSUID` @@ -289,72 +290,66 @@ * The :func:`shutil.copytree` function has two new options: - * *ignore_dangling_symlinks*: when ``symlinks=False`` dp that the - function copies the file pointed to by the symlink, not the symlink - itself. This option will silence the error raised if the file doesn't - exist. + * *ignore_dangling_symlinks*: when ``symlinks=False`` dp that the function + copies the file pointed to by the symlink, not the symlink itself. This + option will silence the error raised if the file doesn't exist. * *copy_function*: is a callable that will be used to copy files. :func:`shutil.copy2` is used by default. (Contributed by Tarek Ziad?.) -* Socket objects now have a :meth:`~socket.socket.detach()` method which - puts the socket into closed state without actually closing the underlying - file descriptor. The latter can then be reused for other purposes. +* Socket objects now have a :meth:`~socket.socket.detach()` method which puts + the socket into closed state without actually closing the underlying file + descriptor. The latter can then be reused for other purposes. (Added by Antoine Pitrou; :issue:`8524`.) * The :mod:`sqlite3` module has two new capabilities. - The :attr:`Connection.in_transit` attribute is true if there is an - active transaction for uncommitted changes. + The :attr:`Connection.in_transit` attribute is true if there is an active + transaction for uncommitted changes. The :meth:`Connection.enable_load_extension` and :meth:`Connection.load_extension` methods allows you to load SQLite extensions from ".so" files. One well-known extension is the fulltext-search extension distributed with SQLite. - (Contributed by R. David Murray and Shashwat Anand, :issue:`8845`.) + (Contributed by R. David Murray and Shashwat Anand; :issue:`8845`.) -* The :mod:`ssl` module has a new class, :class:`~ssl.SSLContext` which - serves as a container for various persistent SSL data, such as protocol - settings, certificates, private keys, and various other options. - The :meth:`~ssl.SSLContext.wrap_socket` method allows to create an - SSL socket from such an SSL context. - (Added by Antoine Pitrou; :issue:`8550`.) - - The :func:`ssl.wrap_socket` constructor function now takes a - *ciphers* argument that's a string listing the encryption algorithms - to be allowed; the format of the string is described - `in the OpenSSL documentation - `__. - (Added by Antoine Pitrou; :issue:`8322`.) +* The :mod:`ssl` module has a new class, :class:`~ssl.SSLContext` which serves + as a container for various persistent SSL data, such as protocol settings, + certificates, private keys, and various other options. The + :meth:`~ssl.SSLContext.wrap_socket` method allows to create an SSL socket from + such an SSL context. (Added by Antoine Pitrou; :issue:`8550`.) + + The :func:`ssl.wrap_socket` constructor function now takes a *ciphers* + argument that's a string listing the encryption algorithms to be allowed; the + format of the string is described `in the OpenSSL documentation + `__. (Added + by Antoine Pitrou; :issue:`8322`.) Various options have been added to the :mod:`ssl` module, such as - :data:`~ssl.OP_NO_SSLv2` which allows to force disabling of the insecure - and obsolete SSLv2 protocol. - (Added by Antoine Pitrou; :issue:`4870`.) - - Another change makes the extension load all of OpenSSL's ciphers and - digest algorithms so that they're all available. Some SSL - certificates couldn't be verified, reporting an "unknown algorithm" - error. (Reported by Beda Kosata, and fixed by Antoine Pitrou; - :issue:`8484`.) - - The version of OpenSSL being used is now available as the module - attributes :data:`ssl.OPENSSL_VERSION` (a string), - :data:`ssl.OPENSSL_VERSION_INFO` (a 5-tuple), and - :data:`ssl.OPENSSL_VERSION_NUMBER` (an integer). (Added by Antoine - Pitrou; :issue:`8321`.) + :data:`~ssl.OP_NO_SSLv2` which allows to force disabling of the insecure and + obsolete SSLv2 protocol. (Added by Antoine Pitrou; :issue:`4870`.) -* The previously deprecated :func:`string.maketrans` function has been - removed in favor of the static methods, :meth:`bytes.maketrans` and + Another change makes the extension load all of OpenSSL's ciphers and digest + algorithms so that they're all available. Some SSL certificates couldn't be + verified, reporting an "unknown algorithm" error. (Reported by Beda Kosata, + and fixed by Antoine Pitrou; :issue:`8484`.) + + The version of OpenSSL being used is now available as the module attributes + :data:`ssl.OPENSSL_VERSION` (a string), :data:`ssl.OPENSSL_VERSION_INFO` (a + 5-tuple), and :data:`ssl.OPENSSL_VERSION_NUMBER` (an integer). (Added by + Antoine Pitrou; :issue:`8321`.) + +* The previously deprecated :func:`string.maketrans` function has been removed + in favor of the static methods, :meth:`bytes.maketrans` and :meth:`bytearray.maketrans`. This change solves the confusion around which - types were supported by the :mod:`string` module. Now, :class:`str`, + types were supported by the :mod:`string` module. Now, :class:`str`, :class:`bytes`, and :class:`bytearray` each have their own **maketrans** and - **translate** methods with intermediate translation tables of the - appropriate type. + **translate** methods with intermediate translation tables of the appropriate + type. (Contributed by Georg Brandl; :issue:`5675`.) @@ -365,44 +360,46 @@ (Contributed by Giampaolo Rodol?; :issue:`8807`.) + Multi-threading =============== -* The mechanism for serializing execution of concurrently running Python - threads (generally known as the GIL or Global Interpreter Lock) has been - rewritten. Among the objectives were more predictable switching intervals - and reduced overhead due to lock contention and the number of ensuing - system calls. The notion of a "check interval" to allow thread switches - has been abandoned and replaced by an absolute duration expressed in - seconds. This parameter is tunable through :func:`sys.setswitchinterval()`. - It currently defaults to 5 milliseconds. +* The mechanism for serializing execution of concurrently running Python threads + (generally known as the GIL or Global Interpreter Lock) has been rewritten. + Among the objectives were more predictable switching intervals and reduced + overhead due to lock contention and the number of ensuing system calls. The + notion of a "check interval" to allow thread switches has been abandoned and + replaced by an absolute duration expressed in seconds. This parameter is + tunable through :func:`sys.setswitchinterval()`. It currently defaults to 5 + milliseconds. Additional details about the implementation can be read from a `python-dev mailing-list message `_ - (however, "priority requests" as exposed in this message have not been - kept for inclusion). + (however, "priority requests" as exposed in this message have not been kept + for inclusion). (Contributed by Antoine Pitrou.) * Recursive locks (created with the :func:`threading.RLock` API) now benefit - from a C implementation which makes them as fast as regular locks, and - between 10x and 15x faster than their previous pure Python implementation. + from a C implementation which makes them as fast as regular locks, and between + 10x and 15x faster than their previous pure Python implementation. (Contributed by Antoine Pitrou; :issue:`3001`.) -* Regular and recursive locks now accept an optional *timeout* argument - to their ``acquire`` method. (Contributed by Antoine Pitrou; :issue:`7316`) +* Regular and recursive locks now accept an optional *timeout* argument to their + ``acquire`` method. (Contributed by Antoine Pitrou; :issue:`7316`.) + Similarly, :meth:`threading.Semaphore.acquire` also gains a *timeout* - argument. (Contributed by Torsten Landschoff; :issue:`850728`.) + argument. (Contributed by Torsten Landschoff; :issue:`850728`.) -Optimizations -============= +.. Optimizations + ============= -Major performance enhancements have been added: + Major performance enhancements have been added: -* Stub + * Stub Filenames and unicode @@ -418,10 +415,10 @@ :func:`os.fsdecode`. -IDLE -==== +.. IDLE + ==== -* Stub + * Stub Build and C API Changes @@ -429,27 +426,30 @@ Changes to Python's build process and to the C API include: -* The C functions that access the Unicode Database now accept and - return characters from the full Unicode range, even on narrow unicode builds +* The C functions that access the Unicode Database now accept and return + characters from the full Unicode range, even on narrow unicode builds (Py_UNICODE_TOLOWER, Py_UNICODE_ISDECIMAL, and others). A visible difference - in Python is that :cfunc:`unicodedata.numeric` now returns the correct value for - large code points, and :func:`repr` may consider more characters as printable. + in Python is that :func:`unicodedata.numeric` now returns the correct value + for large code points, and :func:`repr` may consider more characters as + printable. (Reported by Bupjoe Lee and fixed by Amaury Forgeot D'Arc; :issue:`5127`.) -* Computed gotos are now enabled by default on supported - compilers (which are detected by the configure script). They can still - be disable selectively by specifying ``--without-computed-gotos``. +* Computed gotos are now enabled by default on supported compilers (which are + detected by the configure script). They can still be disable selectively by + specifying ``--without-computed-gotos``. + + (Contributed by Antoine Pitrou; :issue:`9203`.) - (:issue:`9203`) Porting to Python 3.2 ===================== -This section lists previously described changes and other bugfixes -that may require changes to your code: +This section lists previously described changes and other bugfixes that may +require changes to your code: -* bytearray objects cannot be used anymore as filenames: convert them to bytes +* :class:`bytearray` objects cannot be used anymore as filenames: convert them + to :class:`bytes`. * PyArg_Parse*() functions: @@ -457,6 +457,6 @@ * "w" and "w#" formats has been removed: use "w*" instead * The :ctype:`PyCObject` type, deprecated in 3.1, has been removed. To wrap - opaque C pointers in Python objects, the :ctype:`PyCapsule` API should be - used instead; the new type has a well defined interface for passing typing - safety information and a less complicated signature for calling a destructor. + opaque C pointers in Python objects, the :ctype:`PyCapsule` API should be used + instead; the new type has a well defined interface for passing typing safety + information and a less complicated signature for calling a destructor. From python-checkins at python.org Sun Sep 5 19:06:54 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 19:06:54 +0200 (CEST) Subject: [Python-checkins] r84529 - python/branches/py3k/Misc/NEWS Message-ID: <20100905170654.8D145EE9B8@mail.python.org> Author: georg.brandl Date: Sun Sep 5 19:06:50 2010 New Revision: 84529 Log: Rewrap. Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 5 19:06:50 2010 @@ -12,39 +12,38 @@ Core and Builtins ----------------- -- Issue #9225: Remove the ROT_FOUR and DUP_TOPX opcode, the latter replaced - by the new (and simpler) DUP_TOP_TWO. Performance isn't changed, but - our bytecode is a bit simplified. Patch by Demur Rumed. +- Issue #9225: Remove the ROT_FOUR and DUP_TOPX opcode, the latter replaced by + the new (and simpler) DUP_TOP_TWO. Performance isn't changed, but our + bytecode is a bit simplified. Patch by Demur Rumed. - Issue #9766: Rename poorly named variables exposed by _warnings to prevent confusion with the proper variables names from 'warnings' itself. -- Issue #9212: dict_keys and dict_items now provide the isdisjoint() - method, to conform to the Set ABC. Patch by Daniel Urban. +- Issue #9212: dict_keys and dict_items now provide the isdisjoint() method, to + conform to the Set ABC. Patch by Daniel Urban. -- Issue #9737: Fix a crash when trying to delete a slice or an item from - a memoryview object. +- Issue #9737: Fix a crash when trying to delete a slice or an item from a + memoryview object. -- Issue #9549: sys.setdefaultencoding() and PyUnicode_SetDefaultEncoding() - are now removed, since their effect was inexistent in 3.x (the default - encoding is hardcoded to utf-8 and cannot be changed). +- Issue #9549: sys.setdefaultencoding() and PyUnicode_SetDefaultEncoding() are + now removed, since their effect was inexistent in 3.x (the default encoding is + hardcoded to utf-8 and cannot be changed). - Issue #7415: PyUnicode_FromEncodedObject() now uses the new buffer API properly. Patch by Stefan Behnel. -- Issue #5553: The Py_LOCAL_INLINE macro now results in inlining on - most platforms. Previously, it inlined when using Microsoft - Visual C. +- Issue #5553: The Py_LOCAL_INLINE macro now results in inlining on most + platforms. Previously, it inlined only when using Microsoft Visual C. - Issue #9712: Fix tokenize on identifiers that start with non-ascii names. - Issue #9688: __basicsize__ and __itemsize__ must be accessed as Py_ssize_t. -- Issue #9684: Added a definition for SIZEOF_WCHAR_T to PC/pyconfig.h, - to match the pyconfig.h generated by configure on other systems. +- Issue #9684: Added a definition for SIZEOF_WCHAR_T to PC/pyconfig.h, to match + the pyconfig.h generated by configure on other systems. -- Issue #9666: Only catch AttributeError in hasattr(). All other exceptions - that occur during attribute lookup are now propagated to the caller. +- Issue #9666: Only catch AttributeError in hasattr(). All other exceptions that + occur during attribute lookup are now propagated to the caller. - Issue #8622: Add PYTHONFSENCODING environment variable to override the filesystem encoding. @@ -64,35 +63,34 @@ - Issue #9612: The set object is now 64-bit clean under Windows. -- Issue #8202: sys.argv[0] is now set to '-m' instead of '-c' when searching - for the module file to be executed with the -m command line option. +- Issue #8202: sys.argv[0] is now set to '-m' instead of '-c' when searching for + the module file to be executed with the -m command line option. -- Issue #9599: Create PySys_FormatStdout() and PySys_FormatStderr() functions - to write a message formatted by PyUnicode_FromFormatV() to sys.stdout and +- Issue #9599: Create PySys_FormatStdout() and PySys_FormatStderr() functions to + write a message formatted by PyUnicode_FromFormatV() to sys.stdout and sys.stderr. - Issue #9542: Create PyUnicode_FSDecoder() function, a ParseTuple converter: - decode bytes objects to unicode using PyUnicode_DecodeFSDefaultAndSize(); - str objects are output as-is. + decode bytes objects to unicode using PyUnicode_DecodeFSDefaultAndSize(); str + objects are output as-is. -- Issue #9203: Computed gotos are now enabled by default on supported - compilers (which are detected by the configure script). They can still - be disable selectively by specifying --without-computed-gotos. +- Issue #9203: Computed gotos are now enabled by default on supported compilers + (which are detected by the configure script). They can still be disable + selectively by specifying --without-computed-gotos. -- Issue #9425: Create PyErr_WarnFormat() function, similar to PyErr_WarnEx() - but use PyUnicode_FromFormatV() to format the warning message. +- Issue #9425: Create PyErr_WarnFormat() function, similar to PyErr_WarnEx() but + use PyUnicode_FromFormatV() to format the warning message. -- Issue #8530: Prevent stringlib fastsearch from reading beyond the front - of an array. +- Issue #8530: Prevent stringlib fastsearch from reading beyond the front of an + array. -- Issue #5319: Print an error if flushing stdout fails at interpreter - shutdown. +- Issue #5319: Print an error if flushing stdout fails at interpreter shutdown. -- Issue #9337: The str() of a float or complex number is now identical - to its repr(). +- Issue #9337: The str() of a float or complex number is now identical to its + repr(). -- Issue #9416: Fix some issues with complex formatting where the - output with no type specifier failed to match the str output: +- Issue #9416: Fix some issues with complex formatting where the output with no + type specifier failed to match the str output: - format(complex(-0.0, 2.0), '-') omitted the real part from the output, - format(complex(0.0, 2.0), '-') included a sign and parentheses. @@ -103,103 +101,100 @@ - Issue #8734: Avoid crash in msvcrt.get_osfhandle() when an invalid file descriptor is provided. Patch by Pascal Chambon. -- Issue #7736: Release the GIL around calls to opendir() and closedir() - in the posix module. Patch by Marcin Bachry. +- Issue #7736: Release the GIL around calls to opendir() and closedir() in the + posix module. Patch by Marcin Bachry. -- Issue #4835: make PyLong_FromSocket_t() and PyLong_AsSocket_t() private - to the socket module, and fix the width of socket descriptors to be - correctly detected under 64-bit Windows. - -- Issue #1027206: Support IDNA in gethostbyname, gethostbyname_ex, - getaddrinfo and gethostbyaddr. getnameinfo is now restricted to numeric - addresses as input. +- Issue #4835: make PyLong_FromSocket_t() and PyLong_AsSocket_t() private to the + socket module, and fix the width of socket descriptors to be correctly + detected under 64-bit Windows. + +- Issue #1027206: Support IDNA in gethostbyname, gethostbyname_ex, getaddrinfo + and gethostbyaddr. getnameinfo is now restricted to numeric addresses as + input. -- Issue #9214: Set operations on a KeysView or ItemsView in collections - now correctly return a set. (Patch by Eli Bendersky.) +- Issue #9214: Set operations on a KeysView or ItemsView in collections now + correctly return a set. Patch by Eli Bendersky. - Issue #5737: Add Solaris-specific mnemonics in the errno module. Patch by Matthew Ahrens. -- Restore GIL in nis_cat in case of error. Decode NIS data to fs encoding, - using the surrogate error handler. +- Restore GIL in nis_cat in case of error. Decode NIS data to fs encoding, using + the surrogate error handler. -- Issue #665761: ``functools.reduce()`` will no longer mask exceptions - other than ``TypeError`` raised by the iterator argument. +- Issue #665761: ``functools.reduce()`` will no longer mask exceptions other + than ``TypeError`` raised by the iterator argument. - Issue #9570: Use PEP 383 decoding in os.mknod and os.mkfifo. -- Issue #6915: Under Windows, os.listdir() didn't release the Global - Interpreter Lock around all system calls. Original patch by Ryan Kelly. +- Issue #6915: Under Windows, os.listdir() didn't release the Global Interpreter + Lock around all system calls. Original patch by Ryan Kelly. -- Issue #8524: Add a detach() method to socket objects, so as to put the - socket into the closed state without closing the underlying file - descriptor. +- Issue #8524: Add a detach() method to socket objects, so as to put the socket + into the closed state without closing the underlying file descriptor. - Issue #477863: Print a warning at shutdown if gc.garbage is not empty. - Issue #6869: Fix a refcount problem in the _ctypes extension. -- Issue #5504: ctypes should now work with systems where mmap can't - be PROT_WRITE and PROT_EXEC. +- Issue #5504: ctypes should now work with systems where mmap can't be + PROT_WRITE and PROT_EXEC. -- Issue #9507: Named tuple repr will now automatically display the right - name in a tuple subclass. +- Issue #9507: Named tuple repr will now automatically display the right name in + a tuple subclass. -- Issue #9324: Add parameter validation to signal.signal on Windows in order - to prevent crashes. +- Issue #9324: Add parameter validation to signal.signal on Windows in order to + prevent crashes. -- Issue #9526: Remove some outdated (int) casts that were preventing - the array module from working correctly with arrays of more than - 2**31 elements. +- Issue #9526: Remove some outdated (int) casts that were preventing the array + module from working correctly with arrays of more than 2**31 elements. - Fix memory leak in ssl._ssl._test_decode_cert. -- Issue #8065: Fix memory leak in readline module (from failure to - free the result of history_get_history_state()). +- Issue #8065: Fix memory leak in readline module (from failure to free the + result of history_get_history_state()). - Issue #9450: Fix memory leak in readline.replace_history_item and readline.remove_history_item for readline version >= 5.0. - Issue #8105: Validate file descriptor passed to mmap.mmap on Windows. -- Issue #8046: Add context manager protocol support and .closed property - to mmap objects. +- Issue #8046: Add context manager protocol support and .closed property to mmap + objects. Library ------- -- Issue #7451: Improve decoding performance of JSON objects, and reduce - the memory consumption of said decoded objects when they use the same - strings as keys. +- Issue #7451: Improve decoding performance of JSON objects, and reduce the + memory consumption of said decoded objects when they use the same strings as + keys. -- Issue #1100562: Fix deep-copying of objects derived from the list and - dict types. Patch by Michele Orr? and Bj?rn Lindqvist. +- Issue #1100562: Fix deep-copying of objects derived from the list and dict + types. Patch by Michele Orr? and Bj?rn Lindqvist. -- Issue #9753: Fixed socket.dup, which did not always work correctly - on Windows. +- Issue #9753: Fixed socket.dup, which did not always work correctly on Windows. -- Issue #9421: Made the get methods consistently accept the vars - and default arguments on all parser classes. +- Issue #9421: Made the get methods consistently accept the vars and + default arguments on all parser classes. - Issue #7005: Fixed output of None values for RawConfigParser.write and ConfigParser.write. - Issue #8990: array.fromstring() and array.tostring() get renamed to frombytes() and tobytes(), respectively, to avoid confusion. Furthermore, - array.frombytes(), array.extend() as well as the array.array() - constructor now accept bytearray objects. Patch by Thomas Jollans. + array.frombytes(), array.extend() as well as the array.array() constructor now + accept bytearray objects. Patch by Thomas Jollans. -- Issue #808164: Fixed socket.close to avoid references to globals, to - avoid issues when socket.close is called from a __del__ method. +- Issue #808164: Fixed socket.close to avoid references to globals, to avoid + issues when socket.close is called from a __del__ method. -- Issue #9706: ssl module provides a better error handling in various +- Issue #9706: ssl module provides a better error handling in various circumstances. -- Issue #1868: Eliminate subtle timing issues in thread-local objects by - getting rid of the cached copy of thread-local attribute dictionary. +- Issue #1868: Eliminate subtle timing issues in thread-local objects by getting + rid of the cached copy of thread-local attribute dictionary. -- Issue #1512791: In setframerate() in the wave module, non-integral - frame rates are rounded to the nearest integer. +- Issue #1512791: In setframerate() in the wave module, non-integral frame rates + are rounded to the nearest integer. - Issue #8797: urllib2 does a retry for Basic Authentication failure instead of falling into recursion. @@ -210,54 +205,53 @@ - Issue #8750: Fixed MutableSet's methods to correctly handle reflexive operations on its self, namely x -= x and x ^= x. -- Issue #9129: smtpd.py is vulnerable to DoS attacks deriving from missing - error handling when accepting a new connection. +- Issue #9129: smtpd.py is vulnerable to DoS attacks deriving from missing error + handling when accepting a new connection. - Issue #9601: ftplib now provides a workaround for non-compliant - implementations such as IIS shipped with Windows server 2003 returning - invalid response codes for MKD and PWD commands. + implementations such as IIS shipped with Windows server 2003 returning invalid + response codes for MKD and PWD commands. - Issue #658749: asyncore's connect() method now correctly interprets winsock errors. - Issue #9501: Fixed logging regressions in cleanup code. -- Fix functools.total_ordering() to skip methods inherited from object(). +- Fix functools.total_ordering() to skip methods inherited from object. -- Issue #9572: Importlib should not raise an exception if a directory it - thought it needed to create was done concurrently by another process. +- Issue #9572: Importlib should not raise an exception if a directory it thought + it needed to create was done concurrently by another process. - Issue #9617: Signals received during a low-level write operation aren't ignored by the buffered IO layer anymore. - Issue #843590: Make "macintosh" an alias to the "mac_roman" encoding. -- Create os.fsdecode(): decode from the filesystem encoding with - surrogateescape error handler, or strict error handler on Windows. +- Create os.fsdecode(): decode from the filesystem encoding with surrogateescape + error handler, or strict error handler on Windows. -- Issue #3488: Provide convenient shorthand functions ``gzip.compress`` - and ``gzip.decompress``. Original patch by Anand B. Pillai. +- Issue #3488: Provide convenient shorthand functions ``gzip.compress`` and + ``gzip.decompress``. Original patch by Anand B. Pillai. -- Issue #8807: poplib.POP3_SSL class now accepts a context parameter, which is - a ssl.SSLContext object allowing bundling SSL configuration options, +- Issue #8807: poplib.POP3_SSL class now accepts a context parameter, which is a + ssl.SSLContext object allowing bundling SSL configuration options, certificates and private keys into a single (potentially long-lived) structure. - Issue #8866: parameters passed to socket.getaddrinfo can now be specified as single keyword arguments. -- Address XXX comment in dis.py by having inspect.py prefer to reuse the - dis.py compiler flag values over defining its own +- Address XXX comment in dis.py by having inspect.py prefer to reuse the dis.py + compiler flag values over defining its own. -- Issue #9147: Added dis.code_info() which is similar to show_code() - but returns formatted code information in a string rather than - displaying on screen. +- Issue #9147: Added dis.code_info() which is similar to show_code() but returns + formatted code information in a string rather than displaying on screen. - Issue #9567: functools.update_wrapper now adds a __wrapped__ attribute - pointing to the original callable + pointing to the original callable. -- Issue #3445: functools.update_wrapper now tolerates missing attributes - on wrapped callables +- Issue #3445: functools.update_wrapper now tolerates missing attributes on + wrapped callables. - Issue #5867: Add abc.abstractclassmethod and abc.abstractstaticmethod. @@ -268,79 +262,79 @@ encoding and surrogateescape error handler. Patch written by David Watson. - Issue #9603: posix.ttyname() and posix.ctermid() decode the terminal name - using the filesystem encoding and surrogateescape error handler. Patch - written by David Watson. + using the filesystem encoding and surrogateescape error handler. Patch written + by David Watson. -- Issue #7647: The posix module now has the ST_RDONLY and ST_NOSUID - constants, for use with the statvfs() function. Patch by Adam Jackson. +- Issue #7647: The posix module now has the ST_RDONLY and ST_NOSUID constants, + for use with the statvfs() function. Patch by Adam Jackson. -- Issue #8688: MANIFEST files created by distutils now include a magic - comment indicating they are generated. Manually maintained MANIFESTs - without this marker will not be overwritten or removed. - -- Issue #7467: when reading a file from a ZIP archive, its CRC is checked - and a BadZipfile error is raised if it doesn't match (as used to be the - case in Python 2.5 and earlier). - -- Issue #9550: a BufferedReader could issue an additional read when the - original read request had been satisfied, which could block indefinitely - when the underlying raw IO channel was e.g. a socket. Report and original - patch by Jason V. Miller. +- Issue #8688: MANIFEST files created by distutils now include a magic comment + indicating they are generated. Manually maintained MANIFESTs without this + marker will not be overwritten or removed. + +- Issue #7467: when reading a file from a ZIP archive, its CRC is checked and a + BadZipfile error is raised if it doesn't match (as used to be the case in + Python 2.5 and earlier). + +- Issue #9550: a BufferedReader could issue an additional read when the original + read request had been satisfied, which could block indefinitely when the + underlying raw IO channel was e.g. a socket. Report and original patch by + Jason V. Miller. - Issue #3757: thread-local objects now support cyclic garbage collection. - Thread-local objects involved in reference cycles will be deallocated - timely by the cyclic GC, even if the underlying thread is still running. + Thread-local objects involved in reference cycles will be deallocated timely + by the cyclic GC, even if the underlying thread is still running. - Issue #9452: Add read_file, read_string, and read_dict to the configparser API; new source attribute to exceptions. -- Issue #6231: Fix xml.etree.ElementInclude to include the tail of the - current node. +- Issue #6231: Fix xml.etree.ElementInclude to include the tail of the current + node. - Issue #8047: Fix the xml.etree serializer to return bytes by default. Use ``encoding="unicode"`` to generate a Unicode string. -- Issue #8280: urllib2's Request method will remove fragments in the url. - This is how it is supposed to work, wget and curl do the same. Previous - behavior was wrong. +- Issue #8280: urllib2's Request method will remove fragments in the url. This + is how it is supposed to work, wget and curl do the same. Previous behavior + was wrong. - Issue #6683: For SMTP logins we now try all authentication methods advertised by the server. Many servers are buggy and advertise authentication methods they do not support in reality. -- Issue #8814: function annotations (the ``__annotations__`` attribute) - are now included in the set of attributes copied by default by - functools.wraps and functools.update_wrapper. Patch by Terrence Cole. +- Issue #8814: function annotations (the ``__annotations__`` attribute) are now + included in the set of attributes copied by default by functools.wraps and + functools.update_wrapper. Patch by Terrence Cole. - Issue #2944: asyncore doesn't handle connection refused correctly. -- Issue #4184: Private attributes on smtpd.SMTPChannel made public and - deprecate the private attributes. Add tests for smtpd module. +- Issue #4184: Private attributes on smtpd.SMTPChannel made public and deprecate + the private attributes. Add tests for smtpd module. -- Issue #3196: email header decoding is now forgiving if an RFC2047 - encoded word encoded in base64 is lacking padding. +- Issue #3196: email header decoding is now forgiving if an RFC2047 encoded word + encoded in base64 is lacking padding. -- Issue #9444: Argparse now uses the first element of prefix_chars as - the option character for the added 'h/help' option if prefix_chars - does not contain a '-', instead of raising an error. +- Issue #9444: Argparse now uses the first element of prefix_chars as the option + character for the added 'h/help' option if prefix_chars does not contain a + '-', instead of raising an error. -- Issue #7372: Fix pstats regression when stripping paths from profile - data generated with the profile module. +- Issue #7372: Fix pstats regression when stripping paths from profile data + generated with the profile module. -- Issue #9428: Fix running scripts with the profile/cProfile modules from - the command line. +- Issue #9428: Fix running scripts with the profile/cProfile modules from the + command line. -- Issue #7781: Fix restricting stats by entry counts in the pstats - interactive browser. +- Issue #7781: Fix restricting stats by entry counts in the pstats interactive + browser. -- Issue #9209: Do not crash in the pstats interactive browser on invalid - regular expressions. +- Issue #9209: Do not crash in the pstats interactive browser on invalid regular + expressions. -- Update collections.OrderedDict to match the implementation in Py2.7 - (based on lists instead of weakly referenced Link objects). +- Update collections.OrderedDict to match the implementation in Py2.7 (based on + lists instead of weakly referenced Link objects). -- Issue #8397: Raise an error when attempting to mix iteration and regular - reads on a BZ2File object, rather than returning incorrect results. +- Issue #8397: Raise an error when attempting to mix iteration and regular reads + on a BZ2File object, rather than returning incorrect results. - Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its ``__init__`` method. @@ -356,19 +350,19 @@ - Issue #8230: Fix Lib/test/sortperf.py. -- Issue #8620: when a Cmd is fed input that reaches EOF without a final - newline, it no longer truncates the last character of the last command line. +- Issue #8620: when a Cmd is fed input that reaches EOF without a final newline, + it no longer truncates the last character of the last command line. - Issue #5146: Handle UID THREAD command correctly in imaplib. - Issue #5147: Fix the header generated for cookie files written by http.cookiejar.MozillaCookieJar. -- Issue #8198: In pydoc, output all help text to the correct stream - when sys.stdout is reassigned. +- Issue #8198: In pydoc, output all help text to the correct stream when + sys.stdout is reassigned. -- Issue #7909: Do not touch paths with the special prefixes ``\\.\`` - or ``\\?\`` in ntpath.normpath(). +- Issue #7909: Do not touch paths with the special prefixes ``\\.\`` or ``\\?\`` + in ntpath.normpath(). - Issue #1286: Allow using fileinput.FileInput as a context manager. @@ -405,9 +399,8 @@ - Issue #9193: PEP 3149 is accepted. -- Issue #3101: Helper functions _add_one_to_index_C() and - _add_one_to_index_F() become _Py_add_one_to_index_C() and - _Py_add_one_to_index_F(), respectively. +- Issue #3101: Helper functions _add_one_to_index_C() and _add_one_to_index_F() + become _Py_add_one_to_index_C() and _Py_add_one_to_index_F(), respectively. - Issue #9700: define HAVE_BROKEN_POSIX_SEMAPHORES under AIX 6.x. Patch by S?bastien Sabl?. From python-checkins at python.org Sun Sep 5 19:07:12 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 19:07:12 +0200 (CEST) Subject: [Python-checkins] r84530 - in python/branches/py3k: Doc/library/os.rst Modules/posixmodule.c Message-ID: <20100905170712.B8FAEEE9B8@mail.python.org> Author: georg.brandl Date: Sun Sep 5 19:07:12 2010 New Revision: 84530 Log: #9747: fix copy-paste error in getresgid() doc. Modified: python/branches/py3k/Doc/library/os.rst python/branches/py3k/Modules/posixmodule.c Modified: python/branches/py3k/Doc/library/os.rst ============================================================================== --- python/branches/py3k/Doc/library/os.rst (original) +++ python/branches/py3k/Doc/library/os.rst Sun Sep 5 19:07:12 2010 @@ -299,7 +299,7 @@ .. function:: getresgid() Return a tuple (rgid, egid, sgid) denoting the current process's - real, effective, and saved user ids. + real, effective, and saved group ids. Availability: Unix. Modified: python/branches/py3k/Modules/posixmodule.c ============================================================================== --- python/branches/py3k/Modules/posixmodule.c (original) +++ python/branches/py3k/Modules/posixmodule.c Sun Sep 5 19:07:12 2010 @@ -7600,7 +7600,7 @@ #ifdef HAVE_GETRESGID PyDoc_STRVAR(posix_getresgid__doc__, "getresgid() -> (rgid, egid, sgid)\n\n\ -Get tuple of the current process's real, effective, and saved user ids."); +Get tuple of the current process's real, effective, and saved group ids."); static PyObject* posix_getresgid (PyObject *self, PyObject *noargs) From python-checkins at python.org Sun Sep 5 19:09:18 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 19:09:18 +0200 (CEST) Subject: [Python-checkins] r84531 - python/branches/py3k/Modules/fcntlmodule.c Message-ID: <20100905170918.4DE91EE9B8@mail.python.org> Author: georg.brandl Date: Sun Sep 5 19:09:18 2010 New Revision: 84531 Log: #9776: fix some spacing. Modified: python/branches/py3k/Modules/fcntlmodule.c Modified: python/branches/py3k/Modules/fcntlmodule.c ============================================================================== --- python/branches/py3k/Modules/fcntlmodule.c (original) +++ python/branches/py3k/Modules/fcntlmodule.c Sun Sep 5 19:09:18 2010 @@ -82,10 +82,10 @@ Perform the requested operation on file descriptor fd. The operation\n\ is defined by op and is operating system dependent. These constants are\n\ available from the fcntl module. The argument arg is optional, and\n\ -defaults to 0; it may be an int or a string. If arg is given as a string,\n\ +defaults to 0; it may be an int or a string. If arg is given as a string,\n\ the return value of fcntl is a string of that length, containing the\n\ -resulting value put in the arg buffer by the operating system.The length\n\ -of the arg string is not allowed to exceed 1024 bytes. If the arg given\n\ +resulting value put in the arg buffer by the operating system. The length\n\ +of the arg string is not allowed to exceed 1024 bytes. If the arg given\n\ is an integer or if none is specified, the result value is an integer\n\ corresponding to the return value of the fcntl call in the C code."); From python-checkins at python.org Sun Sep 5 19:32:25 2010 From: python-checkins at python.org (eric.araujo) Date: Sun, 5 Sep 2010 19:32:25 +0200 (CEST) Subject: [Python-checkins] r84532 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100905173225.46FC1EE9AE@mail.python.org> Author: eric.araujo Date: Sun Sep 5 19:32:25 2010 New Revision: 84532 Log: Fix typos and wording in what?s new 3.2. - The entry about shutil.copytree is just a revert of r84524 which looks like an unfinished edition. - The use of gender-neutral language (s/his/their/) removes the implicit assumption that programmer == male (change agreed by Antoine). - Other changes should be uncontroversial fixes. I haven?t rewrapped under 80 lines to keep the diffs readable; I?ll rewrap later. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 5 19:32:25 2010 @@ -39,9 +39,10 @@ sufficient; the e-mail address isn't necessary. It's helpful to add the issue number: - XXX Describe the transmogrify() function added to the socket - module. - (Contributed by P.Y. Developer; :issue:`12345`.) + XXX Describe the transmogrify() function added to the socket + module. + + (Contributed by P.Y. Developer; :issue:`12345`.) This saves the maintainer the effort of going through the SVN log when researching a change. @@ -49,24 +50,24 @@ This article explains the new features in Python 3.2, compared to 3.1. -PEP 391: Dictionary Based Configuration for Logging -=================================================== +PEP 391: Dictionary Based Configuration for Logging +==================================================== -The :mod:`logging` module had two ways of configuring the module, either calling -functions for each option or by reading an external file saved in a ConfigParser +The :mod:`logging` module had two ways of configuring the module, either by calling +functions for each option or by reading an external file saved in a :mod:`ConfigParser` format. Those options did not provide the flexibility to create configurations -from JSON or YAML files and they did not support incremental configuration which +from JSON or YAML files and they did not support incremental configuration, which is needed for specifying logger options from a command line. To support a more flexible style, the module now offers :func:`logging.config.dictConfig` to use dictionaries to specify logger -configurations (including formatters, handlers, filters, and loggers). For +configuration (including formatters, handlers, filters, and loggers). For example: >>> import logging.config >>> logging.config.dictConfig(json.load(open('log.cfg', 'rb'))) -The above fragment configures logging from a JSON encoded dictionary stored in +The above fragment configures logging from a JSON-encoded dictionary stored in a file called "log.cfg". Here's a working example of a configuration dictionary:: {"version": 1, @@ -101,14 +102,14 @@ overwrite the cached file, thus losing the benefits of caching. The issue of "pyc fights" has become more pronounced as it has become -common-place for Linux distributions to ship with multiple versions of Python. +commonplace for Linux distributions to ship with multiple versions of Python. These conflicts also arise with CPython alternatives such as Unladen Swallow. To solve this problem, Python's import machinery has been extended to use -distinct filenames for each interpreter. Instead of Python3.2 and Python3.3 and -UnladenSwallow each competing for a file called "mymodule.pyc", they will now +distinct filenames for each interpreter. Instead of Python 3.2 and Python 3.3 and +Unladen Swallow each competing for a file called "mymodule.pyc", they will now look for "mymodule.cpython-32.pyc", "mymodule.cpython-33.pyc", and -"mymodule.unladen10.pyc". And to keep prevent all of these new files from +"mymodule.unladen10.pyc". And to prevent all of these new files from cluttering source directories, the *pyc* files are now collected in a "__pycache__" directory stored under the package directory. @@ -157,7 +158,7 @@ The common directory is "pyshared" and the file names are made distinct by identifying the Python implementation (such as CPython, PyPy, Jython, etc.), the major and minor version numbers, and optional build flags (such as "d" for -debug, "m" for pymalloc, "u" for wide-unicode). For an arbtrary package, "foo", +debug, "m" for pymalloc, "u" for wide-unicode). For an arbitrary package "foo", you may see these files when the distribution package is installed:: /usr/share/pyshared/foo.cpython-32m.so @@ -187,15 +188,15 @@ it only catches :exc:`AttributeError`. Under the hood, :func:`hasattr` works by calling :func:`getattr` and throwing away the results. This is necessary because dynamic attribute creation is possible using :meth:`__getattribute__` - or :meth:`__getattr`. If :func:`hasattr` were to just scan instance and class + or :meth:`__getattr__`. If :func:`hasattr` were to just scan instance and class dictionaries it would miss the dynmaic methods and make it difficult to implement proxy objects. (Discovered by Yury Selivanov and fixed by Benjamin Peterson; :issue:`9666`.) -* The :func:`str` of a float or complex number is now the same as it +* The :func:`str` of a float or complex number is now the same as its :func:`repr`. Previously, the :func:`str` form was shorter but that just - caused confusion and is no longer needed now that we the shortest possible + caused confusion and is no longer needed now that the shortest possible :func:`repr` is displayed by default: >>> repr(math.pi) @@ -223,7 +224,7 @@ New, Improved, and Deprecated Modules ===================================== -* The :mod:`functools` module now includes a new decorator for caching function +* The :mod:`functools` module includes a new decorator for caching function calls. :func:`functools.lru_cache` can save repeated queries to an external resource whenever the results are expected to be the same. @@ -261,7 +262,7 @@ `appspot issue 53094 `_.) * The :class:`ftplib.FTP` class now supports the context manager protocol to - unconditionally consume :exc:`socket.error` exceptions and to close the ftp + unconditionally consume :exc:`socket.error` exceptions and to close the FTP connection when done: >>> from ftplib import FTP @@ -279,7 +280,7 @@ * A warning message will now get printed at interpreter shutdown if the :data:`gc.garbage` list isn't empty. This is meant to make the programmer - aware that his code contains object finalization issues. + aware that their code contains object finalization issues. (Added by Antoine Pitrou; :issue:`477863`.) @@ -290,11 +291,11 @@ * The :func:`shutil.copytree` function has two new options: - * *ignore_dangling_symlinks*: when ``symlinks=False`` dp that the function - copies the file pointed to by the symlink, not the symlink itself. This + * *ignore_dangling_symlinks*: when ``symlinks=False`` (meaning that the function + copies the file pointed to by the symlink, not the symlink itself), this option will silence the error raised if the file doesn't exist. - * *copy_function*: is a callable that will be used to copy files. + * *copy_function*: a callable that will be used to copy files. :func:`shutil.copy2` is used by default. (Contributed by Tarek Ziad?.) @@ -411,8 +412,8 @@ available (ignored) on Windows and Mac OS X: the filesystem encoding is pinned to ``'mbcs'`` on Windows and ``'utf-8'`` on Mac OS X. -The :mod:`os` module has two new functions: :func:`os.fsencode` and -:func:`os.fsdecode`. +The :mod:`os` module has two new functions: :func:`~os.fsencode` and +:func:`~os.fsdecode`. .. IDLE @@ -458,5 +459,5 @@ * The :ctype:`PyCObject` type, deprecated in 3.1, has been removed. To wrap opaque C pointers in Python objects, the :ctype:`PyCapsule` API should be used - instead; the new type has a well defined interface for passing typing safety + instead; the new type has a well-defined interface for passing typing safety information and a less complicated signature for calling a destructor. From python-checkins at python.org Sun Sep 5 19:32:31 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 19:32:31 +0200 (CEST) Subject: [Python-checkins] r84533 - in python/branches/py3k/Misc: NEWS NEWS.help Message-ID: <20100905173231.9C478EE9EC@mail.python.org> Author: georg.brandl Date: Sun Sep 5 19:32:31 2010 New Revision: 84533 Log: Fix reST in NEWS, and remove NEWS.help (all committers should now know reST anyway, and for those who do not, there is Documenting Python.) Removed: python/branches/py3k/Misc/NEWS.help Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 5 19:32:31 2010 @@ -2,8 +2,6 @@ Python News +++++++++++ -(editors: check NEWS.help for information about editing NEWS using ReST.) - What's New in Python 3.2 Alpha 2? ================================= @@ -802,8 +800,8 @@ - Issue #7072: isspace(0xa0) is true on Mac OS X. - Issue #8084: PEP 370 now conforms to system conventions for framework builds - on MacOS X. That is, "python setup.py install --user" will install - into "~/Library/Python/2.7" instead of "~/.local". + on MacOS X. That is, "python setup.py install --user" will install into + "~/Library/Python/2.7" instead of "~/.local". C-API ----- @@ -916,7 +914,7 @@ - Issue #4179: In pdb, allow "list ." as a command to return to the currently debugged line. -- Issue #4108: In urllib.robotparser, if there are multiple 'User-agent: *' +- Issue #4108: In urllib.robotparser, if there are multiple ``User-agent: *`` entries, consider the first one. - Issue #6630: Allow customizing regex flags when subclassing the @@ -2051,7 +2049,7 @@ - Issue #7347: winreg: Add CreateKeyEx and DeleteKeyEx, as well as fix a bug in the return value of QueryReflectionKey. -- Issue #7567: PyCurses_setupterm: Don't call `setupterm' twice. +- Issue #7567: PyCurses_setupterm: Don't call ``setupterm`` twice. Build ----- @@ -2138,7 +2136,7 @@ - Issue #8510: Update to autoconf2.65. Documentation ------------- +------------- - Issue #9524: Document that two CTRL* signals are meant for use only with os.kill. @@ -2681,7 +2679,7 @@ - Issue #2170: refactored xml.dom.minidom.normalize, increasing both its clarity and its speed. -- Issue #1113244: Py_XINCREF, Py_DECREF, Py_XDECREF: Add `do { ... } while (0)' +- Issue #1113244: Py_XINCREF, Py_DECREF, Py_XDECREF: Add ``do { ... } while (0)`` to avoid compiler warnings. - Issue #3739: The unicode-internal encoder now reports the number of characters @@ -3005,7 +3003,7 @@ optional module state data. - Issue #4910: Rename nb_long slot to nb_reserved, and change its - type to (void *). + type to ``(void *)``. - Issue #4935: The overflow checking code in the expandtabs() method common to str, bytes and bytearray could be optimized away by the compiler, letting @@ -3060,8 +3058,8 @@ - Issue #4747: When the terminal does not use utf-8, executing a script with non-ascii characters in its name could fail with a "SyntaxError: None" error. -- Issue #4797: IOError.filename was not set when _fileio.FileIO failed to open - file with `bytes' filename on Windows. +- Issue #4797: IOError.filename was not set when ``_fileio.FileIO`` failed + to open file with a bytes filename on Windows. - Issue #3680: Reference cycles created through a dict, set or deque iterator did not get collected. @@ -3312,7 +3310,7 @@ - Issue #5386: mmap.write_byte didn't check map size, so it could cause buffer overrun. -- Issue #1533164: Installed but not listed *.pyo was breaking Distutils +- Issue #1533164: Installed but not listed ``*.pyo`` was breaking Distutils bdist_rpm command. - Issue #5378: added --quiet option to Distutils bdist_rpm command. @@ -3465,8 +3463,8 @@ - Issue #4959: inspect.formatargspec now works for keyword only arguments without defaults. -- Issue #3321: _multiprocessing.Connection() doesn't check handle; added checks - for *nix machines for negative handles and large int handles. Without this check +- Issue #3321: ``_multiprocessing.Connection()`` doesn't check handle; added checks + for Unix machines for negative handles and large int handles. Without this check it is possible to segfault the interpreter. - Issue #4449: AssertionError in mp_benchmarks.py, caused by an underlying issue Deleted: python/branches/py3k/Misc/NEWS.help ============================================================================== --- python/branches/py3k/Misc/NEWS.help Sun Sep 5 19:32:31 2010 +++ (empty file) @@ -1,73 +0,0 @@ - -*- text -*- - -If you edited Misc/NEWS before it was converted to ReST format skimming this -file should help make the transition a bit easier. For full details about -Docutils and ReST, go to the Docutils website: - - http://docutils.sourceforge.net/ - -To process Misc/NEWS using Docutils, you'll need the latest docutils -snapshot: - - http://docutils.sf.net/docutils-snapshot.tgz - -Docutils works with Python 2.2 or newer. - -To process NEWS into NEWS.html, first install Docutils, and then run -this command: - - python .../docutils/tools/rst2html.py NEWS NEWS.html - -Here ".../docutils" is the directory into which the above snapshot was -extracted. (I hope this recipe will change for the better.) - -David Goodger made a change to the allowable structure of internal -references which greatly simplified initial conversion of the file. - -The changes required fell into the following categories: - -* The top-level "What's New" section headers changed to: - - What's New in Python 2.3 alpha 1? - ================================= - - *Release date: DD-MMM-2002* - - Note that the release date line is emphasized, with a "*" at each - end. - -* Subsections are underlined with a single row of hyphens: - - Type/class unification and new-style classes - -------------------------------------------- - -* Places where "balanced" single quotes were used were changed to use - apostrophes as both the opening and closing quote (`string' -> 'string'). - -* In a few places asterisks needed to be escaped which would otherwise have - been interpreted as beginning blocks of italic or bold text, e.g.: - - - The type of tp_free has been changed from "``void (*)(PyObject *)``" - to "``void (*)(void *)``". - - Note that only the asterisks preceded by whitespace needed to be escaped. - -* One instance of a word ending with an underscore needed to be quoted - ("PyCmp_" became "``PyCmp_``"). - -* One table was converted to ReST form (search Misc/NEWS for "New codecs" - for this example). - -* A few places where chunks of code or indented text were displayed needed - to be properly introduced (preceding paragraph terminated by "::" and the - chunk of code or text indented w.r.t. the paragraph). For example: - - - Note that PyLong_AsDouble can fail! This has always been true, - but no callers checked for it. It's more likely to fail now, - because overflow errors are properly detected now. The proper way - to check:: - - double x = PyLong_AsDouble(some_long_object); - if (x == -1.0 && PyErr_Occurred()) { - /* The conversion failed. */ - } From python-checkins at python.org Sun Sep 5 20:21:47 2010 From: python-checkins at python.org (eric.araujo) Date: Sun, 5 Sep 2010 20:21:47 +0200 (CEST) Subject: [Python-checkins] r84534 - python/branches/py3k/Misc/maintainers.rst Message-ID: <20100905182147.211BAEE9CB@mail.python.org> Author: eric.araujo Date: Sun Sep 5 20:21:46 2010 New Revision: 84534 Log: Fix amk?s tracker name Modified: python/branches/py3k/Misc/maintainers.rst Modified: python/branches/py3k/Misc/maintainers.rst ============================================================================== --- python/branches/py3k/Misc/maintainers.rst (original) +++ python/branches/py3k/Misc/maintainers.rst Sun Sep 5 20:21:46 2010 @@ -83,7 +83,7 @@ crypt csv ctypes theller -curses andrew.kuchling +curses akuchling datetime alexander.belopolsky dbm decimal facundobatista, rhettinger, mark.dickinson @@ -132,7 +132,7 @@ locale loewis, lemburg logging vinay.sajip macpath -mailbox andrew.kuchling +mailbox akuchling mailcap marshal math mark.dickinson, rhettinger, stutzbach From python-checkins at python.org Sun Sep 5 20:25:59 2010 From: python-checkins at python.org (ronald.oussoren) Date: Sun, 5 Sep 2010 20:25:59 +0200 (CEST) Subject: [Python-checkins] r84535 - in python/branches/py3k: Modules/_ctypes/callbacks.c Modules/_ctypes/libffi_osx/include/ffi.h Modules/_ctypes/malloc_closure.c setup.py Message-ID: <20100905182559.BC496EE9F8@mail.python.org> Author: ronald.oussoren Date: Sun Sep 5 20:25:59 2010 New Revision: 84535 Log: Fix for issue9662, patch by ?ukasz Langa in issue5504. Modified: python/branches/py3k/Modules/_ctypes/callbacks.c python/branches/py3k/Modules/_ctypes/libffi_osx/include/ffi.h python/branches/py3k/Modules/_ctypes/malloc_closure.c python/branches/py3k/setup.py Modified: python/branches/py3k/Modules/_ctypes/callbacks.c ============================================================================== --- python/branches/py3k/Modules/_ctypes/callbacks.c (original) +++ python/branches/py3k/Modules/_ctypes/callbacks.c Sun Sep 5 20:25:59 2010 @@ -416,9 +416,13 @@ "ffi_prep_cif failed with %d", result); goto error; } +#if defined(X86_DARWIN) || defined(POWERPC_DARWIN) + result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p); +#else result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn, p, p->pcl_exec); +#endif if (result != FFI_OK) { PyErr_Format(PyExc_RuntimeError, "ffi_prep_closure failed with %d", result); Modified: python/branches/py3k/Modules/_ctypes/libffi_osx/include/ffi.h ============================================================================== --- python/branches/py3k/Modules/_ctypes/libffi_osx/include/ffi.h (original) +++ python/branches/py3k/Modules/_ctypes/libffi_osx/include/ffi.h Sun Sep 5 20:25:59 2010 @@ -264,6 +264,9 @@ void (*fun)(ffi_cif*,void*,void**,void*), void* user_data); +void ffi_closure_free(void *); +void *ffi_closure_alloc (size_t size, void **code); + typedef struct ffi_raw_closure { char tramp[FFI_TRAMPOLINE_SIZE]; ffi_cif* cif; @@ -349,4 +352,4 @@ } #endif -#endif // #ifndef LIBFFI_H \ No newline at end of file +#endif // #ifndef LIBFFI_H Modified: python/branches/py3k/Modules/_ctypes/malloc_closure.c ============================================================================== --- python/branches/py3k/Modules/_ctypes/malloc_closure.c (original) +++ python/branches/py3k/Modules/_ctypes/malloc_closure.c Sun Sep 5 20:25:59 2010 @@ -106,7 +106,6 @@ return NULL; item = free_list; free_list = item->next; - *codeloc = (void *)item; + *codeloc = (void *)item; return (void *)item; } - Modified: python/branches/py3k/setup.py ============================================================================== --- python/branches/py3k/setup.py (original) +++ python/branches/py3k/setup.py Sun Sep 5 20:25:59 2010 @@ -1657,6 +1657,7 @@ depends = ['_ctypes/ctypes.h'] if sys.platform == 'darwin': + sources.append('_ctypes/malloc_closure.c') sources.append('_ctypes/darwin/dlfcn_simple.c') extra_compile_args.append('-DMACOSX') include_dirs.append('_ctypes/darwin') From python-checkins at python.org Sun Sep 5 20:28:46 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 20:28:46 +0200 (CEST) Subject: [Python-checkins] r84536 - sandbox/trunk/release/release.py Message-ID: <20100905182846.4E3DAEEA02@mail.python.org> Author: georg.brandl Date: Sun Sep 5 20:28:46 2010 New Revision: 84536 Log: Fix after changing NEWS layout. Modified: sandbox/trunk/release/release.py Modified: sandbox/trunk/release/release.py ============================================================================== --- sandbox/trunk/release/release.py (original) +++ sandbox/trunk/release/release.py Sun Sep 5 20:28:46 2010 @@ -396,13 +396,13 @@ with open('Misc/NEWS', encoding="utf-8") as fp: lines = fp.readlines() for i, line in enumerate(lines): - if line.startswith("(editors"): + if line.startswith("Python News"): start = i if line.startswith("What's"): end = i break with open('Misc/NEWS', 'w', encoding="utf-8") as fp: - fp.writelines(lines[:start+1]) + fp.writelines(lines[:start+2]) fp.write(NEWS_TEMPLATE) fp.writelines(lines[end-1:]) print("Please fill in the the name of the next version.") From python-checkins at python.org Sun Sep 5 20:43:07 2010 From: python-checkins at python.org (eric.araujo) Date: Sun, 5 Sep 2010 20:43:07 +0200 (CEST) Subject: [Python-checkins] r84537 - python/branches/py3k/Lib/test/test_set.py Message-ID: <20100905184307.9E8A5EEA03@mail.python.org> Author: eric.araujo Date: Sun Sep 5 20:43:07 2010 New Revision: 84537 Log: Make naming consistent Modified: python/branches/py3k/Lib/test/test_set.py Modified: python/branches/py3k/Lib/test/test_set.py ============================================================================== --- python/branches/py3k/Lib/test/test_set.py (original) +++ python/branches/py3k/Lib/test/test_set.py Sun Sep 5 20:43:07 2010 @@ -1,4 +1,4 @@ -import unittest +mport unittest from test import support import gc import weakref @@ -1677,7 +1677,7 @@ def __hash__(self): return 0 -class Test_Weird_Bugs(unittest.TestCase): +class TestWeirdBugs(unittest.TestCase): def test_8420_set_merge(self): # This used to segfault global be_bad, set2, dict2 @@ -1837,7 +1837,7 @@ TestIdentities, TestVariousIteratorArgs, TestGraphs, - Test_Weird_Bugs, + TestWeirdBugs, ) support.run_unittest(*test_classes) From python-checkins at python.org Sun Sep 5 20:48:14 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 20:48:14 +0200 (CEST) Subject: [Python-checkins] r84538 - python/tags/r32a2 Message-ID: <20100905184814.D9905EE9AC@mail.python.org> Author: georg.brandl Date: Sun Sep 5 20:48:14 2010 New Revision: 84538 Log: Tagging release 3.2a2. Added: python/tags/r32a2/ - copied from r84537, /python/branches/py3k/ From python-checkins at python.org Sun Sep 5 20:58:42 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 20:58:42 +0200 (CEST) Subject: [Python-checkins] r84539 - python/tags/r32a2 Message-ID: <20100905185842.764A2D8D8@mail.python.org> Author: georg.brandl Date: Sun Sep 5 20:58:42 2010 New Revision: 84539 Log: Remove tag. Removed: python/tags/r32a2/ From python-checkins at python.org Sun Sep 5 20:59:49 2010 From: python-checkins at python.org (eric.araujo) Date: Sun, 5 Sep 2010 20:59:49 +0200 (CEST) Subject: [Python-checkins] r84540 - python/branches/py3k/Lib/test/test_set.py Message-ID: <20100905185949.BACB4D8D8@mail.python.org> Author: eric.araujo Date: Sun Sep 5 20:59:49 2010 New Revision: 84540 Log: Fix accidental suppression in r84537 Modified: python/branches/py3k/Lib/test/test_set.py Modified: python/branches/py3k/Lib/test/test_set.py ============================================================================== --- python/branches/py3k/Lib/test/test_set.py (original) +++ python/branches/py3k/Lib/test/test_set.py Sun Sep 5 20:59:49 2010 @@ -1,4 +1,4 @@ -mport unittest +import unittest from test import support import gc import weakref From python-checkins at python.org Sun Sep 5 21:01:40 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 21:01:40 +0200 (CEST) Subject: [Python-checkins] r84541 - python/tags/r32a2 Message-ID: <20100905190140.67A89EE9AC@mail.python.org> Author: georg.brandl Date: Sun Sep 5 21:01:40 2010 New Revision: 84541 Log: Re-tag 3.2a2. Added: python/tags/r32a2/ - copied from r84540, /python/branches/py3k/ From python-checkins at python.org Sun Sep 5 23:29:17 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 5 Sep 2010 23:29:17 +0200 (CEST) Subject: [Python-checkins] r84542 - in python/branches/py3k: Include/patchlevel.h Misc/NEWS Message-ID: <20100905212917.6C60DEE9AE@mail.python.org> Author: georg.brandl Date: Sun Sep 5 23:29:17 2010 New Revision: 84542 Log: Post-release update. Modified: python/branches/py3k/Include/patchlevel.h python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Include/patchlevel.h ============================================================================== --- python/branches/py3k/Include/patchlevel.h (original) +++ python/branches/py3k/Include/patchlevel.h Sun Sep 5 23:29:17 2010 @@ -23,7 +23,7 @@ #define PY_RELEASE_SERIAL 2 /* Version as a string */ -#define PY_VERSION "3.2a2" +#define PY_VERSION "3.2a2+" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository) */ Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 5 23:29:17 2010 @@ -2,6 +2,18 @@ Python News +++++++++++ +What's New in Python 3.2 Alpha 3? +================================= + +*Release date: XX-Oct-2010* + +Core and Builtins +----------------- + +Library +------- + + What's New in Python 3.2 Alpha 2? ================================= From python-checkins at python.org Mon Sep 6 00:40:42 2010 From: python-checkins at python.org (r.david.murray) Date: Mon, 6 Sep 2010 00:40:42 +0200 (CEST) Subject: [Python-checkins] r84543 - in python/branches/release31-maint: Lib/locale.py Lib/test/test_locale.py Misc/NEWS Message-ID: <20100905224042.1E1E8EE9AC@mail.python.org> Author: r.david.murray Date: Mon Sep 6 00:40:41 2010 New Revision: 84543 Log: Merged revisions 80521 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ................ r80521 | r.david.murray | 2010-04-26 22:45:53 -0400 (Mon, 26 Apr 2010) | 13 lines Merged revisions 80512 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r80512 | r.david.murray | 2010-04-26 17:17:14 -0400 (Mon, 26 Apr 2010) | 7 lines Issue #6656: fix locale.format_string to handle escaped percents and mappings. Refactors format_string. Includes tests for the two problems noted in the issue, but as far as I can see there are no other tests that confirm that format_string conforms to normal % formatting rules. ........ ................ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/locale.py python/branches/release31-maint/Lib/test/test_locale.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/locale.py ============================================================================== --- python/branches/release31-maint/Lib/locale.py (original) +++ python/branches/release31-maint/Lib/locale.py Mon Sep 6 00:40:41 2010 @@ -224,22 +224,30 @@ percents = list(_percent_re.finditer(f)) new_f = _percent_re.sub('%s', f) - if isinstance(val, tuple): - new_val = list(val) - i = 0 - for perc in percents: - starcount = perc.group('modifiers').count('*') - new_val[i] = format(perc.group(), new_val[i], grouping, False, *new_val[i+1:i+1+starcount]) - del new_val[i+1:i+1+starcount] - i += (1 + starcount) - val = tuple(new_val) - elif isinstance(val, collections.Mapping): + if isinstance(val, collections.Mapping): + new_val = [] for perc in percents: - key = perc.group("key") - val[key] = format(perc.group(), val[key], grouping) + if perc.group()[-1]=='%': + new_val.append('%') + else: + new_val.append(format(perc.group(), val, grouping)) else: - # val is a single value - val = format(percents[0].group(), val, grouping) + if not isinstance(val, tuple): + val = (val,) + new_val = [] + i = 0 + for perc in percents: + if perc.group()[-1]=='%': + new_val.append('%') + else: + starcount = perc.group('modifiers').count('*') + new_val.append(_format(perc.group(), + val[i], + grouping, + False, + *val[i+1:i+1+starcount])) + i += (1 + starcount) + val = tuple(new_val) return new_f % val Modified: python/branches/release31-maint/Lib/test/test_locale.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_locale.py (original) +++ python/branches/release31-maint/Lib/test/test_locale.py Mon Sep 6 00:40:41 2010 @@ -236,6 +236,25 @@ self.assertRaises(ValueError, locale.format, " %f", 'foo') self.assertRaises(ValueError, locale.format, "%fg", 'foo') self.assertRaises(ValueError, locale.format, "%^g", 'foo') + self.assertRaises(ValueError, locale.format, "%f%%", 'foo') + + +class TestLocaleFormatString(unittest.TestCase): + """General tests on locale.format_string""" + + def test_percent_escape(self): + self.assertEqual(locale.format_string('%f%%', 1.0), '%f%%' % 1.0) + self.assertEqual(locale.format_string('%d %f%%d', (1, 1.0)), + '%d %f%%d' % (1, 1.0)) + self.assertEqual(locale.format_string('%(foo)s %%d', {'foo': 'bar'}), + ('%(foo)s %%d' % {'foo': 'bar'})) + + def test_mapping(self): + self.assertEqual(locale.format_string('%(foo)s bing.', {'foo': 'bar'}), + ('%(foo)s bing.' % {'foo': 'bar'})) + self.assertEqual(locale.format_string('%(foo)s', {'foo': 'bar'}), + ('%(foo)s' % {'foo': 'bar'})) + class TestNumberFormatting(BaseLocalizedTest, EnUSNumberFormatting): @@ -377,6 +396,7 @@ tests = [ TestMiscellaneous, TestFormatPatternArg, + TestLocaleFormatString, TestEnUSNumberFormatting, TestCNumberFormatting, TestFrFRNumberFormatting, Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Mon Sep 6 00:40:41 2010 @@ -105,6 +105,9 @@ Library ------- +- Issue #6656: fix locale.format_string to handle escaped percents + and mappings. + - Issue #1100562: Fix deep-copying of objects derived from the list and dict types. Patch by Michele Orr? and Bj?rn Lindqvist. From python-checkins at python.org Mon Sep 6 01:01:12 2010 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 6 Sep 2010 01:01:12 +0200 (CEST) Subject: [Python-checkins] r84544 - in python/branches/py3k: Lib/_pyio.py Lib/test/test_io.py Misc/NEWS Modules/_io/fileio.c Modules/_io/iobase.c Modules/_io/textio.c Message-ID: <20100905230112.76A4BEE9E0@mail.python.org> Author: antoine.pitrou Date: Mon Sep 6 01:01:12 2010 New Revision: 84544 Log: Issue #9293: I/O streams now raise `io.UnsupportedOperation` when an unsupported operation is attempted (for example, writing to a file open only for reading). Modified: python/branches/py3k/Lib/_pyio.py python/branches/py3k/Lib/test/test_io.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/_io/fileio.c python/branches/py3k/Modules/_io/iobase.c python/branches/py3k/Modules/_io/textio.c Modified: python/branches/py3k/Lib/_pyio.py ============================================================================== --- python/branches/py3k/Lib/_pyio.py (original) +++ python/branches/py3k/Lib/_pyio.py Mon Sep 6 01:01:12 2010 @@ -243,8 +243,13 @@ return open(*args, **kwargs) -class UnsupportedOperation(ValueError, IOError): - pass +# In normal operation, both `UnsupportedOperation`s should be bound to the +# same object. +try: + UnsupportedOperation = io.UnsupportedOperation +except AttributeError: + class UnsupportedOperation(ValueError, IOError): + pass class IOBase(metaclass=abc.ABCMeta): @@ -362,9 +367,8 @@ """Internal: raise an IOError if file is not seekable """ if not self.seekable(): - raise IOError("File or stream is not seekable." - if msg is None else msg) - + raise UnsupportedOperation("File or stream is not seekable." + if msg is None else msg) def readable(self) -> bool: """Return whether object was opened for reading. @@ -377,8 +381,8 @@ """Internal: raise an IOError if file is not readable """ if not self.readable(): - raise IOError("File or stream is not readable." - if msg is None else msg) + raise UnsupportedOperation("File or stream is not readable." + if msg is None else msg) def writable(self) -> bool: """Return whether object was opened for writing. @@ -391,8 +395,8 @@ """Internal: raise an IOError if file is not writable """ if not self.writable(): - raise IOError("File or stream is not writable." - if msg is None else msg) + raise UnsupportedOperation("File or stream is not writable." + if msg is None else msg) @property def closed(self): @@ -1647,7 +1651,7 @@ def tell(self): if not self._seekable: - raise IOError("underlying stream is not seekable") + raise UnsupportedOperation("underlying stream is not seekable") if not self._telling: raise IOError("telling position disabled by next() call") self.flush() @@ -1726,17 +1730,17 @@ if self.closed: raise ValueError("tell on closed file") if not self._seekable: - raise IOError("underlying stream is not seekable") + raise UnsupportedOperation("underlying stream is not seekable") if whence == 1: # seek relative to current position if cookie != 0: - raise IOError("can't do nonzero cur-relative seeks") + raise UnsupportedOperation("can't do nonzero cur-relative seeks") # Seeking to the current position should attempt to # sync the underlying buffer with the current position. whence = 0 cookie = self.tell() if whence == 2: # seek relative to end of file if cookie != 0: - raise IOError("can't do nonzero end-relative seeks") + raise UnsupportedOperation("can't do nonzero end-relative seeks") self.flush() position = self.buffer.seek(0, 2) self._set_decoded_chars('') Modified: python/branches/py3k/Lib/test/test_io.py ============================================================================== --- python/branches/py3k/Lib/test/test_io.py (original) +++ python/branches/py3k/Lib/test/test_io.py Mon Sep 6 01:01:12 2010 @@ -179,6 +179,23 @@ pass +class MockUnseekableIO: + def seekable(self): + return False + + def seek(self, *args): + raise self.UnsupportedOperation("not seekable") + + def tell(self, *args): + raise self.UnsupportedOperation("not seekable") + +class CMockUnseekableIO(MockUnseekableIO, io.BytesIO): + UnsupportedOperation = io.UnsupportedOperation + +class PyMockUnseekableIO(MockUnseekableIO, pyio.BytesIO): + UnsupportedOperation = pyio.UnsupportedOperation + + class MockNonBlockWriterIO: def __init__(self): @@ -304,16 +321,26 @@ def test_invalid_operations(self): # Try writing on a file opened in read mode and vice-versa. + exc = self.UnsupportedOperation for mode in ("w", "wb"): with self.open(support.TESTFN, mode) as fp: - self.assertRaises(IOError, fp.read) - self.assertRaises(IOError, fp.readline) + self.assertRaises(exc, fp.read) + self.assertRaises(exc, fp.readline) + with self.open(support.TESTFN, "wb", buffering=0) as fp: + self.assertRaises(exc, fp.read) + self.assertRaises(exc, fp.readline) + with self.open(support.TESTFN, "rb", buffering=0) as fp: + self.assertRaises(exc, fp.write, b"blah") + self.assertRaises(exc, fp.writelines, [b"blah\n"]) with self.open(support.TESTFN, "rb") as fp: - self.assertRaises(IOError, fp.write, b"blah") - self.assertRaises(IOError, fp.writelines, [b"blah\n"]) + self.assertRaises(exc, fp.write, b"blah") + self.assertRaises(exc, fp.writelines, [b"blah\n"]) with self.open(support.TESTFN, "r") as fp: - self.assertRaises(IOError, fp.write, "blah") - self.assertRaises(IOError, fp.writelines, ["blah\n"]) + self.assertRaises(exc, fp.write, "blah") + self.assertRaises(exc, fp.writelines, ["blah\n"]) + # Non-zero seeking from current or end pos + self.assertRaises(exc, fp.seek, 1, self.SEEK_CUR) + self.assertRaises(exc, fp.seek, -1, self.SEEK_END) def test_raw_file_io(self): with self.open(support.TESTFN, "wb", buffering=0) as f: @@ -670,6 +697,11 @@ b.close() self.assertRaises(ValueError, b.flush) + def test_unseekable(self): + bufio = self.tp(self.MockUnseekableIO(b"A" * 10)) + self.assertRaises(self.UnsupportedOperation, bufio.tell) + self.assertRaises(self.UnsupportedOperation, bufio.seek, 0) + class BufferedReaderTest(unittest.TestCase, CommonBufferedTests): read_mode = "rb" @@ -1433,6 +1465,9 @@ BufferedReaderTest.test_misbehaved_io(self) BufferedWriterTest.test_misbehaved_io(self) + # You can't construct a BufferedRandom over a non-seekable stream. + test_unseekable = None + class CBufferedRandomTest(BufferedRandomTest): tp = io.BufferedRandom @@ -2177,6 +2212,11 @@ txt.close() self.assertRaises(ValueError, txt.flush) + def test_unseekable(self): + txt = self.TextIOWrapper(self.MockUnseekableIO(self.testdata)) + self.assertRaises(self.UnsupportedOperation, txt.tell) + self.assertRaises(self.UnsupportedOperation, txt.seek, 0) + class CTextIOWrapperTest(TextIOWrapperTest): def test_initialization(self): @@ -2550,7 +2590,7 @@ # Put the namespaces of the IO module we are testing and some useful mock # classes in the __dict__ of each test. mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO, - MockNonBlockWriterIO) + MockNonBlockWriterIO, MockUnseekableIO) all_members = io.__all__ + ["IncrementalNewlineDecoder"] c_io_ns = {name : getattr(io, name) for name in all_members} py_io_ns = {name : getattr(pyio, name) for name in all_members} Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 6 01:01:12 2010 @@ -13,6 +13,10 @@ Library ------- +- Issue #9293: I/O streams now raise ``io.UnsupportedOperation`` when an + unsupported operation is attempted (for example, writing to a file open + only for reading). + What's New in Python 3.2 Alpha 2? ================================= Modified: python/branches/py3k/Modules/_io/fileio.c ============================================================================== --- python/branches/py3k/Modules/_io/fileio.c (original) +++ python/branches/py3k/Modules/_io/fileio.c Mon Sep 6 01:01:12 2010 @@ -417,7 +417,8 @@ static PyObject * err_mode(char *action) { - PyErr_Format(PyExc_ValueError, "File not open for %s", action); + PyErr_Format(IO_STATE->unsupported_operation, + "File not open for %s", action); return NULL; } Modified: python/branches/py3k/Modules/_io/iobase.c ============================================================================== --- python/branches/py3k/Modules/_io/iobase.c (original) +++ python/branches/py3k/Modules/_io/iobase.c Mon Sep 6 01:01:12 2010 @@ -317,7 +317,7 @@ return NULL; if (res != Py_True) { Py_CLEAR(res); - PyErr_SetString(PyExc_IOError, "File or stream is not seekable."); + iobase_unsupported("File or stream is not seekable."); return NULL; } if (args == Py_True) { @@ -346,7 +346,7 @@ return NULL; if (res != Py_True) { Py_CLEAR(res); - PyErr_SetString(PyExc_IOError, "File or stream is not readable."); + iobase_unsupported("File or stream is not readable."); return NULL; } if (args == Py_True) { @@ -375,7 +375,7 @@ return NULL; if (res != Py_True) { Py_CLEAR(res); - PyErr_SetString(PyExc_IOError, "File or stream is not writable."); + iobase_unsupported("File or stream is not writable."); return NULL; } if (args == Py_True) { Modified: python/branches/py3k/Modules/_io/textio.c ============================================================================== --- python/branches/py3k/Modules/_io/textio.c (original) +++ python/branches/py3k/Modules/_io/textio.c Mon Sep 6 01:01:12 2010 @@ -1259,10 +1259,8 @@ CHECK_CLOSED(self); - if (self->encoder == NULL) { - PyErr_SetString(PyExc_IOError, "not writable"); - return NULL; - } + if (self->encoder == NULL) + return _unsupported("not writable"); Py_INCREF(text); @@ -1399,7 +1397,7 @@ */ if (self->decoder == NULL) { - PyErr_SetString(PyExc_IOError, "not readable"); + _unsupported("not readable"); return -1; } @@ -1489,10 +1487,8 @@ CHECK_CLOSED(self); - if (self->decoder == NULL) { - PyErr_SetString(PyExc_IOError, "not readable"); - return NULL; - } + if (self->decoder == NULL) + return _unsupported("not readable"); if (_textiowrapper_writeflush(self) < 0) return NULL; @@ -1983,8 +1979,7 @@ Py_INCREF(cookieObj); if (!self->seekable) { - PyErr_SetString(PyExc_IOError, - "underlying stream is not seekable"); + _unsupported("underlying stream is not seekable"); goto fail; } @@ -1995,8 +1990,7 @@ goto fail; if (cmp == 0) { - PyErr_SetString(PyExc_IOError, - "can't do nonzero cur-relative seeks"); + _unsupported("can't do nonzero cur-relative seeks"); goto fail; } @@ -2016,8 +2010,7 @@ goto fail; if (cmp == 0) { - PyErr_SetString(PyExc_IOError, - "can't do nonzero end-relative seeks"); + _unsupported("can't do nonzero end-relative seeks"); goto fail; } @@ -2151,8 +2144,7 @@ CHECK_CLOSED(self); if (!self->seekable) { - PyErr_SetString(PyExc_IOError, - "underlying stream is not seekable"); + _unsupported("underlying stream is not seekable"); goto fail; } if (!self->telling) { From python-checkins at python.org Mon Sep 6 01:15:07 2010 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 6 Sep 2010 01:15:07 +0200 (CEST) Subject: [Python-checkins] r84545 - in python/branches/py3k/Lib: _markupbase.py ftplib.py Message-ID: <20100905231507.076BEEEA01@mail.python.org> Author: raymond.hettinger Date: Mon Sep 6 01:15:06 2010 New Revision: 84545 Log: Take advantage of the frozenset constant optimization. Modified: python/branches/py3k/Lib/_markupbase.py python/branches/py3k/Lib/ftplib.py Modified: python/branches/py3k/Lib/_markupbase.py ============================================================================== --- python/branches/py3k/Lib/_markupbase.py (original) +++ python/branches/py3k/Lib/_markupbase.py Mon Sep 6 01:15:06 2010 @@ -122,7 +122,7 @@ # this could be handled in a separate doctype parser if decltype == "doctype": j = self._parse_doctype_subset(j + 1, i) - elif decltype in ("attlist", "linktype", "link", "element"): + elif decltype in {"attlist", "linktype", "link", "element"}: # must tolerate []'d groups in a content model in an element declaration # also in data attribute specifications of attlist declaration # also link type declaration subsets in linktype declarations @@ -145,10 +145,10 @@ sectName, j = self._scan_name( i+3, i ) if j < 0: return j - if sectName in ("temp", "cdata", "ignore", "include", "rcdata"): + if sectName in {"temp", "cdata", "ignore", "include", "rcdata"}: # look for standard ]]> ending match= _markedsectionclose.search(rawdata, i+3) - elif sectName in ("if", "else", "endif"): + elif sectName in {"if", "else", "endif"}: # look for MS Office ]> ending match= _msmarkedsectionclose.search(rawdata, i+3) else: @@ -203,7 +203,7 @@ name, j = self._scan_name(j + 2, declstartpos) if j == -1: return -1 - if name not in ("attlist", "element", "entity", "notation"): + if name not in {"attlist", "element", "entity", "notation"}: self.updatepos(declstartpos, j + 2) self.error( "unknown declaration %r in internal subset" % name) Modified: python/branches/py3k/Lib/ftplib.py ============================================================================== --- python/branches/py3k/Lib/ftplib.py (original) +++ python/branches/py3k/Lib/ftplib.py Mon Sep 6 01:15:06 2010 @@ -171,7 +171,7 @@ def sanitize(self, s): if s[:5] == 'pass ' or s[:5] == 'PASS ': i = len(s) - while i > 5 and s[i-1] in '\r\n': + while i > 5 and s[i-1] in {'\r', '\n'}: i = i-1 s = s[:5] + '*'*(i-5) + s[i:] return repr(s) @@ -221,7 +221,7 @@ if self.debugging: print('*resp*', self.sanitize(resp)) self.lastresp = resp[:3] c = resp[:1] - if c in ('1', '2', '3'): + if c in {'1', '2', '3'}: return resp if c == '4': raise error_temp(resp) @@ -245,7 +245,7 @@ if self.debugging > 1: print('*put urgent*', self.sanitize(line)) self.sock.sendall(line, MSG_OOB) resp = self.getmultiline() - if resp[:3] not in ('426', '225', '226'): + if resp[:3] not in {'426', '225', '226'}: raise error_proto(resp) def sendcmd(self, cmd): @@ -375,7 +375,7 @@ if not user: user = 'anonymous' if not passwd: passwd = '' if not acct: acct = '' - if user == 'anonymous' and passwd in ('', '-'): + if user == 'anonymous' and passwd in {'', '-'}: # If there is no anonymous ftp password specified # then we'll just use anonymous@ # We don't send any other thing because: @@ -534,7 +534,7 @@ def delete(self, filename): '''Delete a file.''' resp = self.sendcmd('DELE ' + filename) - if resp[:3] in ('250', '200'): + if resp[:3] in {'250', '200'}: return resp else: raise error_reply(resp) @@ -897,9 +897,9 @@ # transfer request. # So: STOR before RETR, because here the target is a "user". treply = target.sendcmd('STOR ' + targetname) - if treply[:3] not in ('125', '150'): raise error_proto # RFC 959 + if treply[:3] not in {'125', '150'}: raise error_proto # RFC 959 sreply = source.sendcmd('RETR ' + sourcename) - if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959 + if sreply[:3] not in {'125', '150'}: raise error_proto # RFC 959 source.voidresp() target.voidresp() From python-checkins at python.org Mon Sep 6 01:50:32 2010 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 6 Sep 2010 01:50:32 +0200 (CEST) Subject: [Python-checkins] r84546 - python/branches/py3k/Doc/library/fileinput.rst Message-ID: <20100905235032.2B208EEA12@mail.python.org> Author: raymond.hettinger Date: Mon Sep 6 01:50:32 2010 New Revision: 84546 Log: Clean-up example of using fileinput as a context manager. Modified: python/branches/py3k/Doc/library/fileinput.rst Modified: python/branches/py3k/Doc/library/fileinput.rst ============================================================================== --- python/branches/py3k/Doc/library/fileinput.rst (original) +++ python/branches/py3k/Doc/library/fileinput.rst Mon Sep 6 01:50:32 2010 @@ -58,8 +58,9 @@ :keyword:`with` statement. In this example, *input* is closed after the :keyword:`with` statement is exited, even if an exception occurs:: - with fileinput.input(files=('spam.txt', 'eggs.txt')) as input: - process(input) + with fileinput.input(files=('spam.txt', 'eggs.txt')) as f: + for line in f: + process(line) .. versionchanged:: 3.2 Can be used as a context manager. From python-checkins at python.org Mon Sep 6 02:32:13 2010 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 6 Sep 2010 02:32:13 +0200 (CEST) Subject: [Python-checkins] r84547 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100906003213.0914EEE994@mail.python.org> Author: benjamin.peterson Date: Mon Sep 6 02:32:12 2010 New Revision: 84547 Log: link to docs Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Mon Sep 6 02:32:12 2010 @@ -215,8 +215,8 @@ (By Nick Coghlan and Terrence Cole; :issue:`9567`, :issue:`3445`, and :issue:`8814`.) -* The :mod:`abc` module now supports :func:`abstractclassmethod` and - :func:`abstractstaticmethod`. +* The :mod:`abc` module now supports :func:`~abc.abstractclassmethod` and + :func:`~abc.abstractstaticmethod`. (:issue:`5867`.) From python-checkins at python.org Mon Sep 6 03:16:46 2010 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 6 Sep 2010 03:16:46 +0200 (CEST) Subject: [Python-checkins] r84548 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100906011646.9430DEE993@mail.python.org> Author: raymond.hettinger Date: Mon Sep 6 03:16:46 2010 New Revision: 84548 Log: More updates to whatsnew3.2 Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Mon Sep 6 03:16:46 2010 @@ -53,22 +53,18 @@ PEP 391: Dictionary Based Configuration for Logging ==================================================== -The :mod:`logging` module had two ways of configuring the module, either by calling -functions for each option or by reading an external file saved in a :mod:`ConfigParser` -format. Those options did not provide the flexibility to create configurations -from JSON or YAML files and they did not support incremental configuration, which -is needed for specifying logger options from a command line. +The :mod:`logging` module provided two kinds of configuration, one style with +function calls for each option or another style driven by an external file saved +in a :mod:`ConfigParser` format. Those options did not provide the flexibility +to create configurations from JSON or YAML files, nor they did not support +incremental configuration, which is needed for specifying logger options from a +command line. To support a more flexible style, the module now offers -:func:`logging.config.dictConfig` to use dictionaries to specify logger -configuration (including formatters, handlers, filters, and loggers). For -example: - ->>> import logging.config ->>> logging.config.dictConfig(json.load(open('log.cfg', 'rb'))) - -The above fragment configures logging from a JSON-encoded dictionary stored in a -file called "log.cfg". Here's a working example of a configuration dictionary:: +:func:`logging.config.dictConfig` for specifying logging configuration with +plain Python dictionaries. The configuration options include formatters, +handlers, filters, and loggers. Here's a working example of a configuration +dictionary:: {"version": 1, "formatters": {"brief": {"format": "%(levelname)-8s: %(name)-15s: %(message)s"}, @@ -87,6 +83,15 @@ }, "root": {"level": "DEBUG", "handlers": ["console", "console_priority"]}} + +If that dictionary is stored in a file called "conf.json", it can loaded +and called with code like this:: + + >>> import logging.config + >>> logging.config.dictConfig(json.load(open('conf.json', 'rb'))) + >>> logging.info("Transaction completed normally") + >>> logging.critical("Abnormal termination") + .. seealso:: :pep:`391` - Dictionary Based Configuration for Logging @@ -119,16 +124,16 @@ * Imported modules now have a :attr:`__cached__` attribute which stores the name of the actual file that was imported: - >>> import collections - >>> collections.__cached__ - 'c:/py32/lib/__pycache__/collections.cpython-32.pyc' + >>> import collections + >>> collections.__cached__ + 'c:/py32/lib/__pycache__/collections.cpython-32.pyc' * The tag that is unique to each interpreter is accessible from the :mod:`imp` module: - >>> import imp - >>> imp.get_tag() - 'cpython-32' + >>> import imp + >>> imp.get_tag() + 'cpython-32' * Scripts that try to deduce source filename from the imported file now need to be smarter. It is no longer sufficient to simply strip the "c" from a ".pyc" @@ -199,10 +204,10 @@ caused confusion and is no longer needed now that the shortest possible :func:`repr` is displayed by default: - >>> repr(math.pi) - '3.141592653589793' - >>> str(math.pi) - '3.141592653589793' + >>> repr(math.pi) + '3.141592653589793' + >>> str(math.pi) + '3.141592653589793' (Proposed and implemented by Mark Dickinson; :issue:`9337`.) @@ -218,8 +223,22 @@ * The :mod:`abc` module now supports :func:`~abc.abstractclassmethod` and :func:`~abc.abstractstaticmethod`. - (:issue:`5867`.) + (Patch submitted by Daniel Urban; :issue:`5867`.) + +* A warning message will now get printed at interpreter shutdown if the + :data:`gc.garbage` list isn't empty. This is meant to make the programmer + aware that their code contains object finalization issues. + + (Added by Antoine Pitrou; :issue:`477863`.) + +* Mark Dickinson crafted an elegant and efficient scheme for assuring that + different numeric datatypes will have the same hash value whenever their + actual values are equal:: + + >>> assert hash(Fraction(3, 2)) == hash(1.5) == \ + hash(Decimal("1.5")) == hash(complex(1.5, 0)) + (See :issue:`8188`.) New, Improved, and Deprecated Modules ===================================== @@ -263,26 +282,28 @@ * The :class:`ftplib.FTP` class now supports the context manager protocol to unconditionally consume :exc:`socket.error` exceptions and to close the FTP - connection when done: + connection when done:: - >>> from ftplib import FTP - >>> with FTP("ftp1.at.proftpd.org") as ftp: - ... ftp.login() - ... ftp.dir() - ... - '230 Anonymous login ok, restrictions apply.' - dr-xr-xr-x 9 ftp ftp 154 May 6 10:43 . - dr-xr-xr-x 9 ftp ftp 154 May 6 10:43 .. - dr-xr-xr-x 5 ftp ftp 4096 May 6 10:43 CentOS - dr-xr-xr-x 3 ftp ftp 18 Jul 10 2008 Fedora + >>> from ftplib import FTP + >>> with FTP("ftp1.at.proftpd.org") as ftp: + ... ftp.login() + ... ftp.dir() + ... + '230 Anonymous login ok, restrictions apply.' + dr-xr-xr-x 9 ftp ftp 154 May 6 10:43 . + dr-xr-xr-x 9 ftp ftp 154 May 6 10:43 .. + dr-xr-xr-x 5 ftp ftp 4096 May 6 10:43 CentOS + dr-xr-xr-x 3 ftp ftp 18 Jul 10 2008 Fedora + + Other file-like objects such as :class:`mmap.mmap` and :func:`fileinput.input` + also grew auto-closing context managers:: + + with fileinput.input(files=('log1.txt', 'log2.txt')) as f: + for line in f: + process(line) - (Contributed by Tarek Ziad? and Giampaolo Rodol?; :issue:`4972`.) - -* A warning message will now get printed at interpreter shutdown if the - :data:`gc.garbage` list isn't empty. This is meant to make the programmer - aware that their code contains object finalization issues. - - (Added by Antoine Pitrou; :issue:`477863`.) + (Contributed by Tarek Ziad? and Giampaolo Rodol? in :issue:`4972`, and + by Georg Brandl in :issue:`8046` and :issue:`1286`.) * The :mod:`os` module now has the :const:`ST_RDONLY` and :const:`ST_NOSUID` constants, for use with the :func:`~os.statvfs` function. @@ -395,15 +416,39 @@ argument. (Contributed by Torsten Landschoff; :issue:`850728`.) -.. Optimizations - ============= +Optimizations +============= - Major performance enhancements have been added: +A number of small performance enhancements have been added: - * Stub +* JSON decoding performance is improved and memory consumption is reduced + whenever the same string is repeated for multiple keys. + + (Contributed by Antoine Pitrou; :issue:`7451`.) + +- Python's peephole optimizer now recognizes patterns such ``x in {1, 2, 3}`` as + being a test for membership in a set of constants. The optimizer recasts the + :class:`set` as a :class:`frozenset` and stores the pre-built constant. + + Now that the speed penalty is gone, it is practical to start writing + membership tests using set-notation. This style is both semantically clear + and operationally fast:: + + extension = name.rpartition('.')[2] + if extension in {'xml', 'html', 'xhtml', 'css'}: + handle(name) + + (Patch and additional tests by Dave Malcolm; :issue:`6690`). + +* The fast-search algorithm in stringlib is now used by the :meth:`split`, + :meth:`rsplit`, :meth:`splitlines` and :meth:`replace` methods on + :class:`bytes`, :class:`bytearray` and :class:`str` objects. Likewise, the + algorithm is also used by :meth:`rfind`, :meth:`rindex`, :meth:`rsplit` and + :meth:`rpartition`. + (Patch by Florent Xicluna in :issue:`7622` and :issue:`7462`.) -Filenames and unicode +Filenames and Unicode ===================== The filesystem encoding can be specified by setting the From python-checkins at python.org Mon Sep 6 03:27:07 2010 From: python-checkins at python.org (eric.araujo) Date: Mon, 6 Sep 2010 03:27:07 +0200 (CEST) Subject: [Python-checkins] r84549 - python/branches/py3k/Doc/Makefile Message-ID: <20100906012707.1893DF4EC@mail.python.org> Author: eric.araujo Date: Mon Sep 6 03:27:06 2010 New Revision: 84549 Log: Update Modified: python/branches/py3k/Doc/Makefile Modified: python/branches/py3k/Doc/Makefile ============================================================================== --- python/branches/py3k/Doc/Makefile (original) +++ python/branches/py3k/Doc/Makefile Mon Sep 6 03:27:06 2010 @@ -55,9 +55,9 @@ svn checkout $(SVNROOT)/external/Pygments-1.3.1/pygments tools/pygments; \ fi -update: clean checkout +update: clean -build: checkout +build: mkdir -p build/$(BUILDER) build/doctrees $(PYTHON) tools/sphinx-build.py $(ALLSPHINXOPTS) @echo From python-checkins at python.org Mon Sep 6 03:29:23 2010 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 6 Sep 2010 03:29:23 +0200 (CEST) Subject: [Python-checkins] r84550 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100906012923.5A5CFF4EC@mail.python.org> Author: raymond.hettinger Date: Mon Sep 6 03:29:23 2010 New Revision: 84550 Log: Fix nits Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Mon Sep 6 03:29:23 2010 @@ -56,7 +56,7 @@ The :mod:`logging` module provided two kinds of configuration, one style with function calls for each option or another style driven by an external file saved in a :mod:`ConfigParser` format. Those options did not provide the flexibility -to create configurations from JSON or YAML files, nor they did not support +to create configurations from JSON or YAML files, nor did they not support incremental configuration, which is needed for specifying logger options from a command line. @@ -220,8 +220,8 @@ (By Nick Coghlan and Terrence Cole; :issue:`9567`, :issue:`3445`, and :issue:`8814`.) -* The :mod:`abc` module now supports :func:`~abc.abstractclassmethod` and - :func:`~abc.abstractstaticmethod`. +* The :mod:`abc` module now supports :func:`abstractclassmethod` and + :func:`abstractstaticmethod`. (Patch submitted by Daniel Urban; :issue:`5867`.) @@ -312,11 +312,11 @@ * The :func:`shutil.copytree` function has two new options: - * *ignore_dangling_symlinks*: when ``symlinks=False`` (meaning that the function - copies the file pointed to by the symlink, not the symlink itself), this + * *ignore_dangling_symlinks*: when ``symlinks=False`` so that the function + copies the file pointed to by the symlink, not the symlink itself. This option will silence the error raised if the file doesn't exist. - * *copy_function*: a callable that will be used to copy files. + * *copy_function*: is a callable that will be used to copy files. :func:`shutil.copy2` is used by default. (Contributed by Tarek Ziad?.) @@ -482,7 +482,7 @@ (Reported by Bupjoe Lee and fixed by Amaury Forgeot D'Arc; :issue:`5127`.) * Computed gotos are now enabled by default on supported compilers (which are - detected by the configure script). They can still be disable selectively by + detected by the configure script). They can still be disabled selectively by specifying ``--without-computed-gotos``. (Contributed by Antoine Pitrou; :issue:`9203`.) From python-checkins at python.org Mon Sep 6 03:31:11 2010 From: python-checkins at python.org (eric.araujo) Date: Mon, 6 Sep 2010 03:31:11 +0200 (CEST) Subject: [Python-checkins] r84551 - python/branches/py3k/Doc/Makefile Message-ID: <20100906013111.59735EE984@mail.python.org> Author: eric.araujo Date: Mon Sep 6 03:31:11 2010 New Revision: 84551 Log: Revert accidental commit, apologies for the noise Modified: python/branches/py3k/Doc/Makefile Modified: python/branches/py3k/Doc/Makefile ============================================================================== --- python/branches/py3k/Doc/Makefile (original) +++ python/branches/py3k/Doc/Makefile Mon Sep 6 03:31:11 2010 @@ -55,9 +55,9 @@ svn checkout $(SVNROOT)/external/Pygments-1.3.1/pygments tools/pygments; \ fi -update: clean +update: clean checkout -build: +build: checkout mkdir -p build/$(BUILDER) build/doctrees $(PYTHON) tools/sphinx-build.py $(ALLSPHINXOPTS) @echo From solipsis at pitrou.net Mon Sep 6 05:04:47 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 06 Sep 2010 05:04:47 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84548): sum=0 Message-ID: py3k results for svn r84548 (hg cset 61710858c559) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogOtCbK0', '-x'] From python-checkins at python.org Mon Sep 6 08:45:47 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 6 Sep 2010 08:45:47 +0200 (CEST) Subject: [Python-checkins] r84552 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100906064547.8E946EE984@mail.python.org> Author: georg.brandl Date: Mon Sep 6 08:45:47 2010 New Revision: 84552 Log: Remove redundant word. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Mon Sep 6 08:45:47 2010 @@ -56,7 +56,7 @@ The :mod:`logging` module provided two kinds of configuration, one style with function calls for each option or another style driven by an external file saved in a :mod:`ConfigParser` format. Those options did not provide the flexibility -to create configurations from JSON or YAML files, nor did they not support +to create configurations from JSON or YAML files, nor did they support incremental configuration, which is needed for specifying logger options from a command line. From python-checkins at python.org Mon Sep 6 08:49:07 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 6 Sep 2010 08:49:07 +0200 (CEST) Subject: [Python-checkins] r84553 - python/branches/py3k/Doc/library/string.rst Message-ID: <20100906064907.E6752EE984@mail.python.org> Author: georg.brandl Date: Mon Sep 6 08:49:07 2010 New Revision: 84553 Log: #9780: both { and } are not valid fill characters. Modified: python/branches/py3k/Doc/library/string.rst Modified: python/branches/py3k/Doc/library/string.rst ============================================================================== --- python/branches/py3k/Doc/library/string.rst (original) +++ python/branches/py3k/Doc/library/string.rst Mon Sep 6 08:49:07 2010 @@ -299,11 +299,11 @@ precision: `integer` type: "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%" -The *fill* character can be any character other than '}' (which signifies the -end of the field). The presence of a fill character is signaled by the *next* -character, which must be one of the alignment options. If the second character -of *format_spec* is not a valid alignment option, then it is assumed that both -the fill character and the alignment option are absent. +The *fill* character can be any character other than '{' or '}'. The presence +of a fill character is signaled by the character following it, which must be +one of the alignment options. If the second character of *format_spec* is not +a valid alignment option, then it is assumed that both the fill character and +the alignment option are absent. The meaning of the various alignment options is as follows: From python-checkins at python.org Mon Sep 6 10:30:24 2010 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 6 Sep 2010 10:30:24 +0200 (CEST) Subject: [Python-checkins] r84554 - in python/branches/py3k: Doc/library/hashlib.rst Lib/hashlib.py Lib/test/test_hashlib.py Misc/NEWS Modules/_hashopenssl.c Message-ID: <20100906083024.2813DEEA21@mail.python.org> Author: gregory.p.smith Date: Mon Sep 6 10:30:23 2010 New Revision: 84554 Log: hashlib has two new constant attributes: algorithms_guaranteed and algorithms_avaiable that respectively list the names of hash algorithms guaranteed to exist in all Python implementations and the names of hash algorithms available in the current process. Renames the attribute new in 3.2a0 'algorithms' to 'algorithms_guaranteed'. Modified: python/branches/py3k/Doc/library/hashlib.rst python/branches/py3k/Lib/hashlib.py python/branches/py3k/Lib/test/test_hashlib.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/_hashopenssl.c Modified: python/branches/py3k/Doc/library/hashlib.rst ============================================================================== --- python/branches/py3k/Doc/library/hashlib.rst (original) +++ python/branches/py3k/Doc/library/hashlib.rst Mon Sep 6 10:30:23 2010 @@ -70,10 +70,13 @@ >>> hashlib.sha224(b"Nobody inspects the spammish repetition").hexdigest() 'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2' -A generic :func:`new` constructor that takes the string name of the desired -algorithm as its first parameter also exists to allow access to the above listed -hashes as well as any other algorithms that your OpenSSL library may offer. The -named constructors are much faster than :func:`new` and should be preferred. +.. function:: new(name[, data]) + + Is a generic constructor that takes the string name of the desired + algorithm as its first parameter. It also exists to allow access to the + above listed hashes as well as any other algorithms that your OpenSSL + library may offer. The named constructors are much faster than :func:`new` + and should be preferred. Using :func:`new` with an algorithm provided by OpenSSL: @@ -82,12 +85,22 @@ >>> h.hexdigest() 'cc4a5ce1b3df48aec5d22d1f16b894a0b894eccc' -This module provides the following constant attribute: +Hashlib provides the following constant attributes: + +.. data:: algorithms_guaranteed + + Contains the names of the hash algorithms guaranteed to be supported + by this module on all platforms. + + .. versionadded:: 3.2 -.. data:: hashlib.algorithms +.. data:: algorithms_available - A tuple providing the names of the hash algorithms guaranteed to be - supported by this module. + Contains the names of the hash algorithms that are available + in the running Python interpreter. These names will be recognized + when passed to :func:`new`. :attr:`algorithms_guaranteed` + will always be a subset. Duplicate algorithms with different + name formats may appear in this set (thanks to OpenSSL). .. versionadded:: 3.2 Modified: python/branches/py3k/Lib/hashlib.py ============================================================================== --- python/branches/py3k/Lib/hashlib.py (original) +++ python/branches/py3k/Lib/hashlib.py Mon Sep 6 10:30:23 2010 @@ -1,6 +1,4 @@ -# $Id$ -# -# Copyright (C) 2005-2007 Gregory P. Smith (greg at krypto.org) +# Copyright (C) 2005-2010 Gregory P. Smith (greg at krypto.org) # Licensed to PSF under a Contributor Agreement. # @@ -15,8 +13,9 @@ md5(), sha1(), sha224(), sha256(), sha384(), and sha512() -More algorithms may be available on your platform but the above are -guaranteed to exist. +More algorithms may be available on your platform but the above are guaranteed +to exist. See the algorithms_guaranteed and algorithms_available attributes +to find out what algorithm names can be passed to new(). NOTE: If you want the adler32 or crc32 hash functions they are available in the zlib module. @@ -57,9 +56,11 @@ # always available algorithm is added. __always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') -algorithms = __always_supported +algorithms_guaranteed = __always_supported +algorithms_available = frozenset(__always_supported) -__all__ = __always_supported + ('new', 'algorithms') +__all__ = __always_supported + ('new', 'algorithms_guaranteed', + 'algorithms_available') def __get_builtin_constructor(name): @@ -124,6 +125,8 @@ import _hashlib new = __hash_new __get_hash = __get_openssl_constructor + algorithms_available = algorithms_available.union( + _hashlib.openssl_md_meth_names) except ImportError: new = __py_new __get_hash = __get_builtin_constructor Modified: python/branches/py3k/Lib/test/test_hashlib.py ============================================================================== --- python/branches/py3k/Lib/test/test_hashlib.py (original) +++ python/branches/py3k/Lib/test/test_hashlib.py Mon Sep 6 10:30:23 2010 @@ -101,11 +101,15 @@ c = cons(a) c.hexdigest() - def test_algorithms_attribute(self): - self.assertEqual(hashlib.algorithms, + def test_algorithms_guaranteed(self): + self.assertEqual(hashlib.algorithms_guaranteed, tuple(_algo for _algo in self.supported_hash_names if _algo.islower())) + def test_algorithms_available(self): + self.assertTrue(set(hashlib.algorithms_guaranteed). + issubset(hashlib.algorithms_available)) + def test_unknown_hash(self): try: hashlib.new('spam spam spam spam spam') Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 6 10:30:23 2010 @@ -17,6 +17,11 @@ unsupported operation is attempted (for example, writing to a file open only for reading). +- hashlib has two new constant attributes: algorithms_guaranteed and + algorithms_avaiable that respectively list the names of hash algorithms + guaranteed to exist in all Python implementations and the names of hash + algorithms available in the current process. + What's New in Python 3.2 Alpha 2? ================================= Modified: python/branches/py3k/Modules/_hashopenssl.c ============================================================================== --- python/branches/py3k/Modules/_hashopenssl.c (original) +++ python/branches/py3k/Modules/_hashopenssl.c Mon Sep 6 10:30:23 2010 @@ -38,6 +38,8 @@ /* EVP is the preferred interface to hashing in OpenSSL */ #include +/* We use the object interface to discover what hashes OpenSSL supports. */ +#include #define MUNCH_SIZE INT_MAX @@ -488,6 +490,62 @@ return ret_obj; } + +/* State for our callback function so that it can accumulate a result. */ +typedef struct _internal_name_mapper_state { + PyObject *set; + int error; +} _InternalNameMapperState; + + +/* A callback function to pass to OpenSSL's OBJ_NAME_do_all(...) */ +static void +_openssl_hash_name_mapper(const OBJ_NAME *openssl_obj_name, void *arg) +{ + _InternalNameMapperState *state = (_InternalNameMapperState *)arg; + PyObject *py_name; + + assert(state != NULL); + if (openssl_obj_name == NULL) + return; + /* Ignore aliased names, they pollute the list and OpenSSL appears to + * have a its own definition of alias as the resulting list still + * contains duplicate and alternate names for several algorithms. */ + if (openssl_obj_name->alias) + return; + + py_name = PyUnicode_FromString(openssl_obj_name->name); + if (py_name == NULL) { + state->error = 1; + } else { + if (PySet_Add(state->set, py_name) != 0) { + Py_DECREF(py_name); + state->error = 1; + } + } +} + + +/* Ask OpenSSL for a list of supported ciphers, filling in a Python set. */ +static PyObject* +generate_hash_name_list(void) +{ + _InternalNameMapperState state; + state.set = PyFrozenSet_New(NULL); + if (state.set == NULL) + return NULL; + state.error = 0; + + OBJ_NAME_do_all(OBJ_NAME_TYPE_MD_METH, &_openssl_hash_name_mapper, &state); + + if (state.error) { + Py_DECREF(state.set); + return NULL; + } + return state.set; +} + + /* * This macro generates constructor function definitions for specific * hash algorithms. These constructors are much faster than calling @@ -581,7 +639,7 @@ PyMODINIT_FUNC PyInit__hashlib(void) { - PyObject *m; + PyObject *m, *openssl_md_meth_names; OpenSSL_add_all_digests(); @@ -598,6 +656,16 @@ if (m == NULL) return NULL; + openssl_md_meth_names = generate_hash_name_list(); + if (openssl_md_meth_names == NULL) { + Py_DECREF(m); + return NULL; + } + if (PyModule_AddObject(m, "openssl_md_meth_names", openssl_md_meth_names)) { + Py_DECREF(m); + return NULL; + } + #if HASH_OBJ_CONSTRUCTOR Py_INCREF(&EVPtype); PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype); From python-checkins at python.org Mon Sep 6 14:36:55 2010 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 6 Sep 2010 14:36:55 +0200 (CEST) Subject: [Python-checkins] r84555 - python/branches/py3k/Tools/iobench/iobench.py Message-ID: <20100906123655.91168EEA58@mail.python.org> Author: antoine.pitrou Date: Mon Sep 6 14:36:55 2010 New Revision: 84555 Log: Add an option to choose the IO module under test (allows to bench e.g. the pure Python implementation in _pyio). Modified: python/branches/py3k/Tools/iobench/iobench.py Modified: python/branches/py3k/Tools/iobench/iobench.py ============================================================================== --- python/branches/py3k/Tools/iobench/iobench.py (original) +++ python/branches/py3k/Tools/iobench/iobench.py Mon Sep 6 14:36:55 2010 @@ -427,6 +427,9 @@ action="store", dest="newlines", default='lf', help="line endings for text tests " "(one of: {lf (default), cr, crlf, all})") + parser.add_option("-m", "--io-module", + action="store", dest="io_module", default=None, + help="io module to test (default: builtin open())") options, args = parser.parse_args() if args: parser.error("unexpected arguments") @@ -451,6 +454,9 @@ if options.encoding: TEXT_ENCODING = options.encoding + if options.io_module: + globals()['open'] = __import__(options.io_module, {}, {}, ['open']).open + prepare_files() run_all_tests(test_options) From python-checkins at python.org Mon Sep 6 18:04:11 2010 From: python-checkins at python.org (brian.curtin) Date: Mon, 6 Sep 2010 18:04:11 +0200 (CEST) Subject: [Python-checkins] r84556 - in python/branches/py3k: Lib/test/test_signal.py Modules/signalmodule.c Message-ID: <20100906160411.1CACBEE9D5@mail.python.org> Author: brian.curtin Date: Mon Sep 6 18:04:10 2010 New Revision: 84556 Log: Clean up the fix to #9324 with some of the suggestions raised on python-dev in response to the original checkin. Move the validation from the original loop into a switch statement, and adjust a platform check in the tests. Modified: python/branches/py3k/Lib/test/test_signal.py python/branches/py3k/Modules/signalmodule.c Modified: python/branches/py3k/Lib/test/test_signal.py ============================================================================== --- python/branches/py3k/Lib/test/test_signal.py (original) +++ python/branches/py3k/Lib/test/test_signal.py Mon Sep 6 18:04:10 2010 @@ -9,9 +9,8 @@ import traceback import sys, os, time, errno -if sys.platform == 'os2' or sys.platform == 'riscos': - raise unittest.SkipTest("Can't test signal on %s" % \ - sys.platform) +if sys.platform in ('os2', 'riscos'): + raise unittest.SkipTest("Can't test signal on %s" % sys.platform) class HandlerBCalled(Exception): Modified: python/branches/py3k/Modules/signalmodule.c ============================================================================== --- python/branches/py3k/Modules/signalmodule.c (original) +++ python/branches/py3k/Modules/signalmodule.c Mon Sep 6 18:04:10 2010 @@ -255,21 +255,20 @@ int sig_num; PyObject *old_handler; void (*func)(int); -#ifdef MS_WINDOWS - int cur_sig, num_valid_sigs = 6; - static int valid_sigs[] = {SIGABRT, SIGFPE, SIGILL, SIGINT, - SIGSEGV, SIGTERM}; - BOOL valid_sig = FALSE; -#endif if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj)) return NULL; #ifdef MS_WINDOWS /* Validate that sig_num is one of the allowable signals */ - for (cur_sig = 0; cur_sig < num_valid_sigs; cur_sig++) - valid_sig |= (sig_num == valid_sigs[cur_sig]); - if (!valid_sig) { - PyErr_SetString(PyExc_ValueError, "signal number out of range"); - return NULL; + switch (sig_num) { + case SIGABRT: break; + case SIGFPE: break; + case SIGILL: break; + case SIGINT: break; + case SIGSEGV: break; + case SIGTERM: break; + default: + PyErr_SetString(PyExc_ValueError, "invalid signal value"); + return NULL; } #endif #ifdef WITH_THREAD From python-checkins at python.org Mon Sep 6 18:10:04 2010 From: python-checkins at python.org (brian.curtin) Date: Mon, 6 Sep 2010 18:10:04 +0200 (CEST) Subject: [Python-checkins] r84557 - in python/branches/release31-maint: Lib/test/test_signal.py Modules/signalmodule.c Message-ID: <20100906161004.5E0DADDAF@mail.python.org> Author: brian.curtin Date: Mon Sep 6 18:10:04 2010 New Revision: 84557 Log: Merged revisions 84556 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84556 | brian.curtin | 2010-09-06 11:04:10 -0500 (Mon, 06 Sep 2010) | 7 lines Clean up the fix to #9324 with some of the suggestions raised on python-dev in response to the original checkin. Move the validation from the original loop into a switch statement, and adjust a platform check in the tests. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_signal.py python/branches/release31-maint/Modules/signalmodule.c Modified: python/branches/release31-maint/Lib/test/test_signal.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_signal.py (original) +++ python/branches/release31-maint/Lib/test/test_signal.py Mon Sep 6 18:10:04 2010 @@ -9,9 +9,8 @@ import traceback import sys, os, time, errno -if sys.platform == 'os2' or sys.platform == 'riscos': - raise unittest.SkipTest("Can't test signal on %s" % \ - sys.platform) +if sys.platform in ('os2', 'riscos'): + raise unittest.SkipTest("Can't test signal on %s" % sys.platform) class HandlerBCalled(Exception): Modified: python/branches/release31-maint/Modules/signalmodule.c ============================================================================== --- python/branches/release31-maint/Modules/signalmodule.c (original) +++ python/branches/release31-maint/Modules/signalmodule.c Mon Sep 6 18:10:04 2010 @@ -252,21 +252,20 @@ int sig_num; PyObject *old_handler; void (*func)(int); -#ifdef MS_WINDOWS - int cur_sig, num_valid_sigs = 6; - static int valid_sigs[] = {SIGABRT, SIGFPE, SIGILL, SIGINT, - SIGSEGV, SIGTERM}; - BOOL valid_sig = FALSE; -#endif if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj)) return NULL; #ifdef MS_WINDOWS /* Validate that sig_num is one of the allowable signals */ - for (cur_sig = 0; cur_sig < num_valid_sigs; cur_sig++) - valid_sig |= (sig_num == valid_sigs[cur_sig]); - if (!valid_sig) { - PyErr_SetString(PyExc_ValueError, "signal number out of range"); - return NULL; + switch (sig_num) { + case SIGABRT: break; + case SIGFPE: break; + case SIGILL: break; + case SIGINT: break; + case SIGSEGV: break; + case SIGTERM: break; + default: + PyErr_SetString(PyExc_ValueError, "invalid signal value"); + return NULL; } #endif #ifdef WITH_THREAD From python-checkins at python.org Mon Sep 6 18:17:50 2010 From: python-checkins at python.org (brian.curtin) Date: Mon, 6 Sep 2010 18:17:50 +0200 (CEST) Subject: [Python-checkins] r84558 - in python/branches/release27-maint: Lib/test/test_signal.py Modules/signalmodule.c Message-ID: <20100906161750.5ADD3EE9B6@mail.python.org> Author: brian.curtin Date: Mon Sep 6 18:17:50 2010 New Revision: 84558 Log: Merged revisions 84556 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84556 | brian.curtin | 2010-09-06 11:04:10 -0500 (Mon, 06 Sep 2010) | 7 lines Clean up the fix to #9324 with some of the suggestions raised on python-dev in response to the original checkin. Move the validation from the original loop into a switch statement, and adjust a platform check in the tests. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_signal.py python/branches/release27-maint/Modules/signalmodule.c Modified: python/branches/release27-maint/Lib/test/test_signal.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_signal.py (original) +++ python/branches/release27-maint/Lib/test/test_signal.py Mon Sep 6 18:17:50 2010 @@ -9,9 +9,8 @@ import traceback import sys, os, time, errno -if sys.platform == 'os2' or sys.platform == 'riscos': - raise unittest.SkipTest("Can't test signal on %s" % \ - sys.platform) +if sys.platform in ('os2', 'riscos'): + raise unittest.SkipTest("Can't test signal on %s" % sys.platform) class HandlerBCalled(Exception): Modified: python/branches/release27-maint/Modules/signalmodule.c ============================================================================== --- python/branches/release27-maint/Modules/signalmodule.c (original) +++ python/branches/release27-maint/Modules/signalmodule.c Mon Sep 6 18:17:50 2010 @@ -255,21 +255,20 @@ int sig_num; PyObject *old_handler; void (*func)(int); -#ifdef MS_WINDOWS - int cur_sig, num_valid_sigs = 6; - static int valid_sigs[] = {SIGABRT, SIGFPE, SIGILL, SIGINT, - SIGSEGV, SIGTERM}; - BOOL valid_sig = FALSE; -#endif if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj)) return NULL; #ifdef MS_WINDOWS /* Validate that sig_num is one of the allowable signals */ - for (cur_sig = 0; cur_sig < num_valid_sigs; cur_sig++) - valid_sig |= (sig_num == valid_sigs[cur_sig]); - if (!valid_sig) { - PyErr_SetString(PyExc_ValueError, "signal number out of range"); - return NULL; + switch (sig_num) { + case SIGABRT: break; + case SIGFPE: break; + case SIGILL: break; + case SIGINT: break; + case SIGSEGV: break; + case SIGTERM: break; + default: + PyErr_SetString(PyExc_ValueError, "invalid signal value"); + return NULL; } #endif #ifdef WITH_THREAD From python-checkins at python.org Mon Sep 6 18:29:29 2010 From: python-checkins at python.org (brian.curtin) Date: Mon, 6 Sep 2010 18:29:29 +0200 (CEST) Subject: [Python-checkins] r84559 - python/branches/py3k/Lib/subprocess.py Message-ID: <20100906162929.8439CEE9C7@mail.python.org> Author: brian.curtin Date: Mon Sep 6 18:29:29 2010 New Revision: 84559 Log: Fix #8956. ValueError message was only mentioning one signal. Rather than list out the three signals (or more over time), the message was made less specific but still descriptive. Modified: python/branches/py3k/Lib/subprocess.py Modified: python/branches/py3k/Lib/subprocess.py ============================================================================== --- python/branches/py3k/Lib/subprocess.py (original) +++ python/branches/py3k/Lib/subprocess.py Mon Sep 6 18:29:29 2010 @@ -983,7 +983,7 @@ elif sig == signal.CTRL_BREAK_EVENT: os.kill(self.pid, signal.CTRL_BREAK_EVENT) else: - raise ValueError("Only SIGTERM is supported on Windows") + raise ValueError("Unsupported signal") def terminate(self): """Terminates the process From python-checkins at python.org Mon Sep 6 18:31:27 2010 From: python-checkins at python.org (brian.curtin) Date: Mon, 6 Sep 2010 18:31:27 +0200 (CEST) Subject: [Python-checkins] r84560 - in python/branches/release27-maint: Lib/subprocess.py Message-ID: <20100906163127.A36DDEE9D9@mail.python.org> Author: brian.curtin Date: Mon Sep 6 18:31:27 2010 New Revision: 84560 Log: Merged revisions 84559 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84559 | brian.curtin | 2010-09-06 11:29:29 -0500 (Mon, 06 Sep 2010) | 6 lines Fix #8956. ValueError message was only mentioning one signal. Rather than list out the three signals (or more over time), the message was made less specific but still descriptive. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/subprocess.py Modified: python/branches/release27-maint/Lib/subprocess.py ============================================================================== --- python/branches/release27-maint/Lib/subprocess.py (original) +++ python/branches/release27-maint/Lib/subprocess.py Mon Sep 6 18:31:27 2010 @@ -993,7 +993,7 @@ elif sig == signal.CTRL_BREAK_EVENT: os.kill(self.pid, signal.CTRL_BREAK_EVENT) else: - raise ValueError("Only SIGTERM is supported on Windows") + raise ValueError("Unsupported signal") def terminate(self): """Terminates the process From python-checkins at python.org Mon Sep 6 19:07:31 2010 From: python-checkins at python.org (brian.curtin) Date: Mon, 6 Sep 2010 19:07:31 +0200 (CEST) Subject: [Python-checkins] r84561 - in python/branches/py3k: Doc/library/os.path.rst Lib/ntpath.py Lib/test/test_ntpath.py Misc/NEWS Modules/posixmodule.c Message-ID: <20100906170731.1D7C3EE987@mail.python.org> Author: brian.curtin Date: Mon Sep 6 19:07:27 2010 New Revision: 84561 Log: Implement #7566 - os.path.sameopenfile for Windows. This uses the GetFileInformationByHandle function to return a tuple of values to identify a file, then ntpath.sameopenfile compares file tuples, which is exposed as os.path.sameopenfile. Modified: python/branches/py3k/Doc/library/os.path.rst python/branches/py3k/Lib/ntpath.py python/branches/py3k/Lib/test/test_ntpath.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/posixmodule.c Modified: python/branches/py3k/Doc/library/os.path.rst ============================================================================== --- python/branches/py3k/Doc/library/os.path.rst (original) +++ python/branches/py3k/Doc/library/os.path.rst Mon Sep 6 19:07:27 2010 @@ -251,7 +251,9 @@ Return ``True`` if the file descriptors *fp1* and *fp2* refer to the same file. - Availability: Unix. + Availability: Unix, Windows. + + .. versionchanged:: 3.2 Added Windows support. .. function:: samestat(stat1, stat2) Modified: python/branches/py3k/Lib/ntpath.py ============================================================================== --- python/branches/py3k/Lib/ntpath.py (original) +++ python/branches/py3k/Lib/ntpath.py Mon Sep 6 19:07:27 2010 @@ -10,6 +10,7 @@ import stat import genericpath from genericpath import * +from nt import _getfileinformation __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", @@ -17,7 +18,7 @@ "ismount", "expanduser","expandvars","normpath","abspath", "splitunc","curdir","pardir","sep","pathsep","defpath","altsep", "extsep","devnull","realpath","supports_unicode_filenames","relpath", - "samefile",] + "samefile", "sameopenfile",] # strings representing various path-related bits and pieces # These are primarily for export; internally, they are hardcoded. @@ -652,3 +653,7 @@ # Also, on other operating systems, fake this method with a # Windows-XP approximation. return abspath(f1) == abspath(f2) + +def sameopenfile(f1, f2): + """Test whether two file objects reference the same file""" + return _getfileinformation(f1) == _getfileinformation(f2) Modified: python/branches/py3k/Lib/test/test_ntpath.py ============================================================================== --- python/branches/py3k/Lib/test/test_ntpath.py (original) +++ python/branches/py3k/Lib/test/test_ntpath.py Mon Sep 6 19:07:27 2010 @@ -2,6 +2,7 @@ import os from test.support import TestFailed from test import support, test_genericpath +from tempfile import TemporaryFile import unittest @@ -237,6 +238,18 @@ tester('ntpath.relpath("/a", "/a")', '.') tester('ntpath.relpath("/a/b", "/a/b")', '.') + def test_sameopenfile(self): + with TemporaryFile() as tf1, TemporaryFile() as tf2: + # Make sure the same file is really the same + self.assertTrue(ntpath.sameopenfile(tf1.fileno(), tf1.fileno())) + # Make sure different files are really different + self.assertFalse(ntpath.sameopenfile(tf1.fileno(), tf2.fileno())) + # Make sure invalid values don't cause issues + with self.assertRaises(ValueError): + # Invalid file descriptors shouldn't display assert + # dialogs (#4804) + ntpath.sameopenfile(-1, -1) + class NtCommonTest(test_genericpath.CommonTest): pathmodule = ntpath Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 6 19:07:27 2010 @@ -13,6 +13,8 @@ Library ------- +- Issue #7566: Implement os.path.sameopenfile for Windows. + - Issue #9293: I/O streams now raise ``io.UnsupportedOperation`` when an unsupported operation is attempted (for example, writing to a file open only for reading). Modified: python/branches/py3k/Modules/posixmodule.c ============================================================================== --- python/branches/py3k/Modules/posixmodule.c (original) +++ python/branches/py3k/Modules/posixmodule.c Mon Sep 6 19:07:27 2010 @@ -2758,6 +2758,33 @@ return result; } /* end of posix__getfinalpathname */ + +static PyObject * +posix__getfileinformation(PyObject *self, PyObject *args) +{ + HANDLE hFile; + BY_HANDLE_FILE_INFORMATION info; + int fd; + + if (!PyArg_ParseTuple(args, "i:_getfileinformation", &fd)) + return NULL; + + if (!_PyVerify_fd(fd)) { + PyErr_SetString(PyExc_ValueError, "received invalid file descriptor"); + return NULL; + } + + hFile = (HANDLE)_get_osfhandle(fd); + if (hFile == INVALID_HANDLE_VALUE) + return win32_error("_getfileinformation", NULL); + + if (!GetFileInformationByHandle(hFile, &info)) + return win32_error("_getfileinformation", NULL); + + return Py_BuildValue("iii", info.dwVolumeSerialNumber, + info.nFileIndexHigh, + info.nFileIndexLow); +} #endif /* MS_WINDOWS */ PyDoc_STRVAR(posix_mkdir__doc__, @@ -7908,6 +7935,7 @@ #ifdef MS_WINDOWS {"_getfullpathname", posix__getfullpathname, METH_VARARGS, NULL}, {"_getfinalpathname", posix__getfinalpathname, METH_VARARGS, NULL}, + {"_getfileinformation", posix__getfileinformation, METH_VARARGS, NULL}, #endif #ifdef HAVE_GETLOADAVG {"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__}, From python-checkins at python.org Mon Sep 6 20:48:22 2010 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 6 Sep 2010 20:48:22 +0200 (CEST) Subject: [Python-checkins] r84562 - in python/branches/py3k: Doc/library/io.rst Lib/_pyio.py Lib/test/test_memoryio.py Misc/NEWS Modules/_io/_iomodule.c Modules/_io/_iomodule.h Modules/_io/bytesio.c Message-ID: <20100906184822.26A92FA6F@mail.python.org> Author: antoine.pitrou Date: Mon Sep 6 20:48:21 2010 New Revision: 84562 Log: Issue #5506: BytesIO objects now have a getbuffer() method exporting a view of their contents without duplicating them. The view is both readable and writable. Modified: python/branches/py3k/Doc/library/io.rst python/branches/py3k/Lib/_pyio.py python/branches/py3k/Lib/test/test_memoryio.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/_io/_iomodule.c python/branches/py3k/Modules/_io/_iomodule.h python/branches/py3k/Modules/_io/bytesio.c Modified: python/branches/py3k/Doc/library/io.rst ============================================================================== --- python/branches/py3k/Doc/library/io.rst (original) +++ python/branches/py3k/Doc/library/io.rst Mon Sep 6 20:48:21 2010 @@ -518,6 +518,24 @@ :class:`BytesIO` provides or overrides these methods in addition to those from :class:`BufferedIOBase` and :class:`IOBase`: + .. method:: getbuffer() + + Return a readable and writable view over the contents of the buffer + without copying them. Also, mutating the view will transparently + update the contents of the buffer:: + + >>> b = io.BytesIO(b"abcdef") + >>> view = b.getbuffer() + >>> view[2:4] = b"56" + >>> b.getvalue() + b'ab56ef' + + .. note:: + As long as the view exists, the :class:`BytesIO` object cannot be + resized. + + .. versionadded:: 3.2 + .. method:: getvalue() Return ``bytes`` containing the entire contents of the buffer. Modified: python/branches/py3k/Lib/_pyio.py ============================================================================== --- python/branches/py3k/Lib/_pyio.py (original) +++ python/branches/py3k/Lib/_pyio.py Mon Sep 6 20:48:21 2010 @@ -785,6 +785,11 @@ raise ValueError("getvalue on closed file") return bytes(self._buffer) + def getbuffer(self): + """Return a readable and writable view of the buffer. + """ + return memoryview(self._buffer) + def read(self, n=None): if self.closed: raise ValueError("read from closed file") Modified: python/branches/py3k/Lib/test/test_memoryio.py ============================================================================== --- python/branches/py3k/Lib/test/test_memoryio.py (original) +++ python/branches/py3k/Lib/test/test_memoryio.py Mon Sep 6 20:48:21 2010 @@ -384,7 +384,31 @@ del __main__.PickleTestMemIO -class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase): +class BytesIOMixin: + + def test_getbuffer(self): + memio = self.ioclass(b"1234567890") + buf = memio.getbuffer() + self.assertEqual(bytes(buf), b"1234567890") + memio.seek(5) + buf = memio.getbuffer() + self.assertEqual(bytes(buf), b"1234567890") + # Trying to change the size of the BytesIO while a buffer is exported + # raises a BufferError. + self.assertRaises(BufferError, memio.write, b'x' * 100) + self.assertRaises(BufferError, memio.truncate) + # Mutating the buffer updates the BytesIO + buf[3:6] = b"abc" + self.assertEqual(bytes(buf), b"123abc7890") + self.assertEqual(memio.getvalue(), b"123abc7890") + # After the buffer gets released, we can resize the BytesIO again + del buf + support.gc_collect() + memio.truncate() + + +class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, + BytesIOMixin, unittest.TestCase): UnsupportedOperation = pyio.UnsupportedOperation Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 6 20:48:21 2010 @@ -13,6 +13,10 @@ Library ------- +- Issue #5506: BytesIO objects now have a getbuffer() method exporting a + view of their contents without duplicating them. The view is both readable + and writable. + - Issue #7566: Implement os.path.sameopenfile for Windows. - Issue #9293: I/O streams now raise ``io.UnsupportedOperation`` when an Modified: python/branches/py3k/Modules/_io/_iomodule.c ============================================================================== --- python/branches/py3k/Modules/_io/_iomodule.c (original) +++ python/branches/py3k/Modules/_io/_iomodule.c Mon Sep 6 20:48:21 2010 @@ -710,6 +710,8 @@ /* BytesIO */ PyBytesIO_Type.tp_base = &PyBufferedIOBase_Type; ADD_TYPE(&PyBytesIO_Type, "BytesIO"); + if (PyType_Ready(&_PyBytesIOBuffer_Type) < 0) + goto fail; /* StringIO */ PyStringIO_Type.tp_base = &PyTextIOBase_Type; Modified: python/branches/py3k/Modules/_io/_iomodule.h ============================================================================== --- python/branches/py3k/Modules/_io/_iomodule.h (original) +++ python/branches/py3k/Modules/_io/_iomodule.h Mon Sep 6 20:48:21 2010 @@ -169,3 +169,5 @@ extern PyObject *_PyIO_empty_str; extern PyObject *_PyIO_empty_bytes; extern PyObject *_PyIO_zero; + +extern PyTypeObject _PyBytesIOBuffer_Type; Modified: python/branches/py3k/Modules/_io/bytesio.c ============================================================================== --- python/branches/py3k/Modules/_io/bytesio.c (original) +++ python/branches/py3k/Modules/_io/bytesio.c Mon Sep 6 20:48:21 2010 @@ -10,8 +10,15 @@ size_t buf_size; PyObject *dict; PyObject *weakreflist; + Py_ssize_t exports; } bytesio; +typedef struct { + PyObject_HEAD + bytesio *source; +} bytesiobuf; + + #define CHECK_CLOSED(self) \ if ((self)->buf == NULL) { \ PyErr_SetString(PyExc_ValueError, \ @@ -19,6 +26,14 @@ return NULL; \ } +#define CHECK_EXPORTS(self) \ + if ((self)->exports > 0) { \ + PyErr_SetString(PyExc_BufferError, \ + "Existing exports of data: object cannot be re-sized"); \ + return NULL; \ + } + + /* Internal routine to get a line from the buffer of a BytesIO object. Returns the length between the current position to the next newline character. */ @@ -173,6 +188,30 @@ Py_RETURN_NONE; } +PyDoc_STRVAR(getbuffer_doc, +"getbuffer() -> bytes.\n" +"\n" +"Get a read-write view over the contents of the BytesIO object."); + +static PyObject * +bytesio_getbuffer(bytesio *self) +{ + PyTypeObject *type = &_PyBytesIOBuffer_Type; + bytesiobuf *buf; + PyObject *view; + + CHECK_CLOSED(self); + + buf = (bytesiobuf *) type->tp_alloc(type, 0); + if (buf == NULL) + return NULL; + Py_INCREF(self); + buf->source = self; + view = PyMemoryView_FromObject((PyObject *) buf); + Py_DECREF(buf); + return view; +} + PyDoc_STRVAR(getval_doc, "getvalue() -> bytes.\n" "\n" @@ -422,6 +461,7 @@ PyObject *arg = Py_None; CHECK_CLOSED(self); + CHECK_EXPORTS(self); if (!PyArg_ParseTuple(args, "|O:truncate", &arg)) return NULL; @@ -543,6 +583,7 @@ PyObject *result = NULL; CHECK_CLOSED(self); + CHECK_EXPORTS(self); if (PyObject_GetBuffer(obj, &buf, PyBUF_CONTIG_RO) < 0) return NULL; @@ -664,6 +705,7 @@ Py_TYPE(self)->tp_name, Py_TYPE(state)->tp_name); return NULL; } + CHECK_EXPORTS(self); /* Reset the object to its default state. This is only needed to handle the case of repeated calls to __setstate__. */ self->string_size = 0; @@ -724,6 +766,11 @@ bytesio_dealloc(bytesio *self) { _PyObject_GC_UNTRACK(self); + if (self->exports > 0) { + PyErr_SetString(PyExc_SystemError, + "deallocated BytesIO object has exported buffers"); + PyErr_Print(); + } if (self->buf != NULL) { PyMem_Free(self->buf); self->buf = NULL; @@ -818,6 +865,7 @@ {"readline", (PyCFunction)bytesio_readline, METH_VARARGS, readline_doc}, {"readlines", (PyCFunction)bytesio_readlines, METH_VARARGS, readlines_doc}, {"read", (PyCFunction)bytesio_read, METH_VARARGS, read_doc}, + {"getbuffer", (PyCFunction)bytesio_getbuffer, METH_NOARGS, getbuffer_doc}, {"getvalue", (PyCFunction)bytesio_getvalue, METH_NOARGS, getval_doc}, {"seek", (PyCFunction)bytesio_seek, METH_VARARGS, seek_doc}, {"truncate", (PyCFunction)bytesio_truncate, METH_VARARGS, truncate_doc}, @@ -873,3 +921,96 @@ 0, /*tp_alloc*/ bytesio_new, /*tp_new*/ }; + + +/* + * Implementation of the small intermediate object used by getbuffer(). + * getbuffer() returns a memoryview over this object, which should make it + * invisible from Python code. + */ + +static int +bytesiobuf_getbuffer(bytesiobuf *obj, Py_buffer *view, int flags) +{ + int ret; + void *ptr; + bytesio *b = (bytesio *) obj->source; + if (view == NULL) { + b->exports++; + return 0; + } + ptr = (void *) obj; + ret = PyBuffer_FillInfo(view, (PyObject*)obj, b->buf, b->string_size, + 0, flags); + if (ret >= 0) { + b->exports++; + } + return ret; +} + +static void +bytesiobuf_releasebuffer(bytesiobuf *obj, Py_buffer *view) +{ + bytesio *b = (bytesio *) obj->source; + b->exports--; +} + +static int +bytesiobuf_traverse(bytesiobuf *self, visitproc visit, void *arg) +{ + Py_VISIT(self->source); + return 0; +} + +static void +bytesiobuf_dealloc(bytesiobuf *self) +{ + Py_CLEAR(self->source); + Py_TYPE(self)->tp_free(self); +} + +static PyBufferProcs bytesiobuf_as_buffer = { + (getbufferproc) bytesiobuf_getbuffer, + (releasebufferproc) bytesiobuf_releasebuffer, +}; + +PyTypeObject _PyBytesIOBuffer_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_io._BytesIOBuffer", /*tp_name*/ + sizeof(bytesiobuf), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)bytesiobuf_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + &bytesiobuf_as_buffer, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + 0, /*tp_doc*/ + (traverseproc)bytesiobuf_traverse, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ +}; From python-checkins at python.org Mon Sep 6 21:25:46 2010 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 6 Sep 2010 21:25:46 +0200 (CEST) Subject: [Python-checkins] r84563 - in python/branches/py3k: Doc/library/unittest.rst Lib/unittest/case.py Lib/unittest/test/test_case.py Misc/NEWS Message-ID: <20100906192546.5E27BEE98E@mail.python.org> Author: antoine.pitrou Date: Mon Sep 6 21:25:46 2010 New Revision: 84563 Log: Issue #9754: Similarly to assertRaises and assertRaisesRegexp, unittest test cases now also have assertWarns and assertWarnsRegexp methods to check that a given warning type was triggered by the code under test. Modified: python/branches/py3k/Doc/library/unittest.rst python/branches/py3k/Lib/unittest/case.py python/branches/py3k/Lib/unittest/test/test_case.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/unittest.rst ============================================================================== --- python/branches/py3k/Doc/library/unittest.rst (original) +++ python/branches/py3k/Doc/library/unittest.rst Mon Sep 6 21:25:46 2010 @@ -1083,6 +1083,59 @@ .. versionadded:: 3.1 + .. method:: assertWarns(warning, callable, *args, **kwds) + assertWarns(warning) + + Test that a warning is triggered when *callable* is called with any + positional or keyword arguments that are also passed to + :meth:`assertWarns`. The test passes if *warning* is triggered and + fails if it isn't. Also, any unexpected exception is an error. + To catch any of a group of warnings, a tuple containing the warning + classes may be passed as *warnings*. + + If only the *warning* argument is given, returns a context manager so + that the code under test can be written inline rather than as a function:: + + with self.assertWarns(SomeWarning): + do_something() + + The context manager will store the caught warning object in its + :attr:`warning` attribute, and the source line which triggered the + warnings in the :attr:`filename` and :attr:`lineno` attributes. + This can be useful if the intention is to perform additional checks + on the exception raised:: + + with self.assertWarns(SomeWarning) as cm: + do_something() + + self.assertIn('myfile.py', cm.filename) + self.assertEqual(320, cm.lineno) + + This method works regardless of the warning filters in place when it + is called. + + .. versionadded:: 3.2 + + + .. method:: assertWarnsRegexp(warning, regexp[, callable, ...]) + + Like :meth:`assertWarns` but also tests that *regexp* matches on the + message of the triggered warning. *regexp* may be a regular expression + object or a string containing a regular expression suitable for use + by :func:`re.search`. Example:: + + self.assertWarnsRegexp(DeprecationWarning, + r'legacy_function\(\) is deprecated', + legacy_function, 'XYZ') + + or:: + + with self.assertWarnsRegexp(RuntimeWarning, 'unsafe frobnicating'): + frobnicate('/etc/passwd') + + .. versionadded:: 3.2 + + .. method:: assertIsNone(expr, msg=None) This signals a test failure if *expr* is not None. Modified: python/branches/py3k/Lib/unittest/case.py ============================================================================== --- python/branches/py3k/Lib/unittest/case.py (original) +++ python/branches/py3k/Lib/unittest/case.py Mon Sep 6 21:25:46 2010 @@ -90,8 +90,7 @@ return wrapper -class _AssertRaisesContext(object): - """A context manager used to implement TestCase.assertRaises* methods.""" +class _AssertRaisesBaseContext(object): def __init__(self, expected, test_case, callable_obj=None, expected_regexp=None): @@ -104,8 +103,14 @@ self.obj_name = str(callable_obj) else: self.obj_name = None + if isinstance(expected_regexp, (bytes, str)): + expected_regexp = re.compile(expected_regexp) self.expected_regexp = expected_regexp + +class _AssertRaisesContext(_AssertRaisesBaseContext): + """A context manager used to implement TestCase.assertRaises* methods.""" + def __enter__(self): return self @@ -130,14 +135,62 @@ return True expected_regexp = self.expected_regexp - if isinstance(expected_regexp, (bytes, str)): - expected_regexp = re.compile(expected_regexp) if not expected_regexp.search(str(exc_value)): raise self.failureException('"%s" does not match "%s"' % (expected_regexp.pattern, str(exc_value))) return True +class _AssertWarnsContext(_AssertRaisesBaseContext): + """A context manager used to implement TestCase.assertWarns* methods.""" + + def __enter__(self): + # The __warningregistry__'s need to be in a pristine state for tests + # to work properly. + for v in sys.modules.values(): + if getattr(v, '__warningregistry__', None): + v.__warningregistry__ = {} + self.warnings_manager = warnings.catch_warnings(record=True) + self.warnings = self.warnings_manager.__enter__() + warnings.simplefilter("always", self.expected) + return self + + def __exit__(self, exc_type, exc_value, tb): + self.warnings_manager.__exit__(exc_type, exc_value, tb) + if exc_type is not None: + # let unexpected exceptions pass through + return + try: + exc_name = self.expected.__name__ + except AttributeError: + exc_name = str(self.expected) + first_matching = None + for m in self.warnings: + w = m.message + if not isinstance(w, self.expected): + continue + if first_matching is None: + first_matching = w + if (self.expected_regexp is not None and + not self.expected_regexp.search(str(w))): + continue + # store warning for later retrieval + self.warning = w + self.filename = m.filename + self.lineno = m.lineno + return + # Now we simply try to choose a helpful failure message + if first_matching is not None: + raise self.failureException('"%s" does not match "%s"' % + (self.expected_regexp.pattern, str(first_matching))) + if self.obj_name: + raise self.failureException("{0} not triggered by {1}" + .format(exc_name, self.obj_name)) + else: + raise self.failureException("{0} not triggered" + .format(exc_name)) + + class TestCase(object): """A class whose instances are single test cases. @@ -464,6 +517,37 @@ with context: callableObj(*args, **kwargs) + def assertWarns(self, expected_warning, callable_obj=None, *args, **kwargs): + """Fail unless a warning of class warnClass is triggered + by callableObj when invoked with arguments args and keyword + arguments kwargs. If a different type of warning is + triggered, it will not be handled: depending on the other + warning filtering rules in effect, it might be silenced, printed + out, or raised as an exception. + + If called with callableObj omitted or None, will return a + context object used like this:: + + with self.assertWarns(SomeWarning): + do_something() + + The context manager keeps a reference to the first matching + warning as the 'warning' attribute; similarly, the 'filename' + and 'lineno' attributes give you information about the line + of Python code from which the warning was triggered. + This allows you to inspect the warning after the assertion:: + + with self.assertWarns(SomeWarning) as cm: + do_something() + the_warning = cm.warning + self.assertEqual(the_warning.some_attribute, 147) + """ + context = _AssertWarnsContext(expected_warning, self, callable_obj) + if callable_obj is None: + return context + with context: + callable_obj(*args, **kwargs) + def _getAssertEqualityFunc(self, first, second): """Get a detailed comparison function for the types of the two args. @@ -1019,6 +1103,28 @@ with context: callable_obj(*args, **kwargs) + def assertWarnsRegexp(self, expected_warning, expected_regexp, + callable_obj=None, *args, **kwargs): + """Asserts that the message in a triggered warning matches a regexp. + Basic functioning is similar to assertWarns() with the addition + that only warnings whose messages also match the regular expression + are considered successful matches. + + Args: + expected_warning: Warning class expected to be triggered. + expected_regexp: Regexp (re pattern object or string) expected + to be found in error message. + callable_obj: Function to be called. + args: Extra args. + kwargs: Extra kwargs. + """ + context = _AssertWarnsContext(expected_warning, self, callable_obj, + expected_regexp) + if callable_obj is None: + return context + with context: + callable_obj(*args, **kwargs) + def assertRegexpMatches(self, text, expected_regexp, msg=None): """Fail the test unless the text matches the regular expression.""" if isinstance(expected_regexp, (str, bytes)): Modified: python/branches/py3k/Lib/unittest/test/test_case.py ============================================================================== --- python/branches/py3k/Lib/unittest/test/test_case.py (original) +++ python/branches/py3k/Lib/unittest/test/test_case.py Mon Sep 6 21:25:46 2010 @@ -2,6 +2,8 @@ import pprint import re import sys +import warnings +import inspect from copy import deepcopy from test import support @@ -917,6 +919,138 @@ self.assertIsInstance(e, ExceptionMock) self.assertEqual(e.args[0], v) + def testAssertWarnsCallable(self): + def _runtime_warn(): + warnings.warn("foo", RuntimeWarning) + # Success when the right warning is triggered, even several times + self.assertWarns(RuntimeWarning, _runtime_warn) + self.assertWarns(RuntimeWarning, _runtime_warn) + # A tuple of warning classes is accepted + self.assertWarns((DeprecationWarning, RuntimeWarning), _runtime_warn) + # *args and **kwargs also work + self.assertWarns(RuntimeWarning, + warnings.warn, "foo", category=RuntimeWarning) + # Failure when no warning is triggered + with self.assertRaises(self.failureException): + self.assertWarns(RuntimeWarning, lambda: 0) + # Failure when another warning is triggered + with warnings.catch_warnings(): + # Force default filter (in case tests are run with -We) + warnings.simplefilter("default", RuntimeWarning) + with self.assertRaises(self.failureException): + self.assertWarns(DeprecationWarning, _runtime_warn) + # Filters for other warnings are not modified + with warnings.catch_warnings(): + warnings.simplefilter("error", RuntimeWarning) + with self.assertRaises(RuntimeWarning): + self.assertWarns(DeprecationWarning, _runtime_warn) + + def testAssertWarnsContext(self): + # Believe it or not, it is preferrable to duplicate all tests above, + # to make sure the __warningregistry__ $@ is circumvented correctly. + def _runtime_warn(): + warnings.warn("foo", RuntimeWarning) + _runtime_warn_lineno = inspect.getsourcelines(_runtime_warn)[1] + with self.assertWarns(RuntimeWarning) as cm: + _runtime_warn() + # A tuple of warning classes is accepted + with self.assertWarns((DeprecationWarning, RuntimeWarning)) as cm: + _runtime_warn() + # The context manager exposes various useful attributes + self.assertIsInstance(cm.warning, RuntimeWarning) + self.assertEqual(cm.warning.args[0], "foo") + self.assertIn("test_case.py", cm.filename) + self.assertEqual(cm.lineno, _runtime_warn_lineno + 1) + # Same with several warnings + with self.assertWarns(RuntimeWarning): + _runtime_warn() + _runtime_warn() + with self.assertWarns(RuntimeWarning): + warnings.warn("foo", category=RuntimeWarning) + # Failure when no warning is triggered + with self.assertRaises(self.failureException): + with self.assertWarns(RuntimeWarning): + pass + # Failure when another warning is triggered + with warnings.catch_warnings(): + # Force default filter (in case tests are run with -We) + warnings.simplefilter("default", RuntimeWarning) + with self.assertRaises(self.failureException): + with self.assertWarns(DeprecationWarning): + _runtime_warn() + # Filters for other warnings are not modified + with warnings.catch_warnings(): + warnings.simplefilter("error", RuntimeWarning) + with self.assertRaises(RuntimeWarning): + with self.assertWarns(DeprecationWarning): + _runtime_warn() + + def testAssertWarnsRegexpCallable(self): + def _runtime_warn(msg): + warnings.warn(msg, RuntimeWarning) + self.assertWarnsRegexp(RuntimeWarning, "o+", + _runtime_warn, "foox") + # Failure when no warning is triggered + with self.assertRaises(self.failureException): + self.assertWarnsRegexp(RuntimeWarning, "o+", + lambda: 0) + # Failure when another warning is triggered + with warnings.catch_warnings(): + # Force default filter (in case tests are run with -We) + warnings.simplefilter("default", RuntimeWarning) + with self.assertRaises(self.failureException): + self.assertWarnsRegexp(DeprecationWarning, "o+", + _runtime_warn, "foox") + # Failure when message doesn't match + with self.assertRaises(self.failureException): + self.assertWarnsRegexp(RuntimeWarning, "o+", + _runtime_warn, "barz") + # A little trickier: we ask RuntimeWarnings to be raised, and then + # check for some of them. It is implementation-defined whether + # non-matching RuntimeWarnings are simply re-raised, or produce a + # failureException. + with warnings.catch_warnings(): + warnings.simplefilter("error", RuntimeWarning) + with self.assertRaises((RuntimeWarning, self.failureException)): + self.assertWarnsRegexp(RuntimeWarning, "o+", + _runtime_warn, "barz") + + def testAssertWarnsRegexpContext(self): + # Same as above, but with assertWarnsRegexp as a context manager + def _runtime_warn(msg): + warnings.warn(msg, RuntimeWarning) + _runtime_warn_lineno = inspect.getsourcelines(_runtime_warn)[1] + with self.assertWarnsRegexp(RuntimeWarning, "o+") as cm: + _runtime_warn("foox") + self.assertIsInstance(cm.warning, RuntimeWarning) + self.assertEqual(cm.warning.args[0], "foox") + self.assertIn("test_case.py", cm.filename) + self.assertEqual(cm.lineno, _runtime_warn_lineno + 1) + # Failure when no warning is triggered + with self.assertRaises(self.failureException): + with self.assertWarnsRegexp(RuntimeWarning, "o+"): + pass + # Failure when another warning is triggered + with warnings.catch_warnings(): + # Force default filter (in case tests are run with -We) + warnings.simplefilter("default", RuntimeWarning) + with self.assertRaises(self.failureException): + with self.assertWarnsRegexp(DeprecationWarning, "o+"): + _runtime_warn("foox") + # Failure when message doesn't match + with self.assertRaises(self.failureException): + with self.assertWarnsRegexp(RuntimeWarning, "o+"): + _runtime_warn("barz") + # A little trickier: we ask RuntimeWarnings to be raised, and then + # check for some of them. It is implementation-defined whether + # non-matching RuntimeWarnings are simply re-raised, or produce a + # failureException. + with warnings.catch_warnings(): + warnings.simplefilter("error", RuntimeWarning) + with self.assertRaises((RuntimeWarning, self.failureException)): + with self.assertWarnsRegexp(RuntimeWarning, "o+"): + _runtime_warn("barz") + def testSynonymAssertMethodNames(self): """Test undocumented method name synonyms. Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 6 21:25:46 2010 @@ -13,6 +13,10 @@ Library ------- +- Issue #9754: Similarly to assertRaises and assertRaisesRegexp, unittest + test cases now also have assertWarns and assertWarnsRegexp methods to + check that a given warning type was triggered by the code under test. + - Issue #5506: BytesIO objects now have a getbuffer() method exporting a view of their contents without duplicating them. The view is both readable and writable. From python-checkins at python.org Mon Sep 6 21:46:17 2010 From: python-checkins at python.org (brian.curtin) Date: Mon, 6 Sep 2010 21:46:17 +0200 (CEST) Subject: [Python-checkins] r84564 - in python/branches/py3k/Lib: ntpath.py test/test_ntpath.py Message-ID: <20100906194617.4ECACEE9B1@mail.python.org> Author: brian.curtin Date: Mon Sep 6 21:46:17 2010 New Revision: 84564 Log: Fix some errors that #7566 introduced on non-Windows platforms due to an ImportError. Rearranged the import, faked out the implementation when the import fails, and reorganized a test that depends on Win32 behavior. Modified: python/branches/py3k/Lib/ntpath.py python/branches/py3k/Lib/test/test_ntpath.py Modified: python/branches/py3k/Lib/ntpath.py ============================================================================== --- python/branches/py3k/Lib/ntpath.py (original) +++ python/branches/py3k/Lib/ntpath.py Mon Sep 6 21:46:17 2010 @@ -10,7 +10,6 @@ import stat import genericpath from genericpath import * -from nt import _getfileinformation __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", @@ -656,4 +655,10 @@ def sameopenfile(f1, f2): """Test whether two file objects reference the same file""" - return _getfileinformation(f1) == _getfileinformation(f2) + try: + from nt import _getfileinformation + return _getfileinformation(f1) == _getfileinformation(f2) + except ImportError: + # On other operating systems, return True if the file descriptors + # are the same. + return f1 == f2 Modified: python/branches/py3k/Lib/test/test_ntpath.py ============================================================================== --- python/branches/py3k/Lib/test/test_ntpath.py (original) +++ python/branches/py3k/Lib/test/test_ntpath.py Mon Sep 6 21:46:17 2010 @@ -1,5 +1,6 @@ import ntpath import os +import sys from test.support import TestFailed from test import support, test_genericpath from tempfile import TemporaryFile @@ -244,11 +245,12 @@ self.assertTrue(ntpath.sameopenfile(tf1.fileno(), tf1.fileno())) # Make sure different files are really different self.assertFalse(ntpath.sameopenfile(tf1.fileno(), tf2.fileno())) - # Make sure invalid values don't cause issues - with self.assertRaises(ValueError): - # Invalid file descriptors shouldn't display assert - # dialogs (#4804) - ntpath.sameopenfile(-1, -1) + # Make sure invalid values don't cause issues on win32 + if sys.platform == "win32": + with self.assertRaises(ValueError): + # Invalid file descriptors shouldn't display assert + # dialogs (#4804) + ntpath.sameopenfile(-1, -1) class NtCommonTest(test_genericpath.CommonTest): From python-checkins at python.org Mon Sep 6 21:55:51 2010 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 6 Sep 2010 21:55:51 +0200 (CEST) Subject: [Python-checkins] r84565 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100906195551.3F3F0F959@mail.python.org> Author: raymond.hettinger Date: Mon Sep 6 21:55:51 2010 New Revision: 84565 Log: Fix markup nits Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Mon Sep 6 21:55:51 2010 @@ -220,8 +220,8 @@ (By Nick Coghlan and Terrence Cole; :issue:`9567`, :issue:`3445`, and :issue:`8814`.) -* The :mod:`abc` module now supports :func:`abstractclassmethod` and - :func:`abstractstaticmethod`. +* The :mod:`abc` module now supports :func:`~abc.abstractclassmethod` and + :func:`~abc.abstractstaticmethod`. (Patch submitted by Daniel Urban; :issue:`5867`.) @@ -410,7 +410,7 @@ (Contributed by Antoine Pitrou; :issue:`3001`.) * Regular and recursive locks now accept an optional *timeout* argument to their - ``acquire`` method. (Contributed by Antoine Pitrou; :issue:`7316`.) + :meth:`acquire` method. (Contributed by Antoine Pitrou; :issue:`7316`.) Similarly, :meth:`threading.Semaphore.acquire` also gains a *timeout* argument. (Contributed by Torsten Landschoff; :issue:`850728`.) From python-checkins at python.org Mon Sep 6 22:27:15 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 6 Sep 2010 22:27:15 +0200 (CEST) Subject: [Python-checkins] r84566 - python/branches/py3k/Misc/pymemcompat.h Message-ID: <20100906202715.80F84EE9B7@mail.python.org> Author: florent.xicluna Date: Mon Sep 6 22:27:15 2010 New Revision: 84566 Log: typo Modified: python/branches/py3k/Misc/pymemcompat.h Modified: python/branches/py3k/Misc/pymemcompat.h ============================================================================== --- python/branches/py3k/Misc/pymemcompat.h (original) +++ python/branches/py3k/Misc/pymemcompat.h Mon Sep 6 22:27:15 2010 @@ -72,7 +72,7 @@ It is possible to support both the 2.0 and 2.2 GC APIs, but it's not pretty and this comment block is too narrow to contain a - desciption of what's required... */ + description of what's required... */ #if PY_VERSION_HEX < 0x020200B1 #define PyObject_GC_New PyObject_New From python-checkins at python.org Mon Sep 6 22:27:55 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 6 Sep 2010 22:27:55 +0200 (CEST) Subject: [Python-checkins] r84567 - python/branches/py3k/Lib/string.py Message-ID: <20100906202755.E0D73EE98E@mail.python.org> Author: florent.xicluna Date: Mon Sep 6 22:27:55 2010 New Revision: 84567 Log: typo Modified: python/branches/py3k/Lib/string.py Modified: python/branches/py3k/Lib/string.py ============================================================================== --- python/branches/py3k/Lib/string.py (original) +++ python/branches/py3k/Lib/string.py Mon Sep 6 22:27:55 2010 @@ -174,7 +174,7 @@ # see PEP 3101 for details and purpose of this class # The hard parts are reused from the C implementation. They're exposed as "_" -# prefixed methods of str and unicode. +# prefixed methods of str. # The overall parser is implemented in str._formatter_parser. # The field name parser is implemented in str._formatter_field_name_split @@ -246,7 +246,7 @@ return str(value) elif conversion is None: return value - raise ValueError("Unknown converion specifier {0!s}".format(conversion)) + raise ValueError("Unknown conversion specifier {0!s}".format(conversion)) # returns an iterable that contains tuples of the form: From python-checkins at python.org Mon Sep 6 23:26:09 2010 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 6 Sep 2010 23:26:09 +0200 (CEST) Subject: [Python-checkins] r84568 - in python/branches/py3k: Doc/library/collections.rst Lib/collections.py Lib/functools.py Lib/test/test_collections.py Misc/NEWS Message-ID: <20100906212609.B4701EE982@mail.python.org> Author: raymond.hettinger Date: Mon Sep 6 23:26:09 2010 New Revision: 84568 Log: Add method to OrderedDict for repositioning keys to the ends. Modified: python/branches/py3k/Doc/library/collections.rst python/branches/py3k/Lib/collections.py python/branches/py3k/Lib/functools.py python/branches/py3k/Lib/test/test_collections.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/collections.rst ============================================================================== --- python/branches/py3k/Doc/library/collections.rst (original) +++ python/branches/py3k/Doc/library/collections.rst Mon Sep 6 23:26:09 2010 @@ -793,6 +793,23 @@ (key, value) pair. The pairs are returned in LIFO order if *last* is true or FIFO order if false. + .. method:: move_to_end(key, last=True) + + Move an existing *key* to either end of an ordered dictionary. The item + is moved to the right end if *last* is true (the default) or to the + beginning if *last* is false. Raises :exc:`KeyError` if the *key* does + not exist:: + + >>> d = OrderedDict.fromkeys('abcde') + >>> d.move_to_end('b') + >>> ''.join(d.keys) + 'acdeb' + >>> d.move_to_end('b', 0) + >>> ''.join(d.keys) + 'bacde' + + .. versionadded:: 3.2 + In addition to the usual mapping methods, ordered dictionaries also support reverse iteration using :func:`reversed`. Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Mon Sep 6 23:26:09 2010 @@ -173,18 +173,29 @@ def __del__(self): self.clear() # eliminate cyclical references - def _renew(self, key, PREV=0, NEXT=1): - 'Fast version of self[key]=self.pop(key). Private method for internal use.' + def move_to_end(self, key, last=True, PREV=0, NEXT=1): + '''Move an existing element to the end (or beginning if last==False). + + Raises KeyError if the element does not exist. + When last=True, acts like a fast version of self[key]=self.pop(key). + + ''' link = self.__map[key] link_prev = link[PREV] link_next = link[NEXT] link_prev[NEXT] = link_next link_next[PREV] = link_prev root = self.__root - last = root[PREV] - link[PREV] = last - link[NEXT] = root - last[NEXT] = root[PREV] = link + if last: + last = root[PREV] + link[PREV] = last + link[NEXT] = root + last[NEXT] = root[PREV] = link + else: + first = root[NEXT] + link[PREV] = root + link[NEXT] = first + root[NEXT] = first[PREV] = link ################################################################################ Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Mon Sep 6 23:26:09 2010 @@ -127,7 +127,7 @@ len=len, KeyError=KeyError): cache = OrderedDict() # ordered least recent to most recent cache_popitem = cache.popitem - cache_renew = cache._renew + cache_renew = cache.move_to_end kwd_mark = object() # separate positional and keyword args lock = Lock() Modified: python/branches/py3k/Lib/test/test_collections.py ============================================================================== --- python/branches/py3k/Lib/test/test_collections.py (original) +++ python/branches/py3k/Lib/test/test_collections.py Mon Sep 6 23:26:09 2010 @@ -973,7 +973,19 @@ od['a'] = 1 self.assertEqual(list(od.items()), [('b', 2), ('a', 1)]) - + def test_move_to_end(self): + od = OrderedDict.fromkeys('abcde') + self.assertEqual(list(od), list('abcde')) + od.move_to_end('c') + self.assertEqual(list(od), list('abdec')) + od.move_to_end('c', 0) + self.assertEqual(list(od), list('cabde')) + od.move_to_end('c', 0) + self.assertEqual(list(od), list('cabde')) + od.move_to_end('e') + self.assertEqual(list(od), list('cabde')) + with self.assertRaises(KeyError): + od.move_to_end('x') class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): type2test = OrderedDict Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 6 23:26:09 2010 @@ -13,6 +13,9 @@ Library ------- +- collections.OrderedDict now supports a new method for repositioning + keys to either end. + - Issue #9754: Similarly to assertRaises and assertRaisesRegexp, unittest test cases now also have assertWarns and assertWarnsRegexp methods to check that a given warning type was triggered by the code under test. From python-checkins at python.org Tue Sep 7 00:18:20 2010 From: python-checkins at python.org (vinay.sajip) Date: Tue, 7 Sep 2010 00:18:20 +0200 (CEST) Subject: [Python-checkins] r84569 - in python/branches: py3k/Doc/library/logging.rst release27-maint/Doc/library/logging.rst release31-maint/Doc/library/logging.rst Message-ID: <20100906221820.D7C1AF525@mail.python.org> Author: vinay.sajip Date: Tue Sep 7 00:18:20 2010 New Revision: 84569 Log: Updated information on logging contextual information. Modified: python/branches/py3k/Doc/library/logging.rst python/branches/release27-maint/Doc/library/logging.rst python/branches/release31-maint/Doc/library/logging.rst Modified: python/branches/py3k/Doc/library/logging.rst ============================================================================== --- python/branches/py3k/Doc/library/logging.rst (original) +++ python/branches/py3k/Doc/library/logging.rst Tue Sep 7 00:18:20 2010 @@ -1317,6 +1317,10 @@ be hard to manage if the number of :class:`Logger` instances becomes effectively unbounded. + +Using LoggerAdapters to impart contextual information +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + An easy way in which you can pass contextual information to be output along with logging event information is to use the :class:`LoggerAdapter` class. This class is designed to look like a :class:`Logger`, so that you can call @@ -1421,6 +1425,78 @@ 2008-01-18 14:49:54,033 d.e.f WARNING IP: 127.0.0.1 User: jim A message at WARNING level with 2 parameters +Using Filters to impart contextual information +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can also add contextual information to log output using a user-defined +:class:`Filter`. ``Filter`` instances are allowed to modify the ``LogRecords`` +passed to them, including adding additional attributes which can then be output +using a suitable format string, or if needed a custom :class:`Formatter`. + +For example in a web application, the request being processed (or at least, +the interesting parts of it) can be stored in a threadlocal +(:class:`threading.local`) variable, and then accessed from a ``Filter`` to +add, say, information from the request - say, the remote IP address and remote +user's username - to the ``LogRecord``, using the attribute names 'ip' and +'user' as in the ``LoggerAdapter`` example above. In that case, the same format +string can be used to get similar output to that shown above. Here's an example +script:: + + import logging + from random import choice + + class ContextFilter(logging.Filter): + """ + This is a filter which injects contextual information into the log. + + Rather than use actual contextual information, we just use random + data in this demo. + """ + + USERS = ['jim', 'fred', 'sheila'] + IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1'] + + def filter(self, record): + + record.ip = choice(ContextFilter.IPS) + record.user = choice(ContextFilter.USERS) + return True + + if __name__ == "__main__": + levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) + a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"), + { "ip" : "123.231.231.123", "user" : "sheila" }) + logging.basicConfig(level=logging.DEBUG, + format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s") + a1 = logging.getLogger("a.b.c") + a2 = logging.getLogger("d.e.f") + + f = ContextFilter() + a1.addFilter(f) + a2.addFilter(f) + a1.debug("A debug message") + a1.info("An info message with %s", "some parameters") + for x in range(10): + lvl = choice(levels) + lvlname = logging.getLevelName(lvl) + a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters") + +which, when run, produces something like:: + + 2010-09-06 22:38:15,292 a.b.c DEBUG IP: 123.231.231.123 User: fred A debug message + 2010-09-06 22:38:15,300 a.b.c INFO IP: 192.168.0.1 User: sheila An info message with some parameters + 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f ERROR IP: 127.0.0.1 User: jim A message at ERROR level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f DEBUG IP: 127.0.0.1 User: sheila A message at DEBUG level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f ERROR IP: 123.231.231.123 User: fred A message at ERROR level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 192.168.0.1 User: jim A message at CRITICAL level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f DEBUG IP: 192.168.0.1 User: jim A message at DEBUG level with 2 parameters + 2010-09-06 22:38:15,301 d.e.f ERROR IP: 127.0.0.1 User: sheila A message at ERROR level with 2 parameters + 2010-09-06 22:38:15,301 d.e.f DEBUG IP: 123.231.231.123 User: fred A message at DEBUG level with 2 parameters + 2010-09-06 22:38:15,301 d.e.f INFO IP: 123.231.231.123 User: fred A message at INFO level with 2 parameters + + .. _multiple-processes: Logging to a single file from multiple processes Modified: python/branches/release27-maint/Doc/library/logging.rst ============================================================================== --- python/branches/release27-maint/Doc/library/logging.rst (original) +++ python/branches/release27-maint/Doc/library/logging.rst Tue Sep 7 00:18:20 2010 @@ -1335,6 +1335,10 @@ be hard to manage if the number of :class:`Logger` instances becomes effectively unbounded. + +Using LoggerAdapters to impart contextual information +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + An easy way in which you can pass contextual information to be output along with logging event information is to use the :class:`LoggerAdapter` class. This class is designed to look like a :class:`Logger`, so that you can call @@ -1442,6 +1446,78 @@ The :class:`LoggerAdapter` class was not present in previous versions. +Using Filters to impart contextual information +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can also add contextual information to log output using a user-defined +:class:`Filter`. ``Filter`` instances are allowed to modify the ``LogRecords`` +passed to them, including adding additional attributes which can then be output +using a suitable format string, or if needed a custom :class:`Formatter`. + +For example in a web application, the request being processed (or at least, +the interesting parts of it) can be stored in a threadlocal +(:class:`threading.local`) variable, and then accessed from a ``Filter`` to +add, say, information from the request - say, the remote IP address and remote +user's username - to the ``LogRecord``, using the attribute names 'ip' and +'user' as in the ``LoggerAdapter`` example above. In that case, the same format +string can be used to get similar output to that shown above. Here's an example +script:: + + import logging + from random import choice + + class ContextFilter(logging.Filter): + """ + This is a filter which injects contextual information into the log. + + Rather than use actual contextual information, we just use random + data in this demo. + """ + + USERS = ['jim', 'fred', 'sheila'] + IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1'] + + def filter(self, record): + + record.ip = choice(ContextFilter.IPS) + record.user = choice(ContextFilter.USERS) + return True + + if __name__ == "__main__": + levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) + a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"), + { "ip" : "123.231.231.123", "user" : "sheila" }) + logging.basicConfig(level=logging.DEBUG, + format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s") + a1 = logging.getLogger("a.b.c") + a2 = logging.getLogger("d.e.f") + + f = ContextFilter() + a1.addFilter(f) + a2.addFilter(f) + a1.debug("A debug message") + a1.info("An info message with %s", "some parameters") + for x in range(10): + lvl = choice(levels) + lvlname = logging.getLevelName(lvl) + a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters") + +which, when run, produces something like:: + + 2010-09-06 22:38:15,292 a.b.c DEBUG IP: 123.231.231.123 User: fred A debug message + 2010-09-06 22:38:15,300 a.b.c INFO IP: 192.168.0.1 User: sheila An info message with some parameters + 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f ERROR IP: 127.0.0.1 User: jim A message at ERROR level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f DEBUG IP: 127.0.0.1 User: sheila A message at DEBUG level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f ERROR IP: 123.231.231.123 User: fred A message at ERROR level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 192.168.0.1 User: jim A message at CRITICAL level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f DEBUG IP: 192.168.0.1 User: jim A message at DEBUG level with 2 parameters + 2010-09-06 22:38:15,301 d.e.f ERROR IP: 127.0.0.1 User: sheila A message at ERROR level with 2 parameters + 2010-09-06 22:38:15,301 d.e.f DEBUG IP: 123.231.231.123 User: fred A message at DEBUG level with 2 parameters + 2010-09-06 22:38:15,301 d.e.f INFO IP: 123.231.231.123 User: fred A message at INFO level with 2 parameters + + .. _multiple-processes: Logging to a single file from multiple processes Modified: python/branches/release31-maint/Doc/library/logging.rst ============================================================================== --- python/branches/release31-maint/Doc/library/logging.rst (original) +++ python/branches/release31-maint/Doc/library/logging.rst Tue Sep 7 00:18:20 2010 @@ -246,16 +246,16 @@ methods listed above, but this is how to log at custom log levels. :func:`getLogger` returns a reference to a logger instance with the specified -if it it is provided, or ``root`` if not. The names are period-separated +name if it is provided, or ``root`` if not. The names are period-separated hierarchical structures. Multiple calls to :func:`getLogger` with the same name will return a reference to the same logger object. 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 children of ``foo``. -Child loggers propagate messages up to their parent loggers. Because of this, -it is unnecessary to define and configure all the loggers an application uses. -It is sufficient to configure a top-level logger and create child loggers as -needed. +``foo.bar``, ``foo.bar.baz``, and ``foo.bam`` are all descendants of ``foo``. +Child loggers propagate messages up to the handlers associated with their +ancestor loggers. Because of this, it is unnecessary to define and configure +handlers for all the loggers an application uses. It is sufficient to +configure handlers for a top-level logger and create child loggers as needed. Handlers @@ -283,15 +283,16 @@ are there two :func:`setLevel` methods? The level set in the logger determines which severity of messages it will pass to its handlers. The level set in each handler determines which messages that handler will send on. - :func:`setFormatter` selects a Formatter object for this handler to use. + +* :func:`setFormatter` selects a Formatter object for this handler to use. * :func:`addFilter` and :func:`removeFilter` respectively configure and deconfigure filter objects on handlers. -Application code should not directly instantiate and use handlers. Instead, the -:class:`Handler` class is a base class that defines the interface that all -Handlers should have and establishes some default behavior that child classes -can use (or override). +Application code should not directly instantiate and use instances of +:class:`Handler`. Instead, the :class:`Handler` class is a base class that +defines the interface that all handlers should have and establishes some +default behavior that child classes can use (or override). Formatters @@ -318,6 +319,14 @@ "%(asctime)s - %(levelname)s - %(message)s" +Formatters use a user-configurable function to convert the creation time of a +record to a tuple. By default, :func:`time.localtime` is used; to change this +for a particular formatter instance, set the ``converter`` attribute of the +instance to a function with the same signature as :func:`time.localtime` or +:func:`time.gmtime`. To change it for all formatters, for example if you want +all logging times to be shown in GMT, set the ``converter`` attribute in the +Formatter class (to ``time.gmtime`` for GMT display). + Configuring Logging ^^^^^^^^^^^^^^^^^^^ @@ -464,9 +473,8 @@ just "foo". .. versionadded:: 3.1 - -The :class:`NullHandler` class was not present in previous versions, but is now -included, so that it need not be defined in library code. + The :class:`NullHandler` class was not present in previous versions, but is + now included, so that it need not be defined in library code. @@ -516,7 +524,9 @@ can have zero, one or more handlers associated with it (via the :meth:`addHandler` method of :class:`Logger`). In addition to any handlers directly associated with a logger, *all handlers associated with all ancestors -of the logger* are called to dispatch the message. +of the logger* are called to dispatch the message (unless the *propagate* flag +for a logger is set to a false value, at which point the passing to ancestor +handlers stops). Just as for loggers, handlers can have levels associated with them. A handler's level acts as a filter in the same way as a logger's level does. If a handler @@ -729,7 +739,11 @@ Provides an overriding level *lvl* for all loggers which takes precedence over the logger's own level. When the need arises to temporarily throttle logging - output down across the whole application, this function can be useful. + output down across the whole application, this function can be useful. Its + effect is to disable all logging calls of severity *lvl* and below, so that + if you call it with a value of INFO, then all INFO and DEBUG events would be + discarded, whereas those of severity WARNING and above would be processed + according to the logger's effective level. .. function:: addLevelName(lvl, levelName) @@ -765,11 +779,13 @@ Does basic configuration for the logging system by creating a :class:`StreamHandler` with a default :class:`Formatter` and adding it to the - root logger. The function does nothing if any handlers have been defined for - the root logger. The functions :func:`debug`, :func:`info`, :func:`warning`, + root logger. The functions :func:`debug`, :func:`info`, :func:`warning`, :func:`error` and :func:`critical` will call :func:`basicConfig` automatically if no handlers are defined for the root logger. + This function does nothing if the root logger already has handlers + configured for it. + The following keyword arguments are supported. +--------------+---------------------------------------------+ @@ -838,8 +854,8 @@ .. attribute:: Logger.propagate If this evaluates to false, logging messages are not passed by this logger or by - child loggers to higher level (ancestor) loggers. The constructor sets this - attribute to 1. + its child loggers to the handlers of higher level (ancestor) loggers. The + constructor sets this attribute to 1. .. method:: Logger.setLevel(lvl) @@ -1195,6 +1211,28 @@ This example uses console and file handlers, but you can use any number and combination of handlers you choose. +.. _logging-exceptions: + +Exceptions raised during logging +-------------------------------- + +The logging package is designed to swallow exceptions which occur while logging +in production. This is so that errors which occur while handling logging events +- such as logging misconfiguration, network or other similar errors - do not +cause the application using logging to terminate prematurely. + +:class:`SystemExit` and :class:`KeyboardInterrupt` exceptions are never +swallowed. Other exceptions which occur during the :meth:`emit` method of a +:class:`Handler` subclass are passed to its :meth:`handleError` method. + +The default implementation of :meth:`handleError` in :class:`Handler` checks +to see if a module-level variable, :data:`raiseExceptions`, is set. If set, a +traceback is printed to :data:`sys.stderr`. If not set, the exception is swallowed. + +**Note:** The default value of :data:`raiseExceptions` is ``True``. This is because +during development, you typically want to be notified of any exceptions that +occur. It's advised that you set :data:`raiseExceptions` to ``False`` for production +usage. .. _context-info: @@ -1214,6 +1252,10 @@ be hard to manage if the number of :class:`Logger` instances becomes effectively unbounded. + +Using LoggerAdapters to impart contextual information +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + An easy way in which you can pass contextual information to be output along with logging event information is to use the :class:`LoggerAdapter` class. This class is designed to look like a :class:`Logger`, so that you can call @@ -1318,6 +1360,104 @@ 2008-01-18 14:49:54,033 d.e.f WARNING IP: 127.0.0.1 User: jim A message at WARNING level with 2 parameters +Using Filters to impart contextual information +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can also add contextual information to log output using a user-defined +:class:`Filter`. ``Filter`` instances are allowed to modify the ``LogRecords`` +passed to them, including adding additional attributes which can then be output +using a suitable format string, or if needed a custom :class:`Formatter`. + +For example in a web application, the request being processed (or at least, +the interesting parts of it) can be stored in a threadlocal +(:class:`threading.local`) variable, and then accessed from a ``Filter`` to +add, say, information from the request - say, the remote IP address and remote +user's username - to the ``LogRecord``, using the attribute names 'ip' and +'user' as in the ``LoggerAdapter`` example above. In that case, the same format +string can be used to get similar output to that shown above. Here's an example +script:: + + import logging + from random import choice + + class ContextFilter(logging.Filter): + """ + This is a filter which injects contextual information into the log. + + Rather than use actual contextual information, we just use random + data in this demo. + """ + + USERS = ['jim', 'fred', 'sheila'] + IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1'] + + def filter(self, record): + + record.ip = choice(ContextFilter.IPS) + record.user = choice(ContextFilter.USERS) + return True + + if __name__ == "__main__": + levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) + a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"), + { "ip" : "123.231.231.123", "user" : "sheila" }) + logging.basicConfig(level=logging.DEBUG, + format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s") + a1 = logging.getLogger("a.b.c") + a2 = logging.getLogger("d.e.f") + + f = ContextFilter() + a1.addFilter(f) + a2.addFilter(f) + a1.debug("A debug message") + a1.info("An info message with %s", "some parameters") + for x in range(10): + lvl = choice(levels) + lvlname = logging.getLevelName(lvl) + a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters") + +which, when run, produces something like:: + + 2010-09-06 22:38:15,292 a.b.c DEBUG IP: 123.231.231.123 User: fred A debug message + 2010-09-06 22:38:15,300 a.b.c INFO IP: 192.168.0.1 User: sheila An info message with some parameters + 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f ERROR IP: 127.0.0.1 User: jim A message at ERROR level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f DEBUG IP: 127.0.0.1 User: sheila A message at DEBUG level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f ERROR IP: 123.231.231.123 User: fred A message at ERROR level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 192.168.0.1 User: jim A message at CRITICAL level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters + 2010-09-06 22:38:15,300 d.e.f DEBUG IP: 192.168.0.1 User: jim A message at DEBUG level with 2 parameters + 2010-09-06 22:38:15,301 d.e.f ERROR IP: 127.0.0.1 User: sheila A message at ERROR level with 2 parameters + 2010-09-06 22:38:15,301 d.e.f DEBUG IP: 123.231.231.123 User: fred A message at DEBUG level with 2 parameters + 2010-09-06 22:38:15,301 d.e.f INFO IP: 123.231.231.123 User: fred A message at INFO level with 2 parameters + + +.. _multiple-processes: + +Logging to a single file from multiple processes +------------------------------------------------ + +Although logging is thread-safe, and logging to a single file from multiple +threads in a single process *is* supported, logging to a single file from +*multiple processes* is *not* supported, because there is no standard way to +serialize access to a single file across multiple processes in Python. If you +need to log to a single file from multiple processes, the best way of doing +this is to have all the processes log to a :class:`SocketHandler`, and have a +separate process which implements a socket server which reads from the socket +and logs to file. (If you prefer, you can dedicate one thread in one of the +existing processes to perform this function.) The following section documents +this approach in more detail and includes a working socket receiver which can +be used as a starting point for you to adapt in your own applications. + +If you are using a recent version of Python which includes the +:mod:`multiprocessing` module, you can write your own handler which uses the +:class:`Lock` class from this module to serialize access to the file from +your processes. The existing :class:`FileHandler` and subclasses do not make +use of :mod:`multiprocessing` at present, though they may do so in the future. +Note that at present, the :mod:`multiprocessing` module does not provide +working lock functionality on all platforms (see +http://bugs.python.org/issue3770). + .. _network-logging: @@ -1449,6 +1589,11 @@ 69 myapp.area2 WARNING Jail zesty vixen who grabbed pay from quack. 69 myapp.area2 ERROR The five boxing wizards jump quickly. +Note that there are some security issues with pickle in some scenarios. If +these affect you, you can use an alternative serialization scheme by overriding +the :meth:`makePickle` method and implementing your alternative there, as +well as adapting the above script to use your alternative serialization. + Using arbitrary objects as messages ----------------------------------- @@ -1604,6 +1749,8 @@ :exc:`NotImplementedError`. +.. _stream-handler: + StreamHandler ^^^^^^^^^^^^^ @@ -1637,6 +1784,8 @@ no output, so an explicit :meth:`flush` call may be needed at times. +.. _file-handler: + FileHandler ^^^^^^^^^^^ @@ -1663,6 +1812,7 @@ Outputs the record to the file. +.. _null-handler: NullHandler ^^^^^^^^^^^ @@ -1686,6 +1836,8 @@ See :ref:`library-config` for more information on how to use :class:`NullHandler`. +.. _watched-file-handler: + WatchedFileHandler ^^^^^^^^^^^^^^^^^^ @@ -1724,6 +1876,7 @@ changed. If it has, the existing stream is flushed and closed and the file opened again, before outputting the record to the file. +.. _rotating-file-handler: RotatingFileHandler ^^^^^^^^^^^^^^^^^^^ @@ -1764,6 +1917,7 @@ Outputs the record to the file, catering for rollover as described previously. +.. _timed-rotating-file-handler: TimedRotatingFileHandler ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1811,6 +1965,9 @@ one is deleted. The deletion logic uses the interval to determine which files to delete, so changing the interval may leave old files lying around. + If *delay* is true, then file opening is deferred until the first call to + :meth:`emit`. + .. method:: doRollover() @@ -1822,6 +1979,8 @@ Outputs the record to the file, catering for rollover as described above. +.. _socket-handler: + SocketHandler ^^^^^^^^^^^^^ @@ -1868,6 +2027,11 @@ Pickles the record's attribute dictionary in binary format with a length prefix, and returns it ready for transmission across the socket. + Note that pickles aren't completely secure. If you are concerned about + security, you may want to override this method to implement a more secure + mechanism. For example, you can sign pickles using HMAC and then verify + them on the receiving end, or alternatively you can disable unpickling of + global objects on the receiving end. .. method:: send(packet) @@ -1875,6 +2039,8 @@ partial sends which can happen when the network is busy. +.. _datagram-handler: + DatagramHandler ^^^^^^^^^^^^^^^ @@ -1908,6 +2074,8 @@ Send a pickled string to a socket. +.. _syslog-handler: + SysLogHandler ^^^^^^^^^^^^^ @@ -1944,6 +2112,87 @@ or integers - if strings are passed, internal mapping dictionaries are used to convert them to integers. + The symbolic ``LOG_`` values are defined in :class:`SysLogHandler` and + mirror the values defined in the ``sys/syslog.h`` header file. + + **Priorities** + + +--------------------------+---------------+ + | Name (string) | Symbolic value| + +==========================+===============+ + | ``alert`` | LOG_ALERT | + +--------------------------+---------------+ + | ``crit`` or ``critical`` | LOG_CRIT | + +--------------------------+---------------+ + | ``debug`` | LOG_DEBUG | + +--------------------------+---------------+ + | ``emerg`` or ``panic`` | LOG_EMERG | + +--------------------------+---------------+ + | ``err`` or ``error`` | LOG_ERR | + +--------------------------+---------------+ + | ``info`` | LOG_INFO | + +--------------------------+---------------+ + | ``notice`` | LOG_NOTICE | + +--------------------------+---------------+ + | ``warn`` or ``warning`` | LOG_WARNING | + +--------------------------+---------------+ + + **Facilities** + + +---------------+---------------+ + | Name (string) | Symbolic value| + +===============+===============+ + | ``auth`` | LOG_AUTH | + +---------------+---------------+ + | ``authpriv`` | LOG_AUTHPRIV | + +---------------+---------------+ + | ``cron`` | LOG_CRON | + +---------------+---------------+ + | ``daemon`` | LOG_DAEMON | + +---------------+---------------+ + | ``ftp`` | LOG_FTP | + +---------------+---------------+ + | ``kern`` | LOG_KERN | + +---------------+---------------+ + | ``lpr`` | LOG_LPR | + +---------------+---------------+ + | ``mail`` | LOG_MAIL | + +---------------+---------------+ + | ``news`` | LOG_NEWS | + +---------------+---------------+ + | ``syslog`` | LOG_SYSLOG | + +---------------+---------------+ + | ``user`` | LOG_USER | + +---------------+---------------+ + | ``uucp`` | LOG_UUCP | + +---------------+---------------+ + | ``local0`` | LOG_LOCAL0 | + +---------------+---------------+ + | ``local1`` | LOG_LOCAL1 | + +---------------+---------------+ + | ``local2`` | LOG_LOCAL2 | + +---------------+---------------+ + | ``local3`` | LOG_LOCAL3 | + +---------------+---------------+ + | ``local4`` | LOG_LOCAL4 | + +---------------+---------------+ + | ``local5`` | LOG_LOCAL5 | + +---------------+---------------+ + | ``local6`` | LOG_LOCAL6 | + +---------------+---------------+ + | ``local7`` | LOG_LOCAL7 | + +---------------+---------------+ + + .. method:: mapPriority(levelname) + + Maps a logging level name to a syslog priority name. + You may need to override this if you are using custom levels, or + if the default algorithm is not suitable for your needs. The + default algorithm maps ``DEBUG``, ``INFO``, ``WARNING``, ``ERROR`` and + ``CRITICAL`` to the equivalent syslog names, and all other level + names to "warning". + +.. _nt-eventlog-handler: NTEventLogHandler ^^^^^^^^^^^^^^^^^ @@ -2010,6 +2259,7 @@ lookup to get the message ID. This version returns 1, which is the base message ID in :file:`win32service.pyd`. +.. _smtp-handler: SMTPHandler ^^^^^^^^^^^ @@ -2038,6 +2288,7 @@ If you want to specify a subject line which is record-dependent, override this method. +.. _memory-handler: MemoryHandler ^^^^^^^^^^^^^ @@ -2108,6 +2359,8 @@ Checks for buffer full or a record at the *flushLevel* or higher. +.. _http-handler: + HTTPHandler ^^^^^^^^^^^ @@ -2250,6 +2503,7 @@ just uses :func:`traceback.print_exception`. The resulting string is returned. +.. _filter: Filter Objects -------------- @@ -2275,6 +2529,14 @@ yes. If deemed appropriate, the record may be modified in-place by this method. +Note that filters attached to handlers are consulted whenever an event is +emitted by the handler, whereas filters attached to loggers are consulted +whenever an event is logged to the handler (using :meth:`debug`, :meth:`info`, +etc.) This means that events which have been generated by descendant loggers +will not be filtered by a logger's filter setting, unless the filter has also +been applied to those descendant loggers. + +.. _log-record: LogRecord Objects ----------------- @@ -2306,6 +2568,7 @@ Returns the message for this :class:`LogRecord` instance after merging any user-supplied arguments with the message. +.. _logger-adapter: LoggerAdapter Objects --------------------- From python-checkins at python.org Tue Sep 7 00:23:13 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Tue, 7 Sep 2010 00:23:13 +0200 (CEST) Subject: [Python-checkins] r84570 - python/branches/py3k/Lib/_pyio.py Message-ID: <20100906222313.CDB0AEE9E7@mail.python.org> Author: amaury.forgeotdarc Date: Tue Sep 7 00:23:13 2010 New Revision: 84570 Log: Change docstrings to match the implementation Modified: python/branches/py3k/Lib/_pyio.py Modified: python/branches/py3k/Lib/_pyio.py ============================================================================== --- python/branches/py3k/Lib/_pyio.py (original) +++ python/branches/py3k/Lib/_pyio.py Tue Sep 7 00:23:13 2010 @@ -358,13 +358,13 @@ def seekable(self) -> bool: """Return whether object supports random access. - If False, seek(), tell() and truncate() will raise IOError. + If False, seek(), tell() and truncate() will raise UnsupportedOperation. This method may need to do a test seek(). """ return False def _checkSeekable(self, msg=None): - """Internal: raise an IOError if file is not seekable + """Internal: raise UnsupportedOperation if file is not seekable """ if not self.seekable(): raise UnsupportedOperation("File or stream is not seekable." @@ -373,12 +373,12 @@ def readable(self) -> bool: """Return whether object was opened for reading. - If False, read() will raise IOError. + If False, read() will raise UnsupportedOperation. """ return False def _checkReadable(self, msg=None): - """Internal: raise an IOError if file is not readable + """Internal: raise UnsupportedOperation if file is not readable """ if not self.readable(): raise UnsupportedOperation("File or stream is not readable." @@ -387,12 +387,12 @@ def writable(self) -> bool: """Return whether object was opened for writing. - If False, write() and truncate() will raise IOError. + If False, write() and truncate() will raise UnsupportedOperation. """ return False def _checkWritable(self, msg=None): - """Internal: raise an IOError if file is not writable + """Internal: raise UnsupportedOperation if file is not writable """ if not self.writable(): raise UnsupportedOperation("File or stream is not writable." From python-checkins at python.org Tue Sep 7 00:31:52 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Tue, 7 Sep 2010 00:31:52 +0200 (CEST) Subject: [Python-checkins] r84571 - in python/branches/py3k: Lib/_pyio.py Modules/_io/iobase.c Message-ID: <20100906223152.43E9BC48D@mail.python.org> Author: amaury.forgeotdarc Date: Tue Sep 7 00:31:52 2010 New Revision: 84571 Log: More docstring updates Modified: python/branches/py3k/Lib/_pyio.py python/branches/py3k/Modules/_io/iobase.c Modified: python/branches/py3k/Lib/_pyio.py ============================================================================== --- python/branches/py3k/Lib/_pyio.py (original) +++ python/branches/py3k/Lib/_pyio.py Tue Sep 7 00:31:52 2010 @@ -264,7 +264,8 @@ Even though IOBase does not declare read, readinto, or write because their signatures will vary, implementations and clients should consider those methods part of the interface. Also, implementations - may raise a IOError when operations they do not support are called. + may raise UnsupportedOperation when operations they do not support are + called. The basic type used for binary data read from or written to a file is bytes. bytearrays are accepted too, and in some cases (such as Modified: python/branches/py3k/Modules/_io/iobase.c ============================================================================== --- python/branches/py3k/Modules/_io/iobase.c (original) +++ python/branches/py3k/Modules/_io/iobase.c Tue Sep 7 00:31:52 2010 @@ -35,7 +35,8 @@ "Even though IOBase does not declare read, readinto, or write because\n" "their signatures will vary, implementations and clients should\n" "consider those methods part of the interface. Also, implementations\n" - "may raise a IOError when operations they do not support are called.\n" + "may raise UnsupportedOperation when operations they do not support are\n" + "called.\n" "\n" "The basic type used for binary data read from or written to a file is\n" "bytes. bytearrays are accepted too, and in some cases (such as\n" @@ -300,7 +301,7 @@ PyDoc_STRVAR(iobase_seekable_doc, "Return whether object supports random access.\n" "\n" - "If False, seek(), tell() and truncate() will raise IOError.\n" + "If False, seek(), tell() and truncate() will raise UnsupportedOperation.\n" "This method may need to do a test seek()."); static PyObject * @@ -329,7 +330,7 @@ PyDoc_STRVAR(iobase_readable_doc, "Return whether object was opened for reading.\n" "\n" - "If False, read() will raise IOError."); + "If False, read() will raise UnsupportedOperation."); static PyObject * iobase_readable(PyObject *self, PyObject *args) @@ -358,7 +359,7 @@ PyDoc_STRVAR(iobase_writable_doc, "Return whether object was opened for writing.\n" "\n" - "If False, read() will raise IOError."); + "If False, write() will raise UnsupportedOperation."); static PyObject * iobase_writable(PyObject *self, PyObject *args) From python-checkins at python.org Tue Sep 7 00:54:24 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 00:54:24 +0200 (CEST) Subject: [Python-checkins] r84572 - python/branches/release27-maint/Lib/random.py Message-ID: <20100906225424.28AB2EE9ED@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 00:54:24 2010 New Revision: 84572 Log: Remove outdated XXX comment. Mapping support was removed for 3.x. Modified: python/branches/release27-maint/Lib/random.py Modified: python/branches/release27-maint/Lib/random.py ============================================================================== --- python/branches/release27-maint/Lib/random.py (original) +++ python/branches/release27-maint/Lib/random.py Tue Sep 7 00:54:24 2010 @@ -292,15 +292,6 @@ large population: sample(xrange(10000000), 60) """ - # XXX Although the documentation says `population` is "a sequence", - # XXX attempts are made to cater to any iterable with a __len__ - # XXX method. This has had mixed success. Examples from both - # XXX sides: sets work fine, and should become officially supported; - # XXX dicts are much harder, and have failed in various subtle - # XXX ways across attempts. Support for mapping types should probably - # XXX be dropped (and users should pass mapping.keys() or .values() - # XXX explicitly). - # Sampling without replacement entails tracking either potential # selections (the pool) in a list or previous selections in a set. From python-checkins at python.org Tue Sep 7 01:36:31 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 01:36:31 +0200 (CEST) Subject: [Python-checkins] r84573 - in python/branches/py3k: Doc/library/random.rst Misc/NEWS Message-ID: <20100906233631.7F9DCFBFF@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 01:36:31 2010 New Revision: 84573 Log: Document which part of the random module module are guaranteed. Modified: python/branches/py3k/Doc/library/random.rst python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/random.rst ============================================================================== --- python/branches/py3k/Doc/library/random.rst (original) +++ python/branches/py3k/Doc/library/random.rst Tue Sep 7 01:36:31 2010 @@ -270,3 +270,19 @@ `_ for a compatible alternative random number generator with a long period and comparatively simple update operations. + +Notes on Reproducibility +======================== + +Sometimes it is useful to be able to reproduce the sequences given by a pseudo +random number generator. By re-using a seed value, the same sequence should be +reproducible from run to run as long as multiple threads are not running. + +Most of the random module's algorithms and seeding functions are subject to +change across Python versions, but two aspects are guaranteed not to change: + +* If a new seeding method is added, then a backward compatible seeder will be + offered. + +* The generator's :meth:`random` method will continue to produce the same + sequence when the compatible seeder is given the same seed. Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 7 01:36:31 2010 @@ -13,6 +13,11 @@ Library ------- +- Updates to the random module: + + * Document which parts of the module are guaranteed to stay the same + across versions and which parts are subject to change. + - collections.OrderedDict now supports a new method for repositioning keys to either end. From python-checkins at python.org Tue Sep 7 02:38:15 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 02:38:15 +0200 (CEST) Subject: [Python-checkins] r84574 - in python/branches/py3k: Doc/library/random.rst Lib/random.py Lib/test/test_random.py Misc/NEWS Message-ID: <20100907003815.6BB04EE98A@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 02:38:15 2010 New Revision: 84574 Log: Document which part of the random module module are guaranteed. Modified: python/branches/py3k/Doc/library/random.rst python/branches/py3k/Lib/random.py python/branches/py3k/Lib/test/test_random.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/random.rst ============================================================================== --- python/branches/py3k/Doc/library/random.rst (original) +++ python/branches/py3k/Doc/library/random.rst Tue Sep 7 02:38:15 2010 @@ -51,18 +51,23 @@ Bookkeeping functions: -.. function:: seed([x]) +.. function:: seed([x], version=2) - Initialize the basic random number generator. Optional argument *x* can be any - :term:`hashable` object. If *x* is omitted or ``None``, current system time is used; - current system time is also used to initialize the generator when the module is - first imported. If randomness sources are provided by the operating system, - they are used instead of the system time (see the :func:`os.urandom` function - for details on availability). + Initialize the random number generator. - If *x* is not ``None`` or an int, ``hash(x)`` is used instead. If *x* is an - int, *x* is used directly. + If *x* is omitted or ``None``, the current system time is used. If + randomness sources are provided by the operating system, they are used + instead of the system time (see the :func:`os.urandom` function for details + on availability). + If *x* is an int, it is used directly. + + With version 2 (the default), a :class:`str`, :class:`bytes`, or :class:`bytearray` + object gets converted to a :class:`int` and all of its bits are used. With version 1, + the :func:`hash` of *x* is used instead. + + .. versionchanged:: 3.2 + Moved to the version 2 scheme which uses all of the bits in a string seed. .. function:: getstate() Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Tue Sep 7 02:38:15 2010 @@ -91,13 +91,17 @@ self.seed(x) self.gauss_next = None - def seed(self, a=None): + def seed(self, a=None, version=2): """Initialize internal state from hashable object. None or no argument seeds from current time or from an operating system specific randomness source if available. - If a is not None or an int, hash(a) is used instead. + For version 2 (the default), all of the bits are used if a is a str, + bytes, or bytearray. For version 1, the hash() of a is used instead. + + If a is an int, all bits are used. + """ if a is None: @@ -107,6 +111,11 @@ import time a = int(time.time() * 256) # use fractional seconds + if version == 2 and isinstance(a, (str, bytes, bytearray)): + if isinstance(a, str): + a = a.encode("utf8") + a = int(_hexlify(a), 16) + super().seed(a) self.gauss_next = None Modified: python/branches/py3k/Lib/test/test_random.py ============================================================================== --- python/branches/py3k/Lib/test/test_random.py (original) +++ python/branches/py3k/Lib/test/test_random.py Tue Sep 7 02:38:15 2010 @@ -39,7 +39,7 @@ self.gen.seed(arg) for arg in [list(range(3)), dict(one=1)]: self.assertRaises(TypeError, self.gen.seed, arg) - self.assertRaises(TypeError, self.gen.seed, 1, 2) + self.assertRaises(TypeError, self.gen.seed, 1, 2, 3, 4) self.assertRaises(TypeError, type(self.gen), []) def test_sample(self): @@ -223,6 +223,21 @@ class MersenneTwister_TestBasicOps(TestBasicOps): gen = random.Random() + def test_guaranteed_stable(self): + # These sequences are guaranteed to stay the same across versions of python + self.gen.seed(3456147, version=1) + self.assertEqual([self.gen.random().hex() for i in range(4)], + ['0x1.ac362300d90d2p-1', '0x1.9d16f74365005p-1', + '0x1.1ebb4352e4c4dp-1', '0x1.1a7422abf9c11p-1']) + self.gen.seed("the quick brown fox", version=1) + self.assertEqual([self.gen.random().hex() for i in range(4)], + ['0x1.9ee265c177cdep-2', '0x1.bad51092e3c25p-1', + '0x1.85ff833f71576p-1', '0x1.87efb37462927p-1']) + self.gen.seed("the quick brown fox", version=2) + self.assertEqual([self.gen.random().hex() for i in range(4)], + ['0x1.1294009b9eda4p-2', '0x1.2ff96171b0010p-1', + '0x1.459e0989bd8e0p-5', '0x1.8b5f55892ddcbp-1']) + def test_setstate_first_arg(self): self.assertRaises(ValueError, self.gen.setstate, (1, None, None)) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 7 02:38:15 2010 @@ -18,6 +18,9 @@ * Document which parts of the module are guaranteed to stay the same across versions and which parts are subject to change. + * Update the seed() method to use all of the bits in a string + instead of just the hash value. + - collections.OrderedDict now supports a new method for repositioning keys to either end. From python-checkins at python.org Tue Sep 7 02:48:40 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 02:48:40 +0200 (CEST) Subject: [Python-checkins] r84575 - python/branches/py3k/Lib/random.py Message-ID: <20100907004840.BA9A3EE999@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 02:48:40 2010 New Revision: 84575 Log: Minor code cleanup Modified: python/branches/py3k/Lib/random.py Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Tue Sep 7 02:48:40 2010 @@ -161,13 +161,13 @@ ## -------------------- integer methods ------------------- - def randrange(self, start, stop=None, step=1, int=int, default=None, - maxwidth=1< 0: if istart >= maxwidth: return self._randbelow(istart) From solipsis at pitrou.net Tue Sep 7 05:02:25 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 07 Sep 2010 05:02:25 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84575): sum=0 Message-ID: py3k results for svn r84575 (hg cset 70b80c31a453) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflog7ZRPx5', '-x'] From python-checkins at python.org Tue Sep 7 06:44:52 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 06:44:52 +0200 (CEST) Subject: [Python-checkins] r84576 - in python/branches/py3k: Doc/library/random.rst Lib/random.py Lib/test/test_random.py Misc/NEWS Message-ID: <20100907044452.5B0EEFAAC@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 06:44:52 2010 New Revision: 84576 Log: Issues #7889, #9025 and #9379: Improvements to the random module. Modified: python/branches/py3k/Doc/library/random.rst python/branches/py3k/Lib/random.py python/branches/py3k/Lib/test/test_random.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/random.rst ============================================================================== --- python/branches/py3k/Doc/library/random.rst (original) +++ python/branches/py3k/Doc/library/random.rst Tue Sep 7 06:44:52 2010 @@ -98,6 +98,13 @@ equivalent to ``choice(range(start, stop, step))``, but doesn't actually build a range object. + The positional argument pattern matches that of :func:`range`. Keyword arguments + should not be used because the function may use them in unexpected ways. + + .. versionchanged:: 3.2 + :meth:`randrange` is more sophisticated about producing equally distributed + values. Formerly it used a style like ``int(random()*n)`` which could produce + slightly uneven distributions. .. function:: randint(a, b) Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Tue Sep 7 06:44:52 2010 @@ -161,7 +161,7 @@ ## -------------------- integer methods ------------------- - def randrange(self, start, stop=None, step=1, int=int, maxwidth=1< 0: - if istart >= maxwidth: - return self._randbelow(istart) - return int(self.random() * istart) + return self._randbelow(istart) raise ValueError("empty range for randrange()") # stop argument supplied. @@ -201,9 +199,7 @@ # a long, but we're supposed to return an int (for backward # compatibility). - if width >= maxwidth: - return int(istart + self._randbelow(width)) - return int(istart + int(self.random()*width)) + return int(istart + self._randbelow(width)) if step == 1: raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width)) @@ -221,9 +217,7 @@ if n <= 0: raise ValueError("empty range for randrange()") - if n >= maxwidth: - return istart + istep*self._randbelow(n) - return istart + istep*int(self.random() * n) + return istart + istep*self._randbelow(n) def randint(self, a, b): """Return random integer in range [a, b], including both end points. @@ -231,7 +225,7 @@ return self.randrange(a, b+1) - def _randbelow(self, n, _log=_log, int=int, _maxwidth=1< n-1 > 2**(k-2) - r = getrandbits(k) + k = n.bit_length() # don't use (n-1) here because n can be 1 + r = getrandbits(k) # 0 <= r < 2**k while r >= n: r = getrandbits(k) return r @@ -262,7 +256,7 @@ def choice(self, seq): """Choose a random element from a non-empty sequence.""" - return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty + return seq[self._randbelow(len(seq))] # raises IndexError if seq is empty def shuffle(self, x, random=None, int=int): """x, random=random.random -> shuffle list x in place; return None. @@ -272,11 +266,15 @@ """ if random is None: - random = self.random - for i in reversed(range(1, len(x))): - # pick an element in x[:i+1] with which to exchange x[i] - j = int(random() * (i+1)) - x[i], x[j] = x[j], x[i] + for i in reversed(range(1, len(x))): + # pick an element in x[:i+1] with which to exchange x[i] + j = self._randbelow(i+1) + x[i], x[j] = x[j], x[i] + else: + for i in reversed(range(1, len(x))): + # pick an element in x[:i+1] with which to exchange x[i] + j = int(random() * (i+1)) + x[i], x[j] = x[j], x[i] def sample(self, population, k): """Chooses k unique random elements from a population sequence or set. @@ -314,7 +312,6 @@ n = len(population) if not 0 <= k <= n: raise ValueError("Sample larger than population") - _int = int result = [None] * k setsize = 21 # size of a small set minus size of an empty list if k > 5: @@ -323,16 +320,16 @@ # An n-length list is smaller than a k-length set pool = list(population) for i in range(k): # invariant: non-selected at [0,n-i) - j = _int(random() * (n-i)) + j = self._randbelow(n-i) result[i] = pool[j] pool[j] = pool[n-i-1] # move non-selected item into vacancy else: selected = set() selected_add = selected.add for i in range(k): - j = _int(random() * n) + j = self._randbelow(n) while j in selected: - j = _int(random() * n) + j = self._randbelow(n) selected_add(j) result[i] = population[j] return result Modified: python/branches/py3k/Lib/test/test_random.py ============================================================================== --- python/branches/py3k/Lib/test/test_random.py (original) +++ python/branches/py3k/Lib/test/test_random.py Tue Sep 7 06:44:52 2010 @@ -121,7 +121,15 @@ f = open(support.findfile(file),"rb") r = pickle.load(f) f.close() - self.assertEqual(r.randrange(1000), value) + self.assertEqual(int(r.random()*1000), value) + + def test_bug_9025(self): + # Had problem with an uneven distribution in int(n*random()) + # Verify the fix by checking that distributions fall within expectations. + n = 100000 + randrange = self.gen.randrange + k = sum(randrange(6755399441055744) % 3 == 2 for i in range(n)) + self.assertTrue(0.30 < k/n < .37, (k/n)) class SystemRandom_TestBasicOps(TestBasicOps): gen = random.SystemRandom() Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 7 06:44:52 2010 @@ -19,7 +19,16 @@ across versions and which parts are subject to change. * Update the seed() method to use all of the bits in a string - instead of just the hash value. + instead of just the hash value. This makes better use of the + seed value and assures the seeding is platform independent. + Issue #7889. + + * Improved the random()-->integer algorithm used in choice(), + shuffle(), sample(), randrange(), and randint(). Formerly, it + used int(n*random()) which has a slight bias whenever n is not + a power of two. Issue #9025. + + * Improved documentation of arguments to randrange(). Issue #9379. - collections.OrderedDict now supports a new method for repositioning keys to either end. From python-checkins at python.org Tue Sep 7 07:32:49 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 07:32:49 +0200 (CEST) Subject: [Python-checkins] r84577 - python/branches/py3k/Doc/library/random.rst Message-ID: <20100907053249.B849EEE994@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 07:32:49 2010 New Revision: 84577 Log: Remove outdated reference to Wichmann-Hill algorithm. Modified: python/branches/py3k/Doc/library/random.rst Modified: python/branches/py3k/Doc/library/random.rst ============================================================================== --- python/branches/py3k/Doc/library/random.rst (original) +++ python/branches/py3k/Doc/library/random.rst Tue Sep 7 07:32:49 2010 @@ -8,9 +8,10 @@ This module implements pseudo-random number generators for various distributions. -For integers, uniform selection from a range. For sequences, uniform selection -of a random element, a function to generate a random permutation of a list -in-place, and a function for random sampling without replacement. +For integers, there is uniform selection from a range. For sequences, there is +uniform selection of a random element, a function to generate a random +permutation of a list in-place, and a function for random sampling without +replacement. On the real line, there are functions to compute uniform, normal (Gaussian), lognormal, negative exponential, gamma, and beta distributions. For generating @@ -35,15 +36,6 @@ Optionally, a new generator can supply a :meth:`getrandbits` method --- this allows :meth:`randrange` to produce selections over an arbitrarily large range. -As an example of subclassing, the :mod:`random` module provides the -:class:`WichmannHill` class that implements an alternative generator in pure -Python. The class provides a backward compatible way to reproduce results from -earlier versions of Python, which used the Wichmann-Hill algorithm as the core -generator. Note that this Wichmann-Hill generator can no longer be recommended: -its period is too short by contemporary standards, and the sequence generated is -known to fail some stringent randomness tests. See the references below for a -recent variant that repairs these flaws. - The :mod:`random` module also provides the :class:`SystemRandom` class which uses the system function :func:`os.urandom` to generate random numbers from sources provided by the operating system. From python-checkins at python.org Tue Sep 7 07:35:10 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 07:35:10 +0200 (CEST) Subject: [Python-checkins] r84578 - python/branches/py3k/Doc/library/random.rst Message-ID: <20100907053510.B3519F7BC@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 07:35:10 2010 New Revision: 84578 Log: typo Modified: python/branches/py3k/Doc/library/random.rst Modified: python/branches/py3k/Doc/library/random.rst ============================================================================== --- python/branches/py3k/Doc/library/random.rst (original) +++ python/branches/py3k/Doc/library/random.rst Tue Sep 7 07:35:10 2010 @@ -55,7 +55,7 @@ If *x* is an int, it is used directly. With version 2 (the default), a :class:`str`, :class:`bytes`, or :class:`bytearray` - object gets converted to a :class:`int` and all of its bits are used. With version 1, + object gets converted to an :class:`int` and all of its bits are used. With version 1, the :func:`hash` of *x* is used instead. .. versionchanged:: 3.2 From python-checkins at python.org Tue Sep 7 10:18:27 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 7 Sep 2010 10:18:27 +0200 (CEST) Subject: [Python-checkins] r84579 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100907081827.1E1D0EEA44@mail.python.org> Author: georg.brandl Date: Tue Sep 7 10:18:26 2010 New Revision: 84579 Log: Add stub entry for argparse. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Tue Sep 7 10:18:26 2010 @@ -243,6 +243,8 @@ New, Improved, and Deprecated Modules ===================================== +* XXX mention :mod:`argparse`. + * The :mod:`functools` module includes a new decorator for caching function calls. :func:`functools.lru_cache` can save repeated queries to an external resource whenever the results are expected to be the same. From python-checkins at python.org Tue Sep 7 11:32:57 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 11:32:57 +0200 (CEST) Subject: [Python-checkins] r84580 - python/branches/py3k/Lib/random.py Message-ID: <20100907093257.836C2EE997@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 11:32:57 2010 New Revision: 84580 Log: Small clean-ups. Modified: python/branches/py3k/Lib/random.py Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Tue Sep 7 11:32:57 2010 @@ -167,7 +167,7 @@ This fixes the problem with randint() which includes the endpoint; in Python this is usually not what you want. - Do not supply the 'int' and 'maxwidth' arguments. + Do not supply the 'int' argument. """ # This code is a bit messy to make it fast for the @@ -186,20 +186,7 @@ raise ValueError("non-integer stop for randrange()") width = istop - istart if step == 1 and width > 0: - # Note that - # int(istart + self.random()*width) - # instead would be incorrect. For example, consider istart - # = -2 and istop = 0. Then the guts would be in - # -2.0 to 0.0 exclusive on both ends (ignoring that random() - # might return 0.0), and because int() truncates toward 0, the - # final result would be -1 or 0 (instead of -2 or -1). - # istart + int(self.random()*width) - # would also be incorrect, for a subtler reason: the RHS - # can return a long, and then randrange() would also return - # a long, but we're supposed to return an int (for backward - # compatibility). - - return int(istart + self._randbelow(width)) + return istart + self._randbelow(width) if step == 1: raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width)) @@ -233,20 +220,16 @@ by a single call to the underlying generator. """ - try: - getrandbits = self.getrandbits - except AttributeError: - pass - else: - # Only call self.getrandbits if the original random() builtin method - # has not been overridden or if a new getrandbits() was supplied. - # This assures that the two methods correspond. - if type(self.random) is _BuiltinMethod or type(getrandbits) is _Method: - k = n.bit_length() # don't use (n-1) here because n can be 1 - r = getrandbits(k) # 0 <= r < 2**k - while r >= n: - r = getrandbits(k) - return r + getrandbits = self.getrandbits + # Only call self.getrandbits if the original random() builtin method + # has not been overridden or if a new getrandbits() was supplied. + # This assures that the two methods correspond. + if type(self.random) is _BuiltinMethod or type(getrandbits) is _Method: + k = n.bit_length() # don't use (n-1) here because n can be 1 + r = getrandbits(k) # 0 <= r < 2**k + while r >= n: + r = getrandbits(k) + return r if n >= _maxwidth: _warn("Underlying random() generator does not supply \n" "enough bits to choose from a population range this large") From python-checkins at python.org Tue Sep 7 12:06:56 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 12:06:56 +0200 (CEST) Subject: [Python-checkins] r84581 - in python/branches/py3k/Lib: random.py test/test_random.py Message-ID: <20100907100656.DFC4EEEA29@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 12:06:56 2010 New Revision: 84581 Log: Fix corner case for Random.choice() and add tests. Modified: python/branches/py3k/Lib/random.py python/branches/py3k/Lib/test/test_random.py Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Tue Sep 7 12:06:56 2010 @@ -239,7 +239,11 @@ def choice(self, seq): """Choose a random element from a non-empty sequence.""" - return seq[self._randbelow(len(seq))] # raises IndexError if seq is empty + try: + i = self._randbelow(len(seq)) + except ValueError: + raise IndexError('Cannot choose from an empty sequence') + return seq[i] def shuffle(self, x, random=None, int=int): """x, random=random.random -> shuffle list x in place; return None. Modified: python/branches/py3k/Lib/test/test_random.py ============================================================================== --- python/branches/py3k/Lib/test/test_random.py (original) +++ python/branches/py3k/Lib/test/test_random.py Tue Sep 7 12:06:56 2010 @@ -42,6 +42,13 @@ self.assertRaises(TypeError, self.gen.seed, 1, 2, 3, 4) self.assertRaises(TypeError, type(self.gen), []) + def test_choice(self): + choice = self.gen.choice + with self.assertRaises(IndexError): + choice([]) + self.assertEqual(choice([50]), 50) + self.assertIn(choice([25, 75]), [25, 75]) + def test_sample(self): # For the entire allowable range of 0 <= k <= N, validate that # the sample is of the correct length and contains only unique items From ncoghlan at gmail.com Tue Sep 7 14:34:18 2010 From: ncoghlan at gmail.com (Nick Coghlan) Date: Tue, 7 Sep 2010 22:34:18 +1000 Subject: [Python-checkins] r84559 - python/branches/py3k/Lib/subprocess.py In-Reply-To: <20100906162929.8439CEE9C7@mail.python.org> References: <20100906162929.8439CEE9C7@mail.python.org> Message-ID: On Tue, Sep 7, 2010 at 2:29 AM, brian.curtin wrote: > Author: brian.curtin > Date: Mon Sep ?6 18:29:29 2010 > New Revision: 84559 > > Log: > Fix #8956. ValueError message was only mentioning one signal. > > Rather than list out the three signals (or more over time), the message was > made less specific but still descriptive. > > > > Modified: > ? python/branches/py3k/Lib/subprocess.py > > Modified: python/branches/py3k/Lib/subprocess.py > ============================================================================== > --- python/branches/py3k/Lib/subprocess.py ? ? ?(original) > +++ python/branches/py3k/Lib/subprocess.py ? ? ?Mon Sep ?6 18:29:29 2010 > @@ -983,7 +983,7 @@ > ? ? ? ? ? ? elif sig == signal.CTRL_BREAK_EVENT: > ? ? ? ? ? ? ? ? os.kill(self.pid, signal.CTRL_BREAK_EVENT) > ? ? ? ? ? ? else: > - ? ? ? ? ? ? ? ?raise ValueError("Only SIGTERM is supported on Windows") > + ? ? ? ? ? ? ? ?raise ValueError("Unsupported signal") Would it be worth including the signal number here, to at least give some hint as to exactly which signal was received? Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From ncoghlan at gmail.com Tue Sep 7 15:01:17 2010 From: ncoghlan at gmail.com (Nick Coghlan) Date: Tue, 7 Sep 2010 23:01:17 +1000 Subject: [Python-checkins] r84562 - in python/branches/py3k: Doc/library/io.rst Lib/_pyio.py Lib/test/test_memoryio.py Misc/NEWS Modules/_io/_iomodule.c Modules/_io/_iomodule.h Modules/_io/bytesio.c In-Reply-To: <20100906184822.26A92FA6F@mail.python.org> References: <20100906184822.26A92FA6F@mail.python.org> Message-ID: On Tue, Sep 7, 2010 at 4:48 AM, antoine.pitrou wrote: > Modified: python/branches/py3k/Lib/test/test_memoryio.py > ============================================================================== > --- python/branches/py3k/Lib/test/test_memoryio.py ? ? ?(original) > +++ python/branches/py3k/Lib/test/test_memoryio.py ? ? ?Mon Sep ?6 20:48:21 2010 > @@ -384,7 +384,31 @@ > ? ? ? ? del __main__.PickleTestMemIO > > > -class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase): > +class BytesIOMixin: > + > + ? ?def test_getbuffer(self): > + ? ? ? ?memio = self.ioclass(b"1234567890") > + ? ? ? ?buf = memio.getbuffer() > + ? ? ? ?self.assertEqual(bytes(buf), b"1234567890") > + ? ? ? ?memio.seek(5) > + ? ? ? ?buf = memio.getbuffer() > + ? ? ? ?self.assertEqual(bytes(buf), b"1234567890") > + ? ? ? ?# Trying to change the size of the BytesIO while a buffer is exported > + ? ? ? ?# raises a BufferError. > + ? ? ? ?self.assertRaises(BufferError, memio.write, b'x' * 100) > + ? ? ? ?self.assertRaises(BufferError, memio.truncate) > + ? ? ? ?# Mutating the buffer updates the BytesIO > + ? ? ? ?buf[3:6] = b"abc" > + ? ? ? ?self.assertEqual(bytes(buf), b"123abc7890") > + ? ? ? ?self.assertEqual(memio.getvalue(), b"123abc7890") > + ? ? ? ?# After the buffer gets released, we can resize the BytesIO again > + ? ? ? ?del buf > + ? ? ? ?support.gc_collect() > + ? ? ? ?memio.truncate() I've raised an RFE (http://bugs.python.org/issue9789) to point out that the need for that GC collect call in there to make the test portable to other implementations is rather ugly and supporting an explicit "buf.release()" call may be a nicer option. (And added Guido to the nosy list, since he wasn't keen on supporting the context management protocol idea, but I don't believe he said anything one way or the other about an ordinary method). > +class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, > + ? ? ? ? ? ? ? ? ? ?BytesIOMixin, unittest.TestCase): I was going to ask why CBytesIOTest wasn't affected, but checking the full source of the test file made everything clear :) Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From brian.curtin at gmail.com Tue Sep 7 15:05:21 2010 From: brian.curtin at gmail.com (Brian Curtin) Date: Tue, 7 Sep 2010 08:05:21 -0500 Subject: [Python-checkins] [Python-Dev] r84559 - python/branches/py3k/Lib/subprocess.py In-Reply-To: References: <20100906162929.8439CEE9C7@mail.python.org> Message-ID: On Tue, Sep 7, 2010 at 07:34, Nick Coghlan wrote: > On Tue, Sep 7, 2010 at 2:29 AM, brian.curtin > wrote: > > Author: brian.curtin > > Date: Mon Sep 6 18:29:29 2010 > > New Revision: 84559 > > > > Log: > > Fix #8956. ValueError message was only mentioning one signal. > > > > Rather than list out the three signals (or more over time), the message > was > > made less specific but still descriptive. > > > > > > > > Modified: > > python/branches/py3k/Lib/subprocess.py > > > > Modified: python/branches/py3k/Lib/subprocess.py > > > ============================================================================== > > --- python/branches/py3k/Lib/subprocess.py (original) > > +++ python/branches/py3k/Lib/subprocess.py Mon Sep 6 18:29:29 2010 > > @@ -983,7 +983,7 @@ > > elif sig == signal.CTRL_BREAK_EVENT: > > os.kill(self.pid, signal.CTRL_BREAK_EVENT) > > else: > > - raise ValueError("Only SIGTERM is supported on Windows") > > + raise ValueError("Unsupported signal") > > Would it be worth including the signal number here, to at least give > some hint as to exactly which signal was received? > > Cheers, > Nick. Sure, seems reasonable to me. Does """raise ValueError("Unsupported signal: {}".format(sig))""" look fine, or is there a more preferred format when displaying bad values in exception messages? -------------- next part -------------- An HTML attachment was scrubbed... URL: From ncoghlan at gmail.com Tue Sep 7 15:12:33 2010 From: ncoghlan at gmail.com (Nick Coghlan) Date: Tue, 7 Sep 2010 23:12:33 +1000 Subject: [Python-checkins] r84564 - in python/branches/py3k/Lib: ntpath.py test/test_ntpath.py In-Reply-To: <20100906194617.4ECACEE9B1@mail.python.org> References: <20100906194617.4ECACEE9B1@mail.python.org> Message-ID: On Tue, Sep 7, 2010 at 5:46 AM, brian.curtin wrote: > Modified: python/branches/py3k/Lib/ntpath.py > ============================================================================== > --- python/branches/py3k/Lib/ntpath.py ?(original) > +++ python/branches/py3k/Lib/ntpath.py ?Mon Sep ?6 21:46:17 2010 > @@ -10,7 +10,6 @@ > ?import stat > ?import genericpath > ?from genericpath import * > -from nt import _getfileinformation > > ?__all__ = ["normcase","isabs","join","splitdrive","split","splitext", > ? ? ? ? ? ?"basename","dirname","commonprefix","getsize","getmtime", > @@ -656,4 +655,10 @@ > > ?def sameopenfile(f1, f2): > ? ? """Test whether two file objects reference the same file""" > - ? ?return _getfileinformation(f1) == _getfileinformation(f2) > + ? ?try: > + ? ? ? ?from nt import _getfileinformation > + ? ? ? ?return _getfileinformation(f1) == _getfileinformation(f2) > + ? ?except ImportError: > + ? ? ? ?# On other operating systems, return True if the file descriptors > + ? ? ? ?# are the same. > + ? ? ? ?return f1 == f2 Given the potential deadlock problems with imports inside functions, I'd prefer to see this written as either: try: from nt import _getfileinformation def sameopenfile(f1, f2): return _getfileinformation(f1) == _getfileinformation(f2) except ImportError: # On other operating systems, return True if the file descriptors # are the same. def sameopenfile(f1, f2): return f1 == f2 sameopenfile.__doc__ = "Test whether two file objects reference the same file" or as: try: from nt import _getfileinformation except ImportError: # On other operating systems, file comparison is by file descriptor anyway # so a separate file information object is unnecessary def _getfileinformation(f): return f def sameopenfile(f1, f2): """Test whether two file objects reference the same file""" return _getfileinformation(f1) == _getfileinformation(f2) The latter is cleaner code, while the former is likely an unnecessary micro-optimisation. Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From ncoghlan at gmail.com Tue Sep 7 15:19:47 2010 From: ncoghlan at gmail.com (Nick Coghlan) Date: Tue, 7 Sep 2010 23:19:47 +1000 Subject: [Python-checkins] [Python-Dev] r84559 - python/branches/py3k/Lib/subprocess.py In-Reply-To: References: <20100906162929.8439CEE9C7@mail.python.org> Message-ID: On Tue, Sep 7, 2010 at 11:05 PM, Brian Curtin wrote: > Sure, seems reasonable to me. > Does """raise ValueError("Unsupported signal: {}".format(sig))""" look fine, > or is there a more preferred format when displaying bad values in exception > messages? No, that's about what I was thinking as well. When all we have is an error code (or similar number), it's difficult to make the associated error message particularly pretty. Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From brian.curtin at gmail.com Tue Sep 7 15:20:21 2010 From: brian.curtin at gmail.com (Brian Curtin) Date: Tue, 7 Sep 2010 08:20:21 -0500 Subject: [Python-checkins] [Python-Dev] r84564 - in python/branches/py3k/Lib: ntpath.py test/test_ntpath.py In-Reply-To: References: <20100906194617.4ECACEE9B1@mail.python.org> Message-ID: On Tue, Sep 7, 2010 at 08:12, Nick Coghlan wrote: > On Tue, Sep 7, 2010 at 5:46 AM, brian.curtin > wrote: > > Modified: python/branches/py3k/Lib/ntpath.py > > > ============================================================================== > > --- python/branches/py3k/Lib/ntpath.py (original) > > +++ python/branches/py3k/Lib/ntpath.py Mon Sep 6 21:46:17 2010 > > @@ -10,7 +10,6 @@ > > import stat > > import genericpath > > from genericpath import * > > -from nt import _getfileinformation > > > > __all__ = ["normcase","isabs","join","splitdrive","split","splitext", > > "basename","dirname","commonprefix","getsize","getmtime", > > @@ -656,4 +655,10 @@ > > > > def sameopenfile(f1, f2): > > """Test whether two file objects reference the same file""" > > - return _getfileinformation(f1) == _getfileinformation(f2) > > + try: > > + from nt import _getfileinformation > > + return _getfileinformation(f1) == _getfileinformation(f2) > > + except ImportError: > > + # On other operating systems, return True if the file > descriptors > > + # are the same. > > + return f1 == f2 > > Given the potential deadlock problems with imports inside functions, > I'd prefer to see this written as either: > > try: > from nt import _getfileinformation > def sameopenfile(f1, f2): > return _getfileinformation(f1) == _getfileinformation(f2) > except ImportError: > # On other operating systems, return True if the file descriptors > # are the same. > def sameopenfile(f1, f2): > return f1 == f2 > sameopenfile.__doc__ = "Test whether two file objects reference the same > file" > > or as: > > try: > from nt import _getfileinformation > except ImportError: > # On other operating systems, file comparison is by file descriptor anyway > # so a separate file information object is unnecessary > def _getfileinformation(f): return f > > def sameopenfile(f1, f2): > """Test whether two file objects reference the same file""" > return _getfileinformation(f1) == _getfileinformation(f2) > > The latter is cleaner code, while the former is likely an unnecessary > micro-optimisation. > > Cheers, > Nick. Similar idea(s) would also apply to the function right above that, samefile. I'll create a new issue for cleaning this up. -------------- next part -------------- An HTML attachment was scrubbed... URL: From python-checkins at python.org Tue Sep 7 15:24:38 2010 From: python-checkins at python.org (brian.curtin) Date: Tue, 7 Sep 2010 15:24:38 +0200 (CEST) Subject: [Python-checkins] r84582 - python/branches/py3k/Lib/subprocess.py Message-ID: <20100907132438.BD8ECEEA3E@mail.python.org> Author: brian.curtin Date: Tue Sep 7 15:24:38 2010 New Revision: 84582 Log: Adjust #8956 to add the bad signal number to the exception message. Modified: python/branches/py3k/Lib/subprocess.py Modified: python/branches/py3k/Lib/subprocess.py ============================================================================== --- python/branches/py3k/Lib/subprocess.py (original) +++ python/branches/py3k/Lib/subprocess.py Tue Sep 7 15:24:38 2010 @@ -983,7 +983,7 @@ elif sig == signal.CTRL_BREAK_EVENT: os.kill(self.pid, signal.CTRL_BREAK_EVENT) else: - raise ValueError("Unsupported signal") + raise ValueError("Unsupported signal: {}".format(sig)) def terminate(self): """Terminates the process From python-checkins at python.org Tue Sep 7 15:27:20 2010 From: python-checkins at python.org (brian.curtin) Date: Tue, 7 Sep 2010 15:27:20 +0200 (CEST) Subject: [Python-checkins] r84583 - in python/branches/release27-maint: Lib/subprocess.py Message-ID: <20100907132720.2E7A8EE9B2@mail.python.org> Author: brian.curtin Date: Tue Sep 7 15:27:20 2010 New Revision: 84583 Log: Merged revisions 84582 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84582 | brian.curtin | 2010-09-07 08:24:38 -0500 (Tue, 07 Sep 2010) | 3 lines Adjust #8956 to add the bad signal number to the exception message. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/subprocess.py Modified: python/branches/release27-maint/Lib/subprocess.py ============================================================================== --- python/branches/release27-maint/Lib/subprocess.py (original) +++ python/branches/release27-maint/Lib/subprocess.py Tue Sep 7 15:27:20 2010 @@ -993,7 +993,7 @@ elif sig == signal.CTRL_BREAK_EVENT: os.kill(self.pid, signal.CTRL_BREAK_EVENT) else: - raise ValueError("Unsupported signal") + raise ValueError("Unsupported signal: {}".format(sig)) def terminate(self): """Terminates the process From brian.curtin at gmail.com Tue Sep 7 15:28:40 2010 From: brian.curtin at gmail.com (Brian Curtin) Date: Tue, 7 Sep 2010 08:28:40 -0500 Subject: [Python-checkins] [Python-Dev] r84559 - python/branches/py3k/Lib/subprocess.py In-Reply-To: References: <20100906162929.8439CEE9C7@mail.python.org> Message-ID: On Tue, Sep 7, 2010 at 08:19, Nick Coghlan wrote: > On Tue, Sep 7, 2010 at 11:05 PM, Brian Curtin > wrote: > > Sure, seems reasonable to me. > > Does """raise ValueError("Unsupported signal: {}".format(sig))""" look > fine, > > or is there a more preferred format when displaying bad values in > exception > > messages? > > No, that's about what I was thinking as well. When all we have is an > error code (or similar number), it's difficult to make the associated > error message particularly pretty. > > Cheers, > Nick. Made the adjustment in r84582 (py3k) and r84583 (release27-maint). Thanks for the suggestion. -------------- next part -------------- An HTML attachment was scrubbed... URL: From python-checkins at python.org Tue Sep 7 16:52:43 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 16:52:43 +0200 (CEST) Subject: [Python-checkins] r84584 - in python/branches/py3k: Misc/NEWS configure configure.in pyconfig.h.in setup.py Message-ID: <20100907145243.1341AEEA76@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 16:52:42 2010 New Revision: 84584 Log: Issue #4026: Make the fcntl extension build under AIX. Patch by S?bastien Sabl?. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/configure python/branches/py3k/configure.in python/branches/py3k/pyconfig.h.in python/branches/py3k/setup.py Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 7 16:52:42 2010 @@ -52,6 +52,12 @@ guaranteed to exist in all Python implementations and the names of hash algorithms available in the current process. +Build +----- + +- Issue #4026: Make the fcntl extension build under AIX. Patch by S?bastien + Sabl?. + What's New in Python 3.2 Alpha 2? ================================= Modified: python/branches/py3k/configure ============================================================================== --- python/branches/py3k/configure (original) +++ python/branches/py3k/configure Tue Sep 7 16:52:42 2010 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 84477 . +# From configure.in Revision: 84512 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.65 for python 3.2. # @@ -9588,6 +9588,7 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock" >&5 $as_echo_n "checking for flock... " >&6; } +have_flock=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -9605,14 +9606,84 @@ $as_echo "#define HAVE_FLOCK 1" >>confdefs.h - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else + have_flock=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_flock" >&5 +$as_echo "$have_flock" >&6; } + +if test "$have_flock" = yes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if flock requires additional libraries." >&5 +$as_echo_n "checking if flock requires additional libraries.... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ +void *p = flock; flock(0, 0) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 +$as_echo_n "checking for flock in -lbsd... " >&6; } +if test "${ac_cv_lib_bsd_flock+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbsd $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char flock (); +int +main () +{ +return flock (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_bsd_flock=yes +else + ac_cv_lib_bsd_flock=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_flock" >&5 +$as_echo "$ac_cv_lib_bsd_flock" >&6; } +if test "x$ac_cv_lib_bsd_flock" = x""yes; then : + + +$as_echo "#define FLOCK_NEEDS_LIBBSD 1" >>confdefs.h + + +fi + + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking for getpagesize" >&5 $as_echo_n "checking for getpagesize... " >&6; } @@ -14226,8 +14297,8 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" +config_files="`echo $ac_config_files`" +config_headers="`echo $ac_config_headers`" _ACEOF Modified: python/branches/py3k/configure.in ============================================================================== --- python/branches/py3k/configure.in (original) +++ python/branches/py3k/configure.in Tue Sep 7 16:52:42 2010 @@ -2642,13 +2642,27 @@ ]) AC_MSG_CHECKING(for flock) +have_flock=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[void* p = flock]])], [AC_DEFINE(HAVE_FLOCK, 1, Define if you have the 'flock' function.) - AC_MSG_RESULT(yes)], - [AC_MSG_RESULT(no) + have_flock=yes ]) +AC_MSG_RESULT($have_flock) + +if test "$have_flock" = yes ; then + AC_MSG_CHECKING(if flock requires additional libraries.) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #include + ]], [[void *p = flock; flock(0, 0)]])], + [AC_MSG_RESULT(no)], + [AC_MSG_RESULT(yes) + AC_CHECK_LIB(bsd,flock, [ + AC_DEFINE(FLOCK_NEEDS_LIBBSD, 1, Define if flock needs to be linked with bsd library.) + ]) + ]) +fi AC_MSG_CHECKING(for getpagesize) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ Modified: python/branches/py3k/pyconfig.h.in ============================================================================== --- python/branches/py3k/pyconfig.h.in (original) +++ python/branches/py3k/pyconfig.h.in Tue Sep 7 16:52:42 2010 @@ -30,6 +30,9 @@ /* Define if --enable-ipv6 is specified */ #undef ENABLE_IPV6 +/* Define if flock needs to be linked with bsd library */ +#undef FLOCK_NEEDS_LIBBSD + /* Define if getpgrp() must be called as getpgrp(0). */ #undef GETPGRP_HAVE_ARG Modified: python/branches/py3k/setup.py ============================================================================== --- python/branches/py3k/setup.py (original) +++ python/branches/py3k/setup.py Tue Sep 7 16:52:42 2010 @@ -499,7 +499,11 @@ # supported...) # fcntl(2) and ioctl(2) - exts.append( Extension('fcntl', ['fcntlmodule.c']) ) + libs = [] + if (config_h_vars.get('FLOCK_NEEDS_LIBBSD', False)): + # May be necessary on AIX for flock function + libs = ['bsd'] + exts.append( Extension('fcntl', ['fcntlmodule.c'], libraries=libs) ) # pwd(3) exts.append( Extension('pwd', ['pwdmodule.c']) ) # grp(3) From python-checkins at python.org Tue Sep 7 16:55:24 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 16:55:24 +0200 (CEST) Subject: [Python-checkins] r84585 - in python/branches/release27-maint: Misc/NEWS configure configure.in pyconfig.h.in setup.py Message-ID: <20100907145524.E9A68EEA78@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 16:55:24 2010 New Revision: 84585 Log: Merged revisions 84584 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84584 | antoine.pitrou | 2010-09-07 16:52:42 +0200 (mar., 07 sept. 2010) | 4 lines Issue #4026: Make the fcntl extension build under AIX. Patch by S?bastien Sabl?. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/configure python/branches/release27-maint/configure.in python/branches/release27-maint/pyconfig.h.in python/branches/release27-maint/setup.py Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Sep 7 16:55:24 2010 @@ -275,6 +275,9 @@ Build ----- +- Issue #4026: Make the fcntl extension build under AIX. Patch by S?bastien + Sabl?. + - Issue #3101: Helper functions _add_one_to_index_C() and _add_one_to_index_F() become _Py_add_one_to_index_C() and _Py_add_one_to_index_F(), respectively. Modified: python/branches/release27-maint/configure ============================================================================== --- python/branches/release27-maint/configure (original) +++ python/branches/release27-maint/configure Tue Sep 7 16:55:24 2010 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 81582 . +# From configure.in Revision: 84368 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.65 for python 2.7. # @@ -9742,6 +9742,7 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock" >&5 $as_echo_n "checking for flock... " >&6; } +have_flock=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -9759,14 +9760,84 @@ $as_echo "#define HAVE_FLOCK 1" >>confdefs.h - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else + have_flock=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_flock" >&5 +$as_echo "$have_flock" >&6; } + +if test "$have_flock" = yes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if flock requires additional libraries." >&5 +$as_echo_n "checking if flock requires additional libraries.... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ +void *p = flock; flock(0, 0) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 +$as_echo_n "checking for flock in -lbsd... " >&6; } +if test "${ac_cv_lib_bsd_flock+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbsd $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char flock (); +int +main () +{ +return flock (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_bsd_flock=yes +else + ac_cv_lib_bsd_flock=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_flock" >&5 +$as_echo "$ac_cv_lib_bsd_flock" >&6; } +if test "x$ac_cv_lib_bsd_flock" = x""yes; then : + + +$as_echo "#define FLOCK_NEEDS_LIBBSD 1" >>confdefs.h + + +fi + + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking for getpagesize" >&5 $as_echo_n "checking for getpagesize... " >&6; } Modified: python/branches/release27-maint/configure.in ============================================================================== --- python/branches/release27-maint/configure.in (original) +++ python/branches/release27-maint/configure.in Tue Sep 7 16:55:24 2010 @@ -2775,13 +2775,27 @@ ]) AC_MSG_CHECKING(for flock) +have_flock=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[void* p = flock]])], [AC_DEFINE(HAVE_FLOCK, 1, Define if you have the 'flock' function.) - AC_MSG_RESULT(yes)], - [AC_MSG_RESULT(no) + have_flock=yes ]) +AC_MSG_RESULT($have_flock) + +if test "$have_flock" = yes ; then + AC_MSG_CHECKING(if flock requires additional libraries.) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #include + ]], [[void *p = flock; flock(0, 0)]])], + [AC_MSG_RESULT(no)], + [AC_MSG_RESULT(yes) + AC_CHECK_LIB(bsd,flock, [ + AC_DEFINE(FLOCK_NEEDS_LIBBSD, 1, Define if flock needs to be linked with bsd library.) + ]) + ]) +fi AC_MSG_CHECKING(for getpagesize) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ Modified: python/branches/release27-maint/pyconfig.h.in ============================================================================== --- python/branches/release27-maint/pyconfig.h.in (original) +++ python/branches/release27-maint/pyconfig.h.in Tue Sep 7 16:55:24 2010 @@ -36,6 +36,9 @@ /* Define if --enable-ipv6 is specified */ #undef ENABLE_IPV6 +/* Define if flock needs to be linked with bsd library */ +#undef FLOCK_NEEDS_LIBBSD + /* Define if getpgrp() must be called as getpgrp(0). */ #undef GETPGRP_HAVE_ARG Modified: python/branches/release27-maint/setup.py ============================================================================== --- python/branches/release27-maint/setup.py (original) +++ python/branches/release27-maint/setup.py Tue Sep 7 16:55:24 2010 @@ -529,7 +529,11 @@ # supported...) # fcntl(2) and ioctl(2) - exts.append( Extension('fcntl', ['fcntlmodule.c']) ) + libs = [] + if (config_h_vars.get('FLOCK_NEEDS_LIBBSD', False)): + # May be necessary on AIX for flock function + libs = ['bsd'] + exts.append( Extension('fcntl', ['fcntlmodule.c'], libraries=libs) ) # pwd(3) exts.append( Extension('pwd', ['pwdmodule.c']) ) # grp(3) From python-checkins at python.org Tue Sep 7 16:58:51 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 16:58:51 +0200 (CEST) Subject: [Python-checkins] r84586 - in python/branches/release31-maint: Misc/NEWS configure configure.in pyconfig.h.in setup.py Message-ID: <20100907145851.02D35EEA88@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 16:58:50 2010 New Revision: 84586 Log: Merged revisions 84584 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84584 | antoine.pitrou | 2010-09-07 16:52:42 +0200 (mar., 07 sept. 2010) | 4 lines Issue #4026: Make the fcntl extension build under AIX. Patch by S?bastien Sabl?. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/configure python/branches/release31-maint/configure.in python/branches/release31-maint/pyconfig.h.in python/branches/release31-maint/setup.py Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Sep 7 16:58:50 2010 @@ -535,6 +535,9 @@ Build ----- +- Issue #4026: Make the fcntl extension build under AIX. Patch by S?bastien + Sabl?. + - Issue #3101: Helper functions _add_one_to_index_C() and _add_one_to_index_F() become _Py_add_one_to_index_C() and _Py_add_one_to_index_F(), respectively. Modified: python/branches/release31-maint/configure ============================================================================== --- python/branches/release31-maint/configure (original) +++ python/branches/release31-maint/configure Tue Sep 7 16:58:50 2010 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 82963 . +# From configure.in Revision: 84367 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.65 for python 3.1. # @@ -9410,6 +9410,7 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock" >&5 $as_echo_n "checking for flock... " >&6; } +have_flock=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -9426,16 +9427,89 @@ _ACEOF if ac_fn_c_try_compile "$LINENO"; then : + $as_echo "#define HAVE_FLOCK 1" >>confdefs.h - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else + have_flock=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_flock" >&5 +$as_echo "$have_flock" >&6; } + +if test "$have_flock" = yes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if flock requires additional libraries." >&5 +$as_echo_n "checking if flock requires additional libraries.... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include "confdefs.h" + #include + +int +main () +{ +flock(0, 0) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 +$as_echo_n "checking for flock in -lbsd... " >&6; } +if test "${ac_cv_lib_bsd_flock+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbsd $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char flock (); +int +main () +{ +return flock (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_bsd_flock=yes +else + ac_cv_lib_bsd_flock=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_flock" >&5 +$as_echo "$ac_cv_lib_bsd_flock" >&6; } +if test "x$ac_cv_lib_bsd_flock" = x""yes; then : + + +$as_echo "#define FLOCK_NEEDS_LIBBSD 1" >>confdefs.h + + +fi + + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking for getpagesize" >&5 $as_echo_n "checking for getpagesize... " >&6; } Modified: python/branches/release31-maint/configure.in ============================================================================== --- python/branches/release31-maint/configure.in (original) +++ python/branches/release31-maint/configure.in Tue Sep 7 16:58:50 2010 @@ -2642,14 +2642,29 @@ ) AC_MSG_CHECKING(for flock) +have_flock=no AC_TRY_COMPILE([ #include "confdefs.h" #include -], void* p = flock, +], void* p = flock, [ AC_DEFINE(HAVE_FLOCK, 1, Define if you have the 'flock' function.) - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no) -) + have_flock=yes +]) +AC_MSG_RESULT($have_flock) + +if test "$have_flock" = yes ; then + AC_MSG_CHECKING(if flock requires additional libraries.) + AC_TRY_LINK([ + #include "confdefs.h" + #include + ], [flock(0, 0)], + AC_MSG_RESULT(no), [ + AC_MSG_RESULT(yes) + AC_CHECK_LIB(bsd,flock, [ + AC_DEFINE(FLOCK_NEEDS_LIBBSD, 1, Define if flock needs to be linked with bsd library.) + ]) + ]) +fi AC_MSG_CHECKING(for getpagesize) AC_TRY_COMPILE([ Modified: python/branches/release31-maint/pyconfig.h.in ============================================================================== --- python/branches/release31-maint/pyconfig.h.in (original) +++ python/branches/release31-maint/pyconfig.h.in Tue Sep 7 16:58:50 2010 @@ -33,6 +33,9 @@ /* Define if --enable-ipv6 is specified */ #undef ENABLE_IPV6 +/* Define if flock needs to be linked with bsd library */ +#undef FLOCK_NEEDS_LIBBSD + /* Define if getpgrp() must be called as getpgrp(0). */ #undef GETPGRP_HAVE_ARG Modified: python/branches/release31-maint/setup.py ============================================================================== --- python/branches/release31-maint/setup.py (original) +++ python/branches/release31-maint/setup.py Tue Sep 7 16:58:50 2010 @@ -489,7 +489,11 @@ # supported...) # fcntl(2) and ioctl(2) - exts.append( Extension('fcntl', ['fcntlmodule.c']) ) + libs = [] + if (config_h_vars.get('FLOCK_NEEDS_LIBBSD', False)): + # May be necessary on AIX for flock function + libs = ['bsd'] + exts.append( Extension('fcntl', ['fcntlmodule.c'], libraries=libs) ) if platform not in ['mac']: # pwd(3) exts.append( Extension('pwd', ['pwdmodule.c']) ) From python-checkins at python.org Tue Sep 7 17:00:15 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 17:00:15 +0200 (CEST) Subject: [Python-checkins] r84587 - in python/branches/py3k/Lib: random.py test/test_generators.py Message-ID: <20100907150015.A05F3EEA46@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 17:00:15 2010 New Revision: 84587 Log: Fix test that depends on a particular implementation of random.choice(). Modified: python/branches/py3k/Lib/random.py python/branches/py3k/Lib/test/test_generators.py Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Tue Sep 7 17:00:15 2010 @@ -136,7 +136,7 @@ # really unsigned 32-bit ints, so we convert negative ints from # version 2 to positive longs for version 3. try: - internalstate = tuple( x % (2**32) for x in internalstate ) + internalstate = tuple(x % (2**32) for x in internalstate) except ValueError as e: raise TypeError from e super(Random, self).setstate(internalstate) @@ -214,10 +214,7 @@ def _randbelow(self, n, int=int, _maxwidth=1<A B->B C->C D->D E->E F->F G->G H->H I->I J->J K->K L->L M->M -merged I into A - A->A B->B C->C D->D E->E F->F G->G H->H I->A J->J K->K L->L M->M -merged D into C - A->A B->B C->C D->C E->E F->F G->G H->H I->A J->J K->K L->L M->M -merged K into H - A->A B->B C->C D->C E->E F->F G->G H->H I->A J->J K->H L->L M->M -merged L into A - A->A B->B C->C D->C E->E F->F G->G H->H I->A J->J K->H L->A M->M -merged E into A - A->A B->B C->C D->C E->A F->F G->G H->H I->A J->J K->H L->A M->M -merged B into G - A->A B->G C->C D->C E->A F->F G->G H->H I->A J->J K->H L->A M->M +merged K into B + A->A B->B C->C D->D E->E F->F G->G H->H I->I J->J K->B L->L M->M merged A into F - A->F B->G C->C D->C E->F F->F G->G H->H I->F J->J K->H L->F M->M -merged H into G - A->F B->G C->C D->C E->F F->F G->G H->G I->F J->J K->G L->F M->M -merged F into J - A->J B->G C->C D->C E->J F->J G->G H->G I->J J->J K->G L->J M->M + A->F B->B C->C D->D E->E F->F G->G H->H I->I J->J K->B L->L M->M +merged E into F + A->F B->B C->C D->D E->F F->F G->G H->H I->I J->J K->B L->L M->M +merged D into C + A->F B->B C->C D->C E->F F->F G->G H->H I->I J->J K->B L->L M->M merged M into C - A->J B->G C->C D->C E->J F->J G->G H->G I->J J->J K->G L->J M->C -merged J into G - A->G B->G C->C D->C E->G F->G G->G H->G I->G J->G K->G L->G M->C -merged C into G - A->G B->G C->G D->G E->G F->G G->G H->G I->G J->G K->G L->G M->G + A->F B->B C->C D->C E->F F->F G->G H->H I->I J->J K->B L->L M->C +merged J into B + A->F B->B C->C D->C E->F F->F G->G H->H I->I J->B K->B L->L M->C +merged B into C + A->F B->C C->C D->C E->F F->F G->G H->H I->I J->C K->C L->L M->C +merged F into G + A->G B->C C->C D->C E->G F->G G->G H->H I->I J->C K->C L->L M->C +merged L into C + A->G B->C C->C D->C E->G F->G G->G H->H I->I J->C K->C L->C M->C +merged G into I + A->I B->C C->C D->C E->I F->I G->I H->H I->I J->C K->C L->C M->C +merged I into H + A->H B->C C->C D->C E->H F->H G->H H->H I->H J->C K->C L->C M->C +merged C into H + A->H B->H C->H D->H E->H F->H G->H H->H I->H J->H K->H L->H M->H """ # Emacs turd ' From python-checkins at python.org Tue Sep 7 17:38:33 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 17:38:33 +0200 (CEST) Subject: [Python-checkins] r84588 - python/branches/py3k/Lib/test/test_random.py Message-ID: <20100907153833.6BE0FEEA7F@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 17:38:33 2010 New Revision: 84588 Log: Remove invalid test (it was supposed to fail on 64-bit machines.). Modified: python/branches/py3k/Lib/test/test_random.py Modified: python/branches/py3k/Lib/test/test_random.py ============================================================================== --- python/branches/py3k/Lib/test/test_random.py (original) +++ python/branches/py3k/Lib/test/test_random.py Tue Sep 7 17:38:33 2010 @@ -244,10 +244,6 @@ self.assertEqual([self.gen.random().hex() for i in range(4)], ['0x1.ac362300d90d2p-1', '0x1.9d16f74365005p-1', '0x1.1ebb4352e4c4dp-1', '0x1.1a7422abf9c11p-1']) - self.gen.seed("the quick brown fox", version=1) - self.assertEqual([self.gen.random().hex() for i in range(4)], - ['0x1.9ee265c177cdep-2', '0x1.bad51092e3c25p-1', - '0x1.85ff833f71576p-1', '0x1.87efb37462927p-1']) self.gen.seed("the quick brown fox", version=2) self.assertEqual([self.gen.random().hex() for i in range(4)], ['0x1.1294009b9eda4p-2', '0x1.2ff96171b0010p-1', From python-checkins at python.org Tue Sep 7 18:30:09 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 18:30:09 +0200 (CEST) Subject: [Python-checkins] r84589 - in python/branches/py3k: Lib/test/test_ioctl.py Misc/NEWS Modules/fcntlmodule.c Message-ID: <20100907163009.A47B1EEA03@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 18:30:09 2010 New Revision: 84589 Log: Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True, and the passed buffer was exactly 1024 bytes long, the buffer wouldn't be updated back after the system call. Original patch by Brian Brazil. Modified: python/branches/py3k/Lib/test/test_ioctl.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/fcntlmodule.c Modified: python/branches/py3k/Lib/test/test_ioctl.py ============================================================================== --- python/branches/py3k/Lib/test/test_ioctl.py (original) +++ python/branches/py3k/Lib/test/test_ioctl.py Tue Sep 7 18:30:09 2010 @@ -1,3 +1,4 @@ +import array import unittest from test.support import run_unittest, import_module, get_attribute import os, struct @@ -34,16 +35,36 @@ rpgrp = struct.unpack("i", r)[0] self.assertIn(rpgrp, ids) - def test_ioctl_mutate(self): - import array - buf = array.array('i', [0]) + def _check_ioctl_mutate_len(self, nbytes=None): + buf = array.array('i') + intsize = buf.itemsize ids = (os.getpgrp(), os.getsid(0)) - tty = open("/dev/tty", "r") - r = fcntl.ioctl(tty, termios.TIOCGPGRP, buf, 1) + # A fill value unlikely to be in `ids` + fill = -12345 + if nbytes is not None: + # Extend the buffer so that it is exactly `nbytes` bytes long + buf.extend([fill] * (nbytes // intsize)) + self.assertEqual(len(buf) * intsize, nbytes) # sanity check + else: + buf.append(fill) + with open("/dev/tty", "r") as tty: + r = fcntl.ioctl(tty, termios.TIOCGPGRP, buf, 1) rpgrp = buf[0] self.assertEquals(r, 0) self.assertIn(rpgrp, ids) + def test_ioctl_mutate(self): + self._check_ioctl_mutate_len() + + def test_ioctl_mutate_1024(self): + # Issue #9758: a mutable buffer of exactly 1024 bytes wouldn't be + # copied back after the system call. + self._check_ioctl_mutate_len(1024) + + def test_ioctl_mutate_2048(self): + # Test with a larger buffer, just for the record. + self._check_ioctl_mutate_len(2048) + def test_ioctl_signed_unsigned_code_param(self): if not pty: raise unittest.SkipTest('pty module required') Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 7 18:30:09 2010 @@ -13,6 +13,10 @@ Library ------- +- Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True, + and the passed buffer was exactly 1024 bytes long, the buffer wouldn't + be updated back after the system call. Original patch by Brian Brazil. + - Updates to the random module: * Document which parts of the module are guaranteed to stay the same Modified: python/branches/py3k/Modules/fcntlmodule.c ============================================================================== --- python/branches/py3k/Modules/fcntlmodule.c (original) +++ python/branches/py3k/Modules/fcntlmodule.c Tue Sep 7 18:30:09 2010 @@ -157,7 +157,7 @@ else { ret = ioctl(fd, code, arg); } - if (mutate_arg && (len < IOCTL_BUFSZ)) { + if (mutate_arg && (len <= IOCTL_BUFSZ)) { memcpy(str, buf, len); } PyBuffer_Release(&pstr); /* No further access to str below this point */ From python-checkins at python.org Tue Sep 7 18:32:28 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 18:32:28 +0200 (CEST) Subject: [Python-checkins] r84590 - in python/branches/release31-maint: Lib/test/test_ioctl.py Misc/NEWS Modules/fcntlmodule.c Message-ID: <20100907163228.3B7A3EEA03@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 18:32:28 2010 New Revision: 84590 Log: Merged revisions 84589 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84589 | antoine.pitrou | 2010-09-07 18:30:09 +0200 (mar., 07 sept. 2010) | 5 lines Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True, and the passed buffer was exactly 1024 bytes long, the buffer wouldn't be updated back after the system call. Original patch by Brian Brazil. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_ioctl.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/fcntlmodule.c Modified: python/branches/release31-maint/Lib/test/test_ioctl.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_ioctl.py (original) +++ python/branches/release31-maint/Lib/test/test_ioctl.py Tue Sep 7 18:32:28 2010 @@ -1,3 +1,4 @@ +import array import unittest from test.support import run_unittest, import_module, get_attribute import os, struct @@ -34,16 +35,36 @@ rpgrp = struct.unpack("i", r)[0] self.assertTrue(rpgrp in ids, "%s not in %s" % (rpgrp, ids)) - def test_ioctl_mutate(self): - import array - buf = array.array('i', [0]) + def _check_ioctl_mutate_len(self, nbytes=None): + buf = array.array('i') + intsize = buf.itemsize ids = (os.getpgrp(), os.getsid(0)) - tty = open("/dev/tty", "r") - r = fcntl.ioctl(tty, termios.TIOCGPGRP, buf, 1) + # A fill value unlikely to be in `ids` + fill = -12345 + if nbytes is not None: + # Extend the buffer so that it is exactly `nbytes` bytes long + buf.extend([fill] * (nbytes // intsize)) + self.assertEqual(len(buf) * intsize, nbytes) # sanity check + else: + buf.append(fill) + with open("/dev/tty", "r") as tty: + r = fcntl.ioctl(tty, termios.TIOCGPGRP, buf, 1) rpgrp = buf[0] self.assertEquals(r, 0) self.assertTrue(rpgrp in ids, "%s not in %s" % (rpgrp, ids)) + def test_ioctl_mutate(self): + self._check_ioctl_mutate_len() + + def test_ioctl_mutate_1024(self): + # Issue #9758: a mutable buffer of exactly 1024 bytes wouldn't be + # copied back after the system call. + self._check_ioctl_mutate_len(1024) + + def test_ioctl_mutate_2048(self): + # Test with a larger buffer, just for the record. + self._check_ioctl_mutate_len(2048) + def test_ioctl_signed_unsigned_code_param(self): if not pty: raise unittest.SkipTest('pty module required') Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Sep 7 18:32:28 2010 @@ -105,6 +105,10 @@ Library ------- +- Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True, + and the passed buffer was exactly 1024 bytes long, the buffer wouldn't + be updated back after the system call. Original patch by Brian Brazil. + - Issue #6656: fix locale.format_string to handle escaped percents and mappings. Modified: python/branches/release31-maint/Modules/fcntlmodule.c ============================================================================== --- python/branches/release31-maint/Modules/fcntlmodule.c (original) +++ python/branches/release31-maint/Modules/fcntlmodule.c Tue Sep 7 18:32:28 2010 @@ -157,7 +157,7 @@ else { ret = ioctl(fd, code, arg); } - if (mutate_arg && (len < IOCTL_BUFSZ)) { + if (mutate_arg && (len <= IOCTL_BUFSZ)) { memcpy(str, buf, len); } PyBuffer_Release(&pstr); /* No further access to str below this point */ From python-checkins at python.org Tue Sep 7 18:34:48 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 18:34:48 +0200 (CEST) Subject: [Python-checkins] r84591 - in python/branches/release27-maint: Lib/test/test_ioctl.py Misc/NEWS Modules/fcntlmodule.c Message-ID: <20100907163448.18881EEA03@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 18:34:47 2010 New Revision: 84591 Log: Merged revisions 84589 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84589 | antoine.pitrou | 2010-09-07 18:30:09 +0200 (mar., 07 sept. 2010) | 5 lines Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True, and the passed buffer was exactly 1024 bytes long, the buffer wouldn't be updated back after the system call. Original patch by Brian Brazil. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_ioctl.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/fcntlmodule.c Modified: python/branches/release27-maint/Lib/test/test_ioctl.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_ioctl.py (original) +++ python/branches/release27-maint/Lib/test/test_ioctl.py Tue Sep 7 18:34:47 2010 @@ -1,3 +1,4 @@ +import array import unittest from test.test_support import run_unittest, import_module, get_attribute import os, struct @@ -34,16 +35,36 @@ rpgrp = struct.unpack("i", r)[0] self.assertIn(rpgrp, ids) - def test_ioctl_mutate(self): - import array - buf = array.array('i', [0]) + def _check_ioctl_mutate_len(self, nbytes=None): + buf = array.array('i') + intsize = buf.itemsize ids = (os.getpgrp(), os.getsid(0)) - tty = open("/dev/tty", "r") - r = fcntl.ioctl(tty, termios.TIOCGPGRP, buf, 1) + # A fill value unlikely to be in `ids` + fill = -12345 + if nbytes is not None: + # Extend the buffer so that it is exactly `nbytes` bytes long + buf.extend([fill] * (nbytes // intsize)) + self.assertEqual(len(buf) * intsize, nbytes) # sanity check + else: + buf.append(fill) + with open("/dev/tty", "r") as tty: + r = fcntl.ioctl(tty, termios.TIOCGPGRP, buf, 1) rpgrp = buf[0] self.assertEquals(r, 0) self.assertIn(rpgrp, ids) + def test_ioctl_mutate(self): + self._check_ioctl_mutate_len() + + def test_ioctl_mutate_1024(self): + # Issue #9758: a mutable buffer of exactly 1024 bytes wouldn't be + # copied back after the system call. + self._check_ioctl_mutate_len(1024) + + def test_ioctl_mutate_2048(self): + # Test with a larger buffer, just for the record. + self._check_ioctl_mutate_len(2048) + def test_ioctl_signed_unsigned_code_param(self): if not pty: raise unittest.SkipTest('pty module required') Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Sep 7 18:34:47 2010 @@ -36,6 +36,10 @@ Library ------- +- Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True, + and the passed buffer was exactly 1024 bytes long, the buffer wouldn't + be updated back after the system call. Original patch by Brian Brazil. + - Issue #1100562: Fix deep-copying of objects derived from the list and dict types. Patch by Michele Orr? and Bj?rn Lindqvist. Modified: python/branches/release27-maint/Modules/fcntlmodule.c ============================================================================== --- python/branches/release27-maint/Modules/fcntlmodule.c (original) +++ python/branches/release27-maint/Modules/fcntlmodule.c Tue Sep 7 18:34:47 2010 @@ -153,7 +153,7 @@ else { ret = ioctl(fd, code, arg); } - if (mutate_arg && (len < IOCTL_BUFSZ)) { + if (mutate_arg && (len <= IOCTL_BUFSZ)) { memcpy(str, buf, len); } if (ret < 0) { From benjamin at python.org Tue Sep 7 19:39:54 2010 From: benjamin at python.org (Benjamin Peterson) Date: Tue, 7 Sep 2010 12:39:54 -0500 Subject: [Python-checkins] [Python-Dev] r84562 - in python/branches/py3k: Doc/library/io.rst Lib/_pyio.py Lib/test/test_memoryio.py Misc/NEWS Modules/_io/_iomodule.c Modules/_io/_iomodule.h Modules/_io/bytesio.c In-Reply-To: References: <20100906184822.26A92FA6F@mail.python.org> Message-ID: 2010/9/7 Nick Coghlan : > I've raised an RFE (http://bugs.python.org/issue9789) to point out > that the need for that GC collect call in there to make the test > portable to other implementations is rather ugly Why? You're testing garbage collection, so you should call garbage collection. -- Regards, Benjamin From python-checkins at python.org Tue Sep 7 20:44:12 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 20:44:12 +0200 (CEST) Subject: [Python-checkins] r84592 - python/branches/py3k/Doc/library/nntplib.rst Message-ID: <20100907184412.F0820FDC3@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 20:44:12 2010 New Revision: 84592 Log: Update nntplib examples to use a public news server. The example still doesn't work as-is under py3k, due to incomplete or buggy porting of the nntplib module. Modified: python/branches/py3k/Doc/library/nntplib.rst Modified: python/branches/py3k/Doc/library/nntplib.rst ============================================================================== --- python/branches/py3k/Doc/library/nntplib.rst (original) +++ python/branches/py3k/Doc/library/nntplib.rst Tue Sep 7 20:44:12 2010 @@ -18,35 +18,35 @@ Here are two small examples of how it can be used. To list some statistics about a newsgroup and print the subjects of the last 10 articles:: - >>> s = NNTP('news.cwi.nl') - >>> resp, count, first, last, name = s.group('comp.lang.python') + >>> s = NNTP('news.gmane.org') + >>> resp, count, first, last, name = s.group('gmane.comp.python.committers') >>> print('Group', name, 'has', count, 'articles, range', first, 'to', last) - Group comp.lang.python has 59 articles, range 3742 to 3803 + Group gmane.comp.python.committers has 1071 articles, range 1 to 1071 >>> resp, subs = s.xhdr('subject', first + '-' + last) >>> for id, sub in subs[-10:]: print(id, sub) ... - 3792 Re: Removing elements from a list while iterating... - 3793 Re: Who likes Info files? - 3794 Emacs and doc strings - 3795 a few questions about the Mac implementation - 3796 Re: executable python scripts - 3797 Re: executable python scripts - 3798 Re: a few questions about the Mac implementation - 3799 Re: PROPOSAL: A Generic Python Object Interface for Python C Modules - 3802 Re: executable python scripts - 3803 Re: \POSIX{} wait and SIGCHLD + 1062 Re: Mercurial Status? + 1063 Re: [python-committers] (Windows) buildbots on 3.x + 1064 Re: Mercurial Status? + 1065 Re: Mercurial Status? + 1066 Python 2.6.6 status + 1067 Commit Privileges for Ask Solem + 1068 Re: Commit Privileges for Ask Solem + 1069 Re: Commit Privileges for Ask Solem + 1070 Re: Commit Privileges for Ask Solem + 1071 2.6.6 rc 2 >>> s.quit() - '205 news.cwi.nl closing connection. Goodbye.' + '205 Bye!' To post an article from a file (this assumes that the article has valid -headers):: +headers, and that you have right to post on the particular newsgroup):: - >>> s = NNTP('news.cwi.nl') + >>> s = NNTP('news.gmane.org') >>> f = open('/tmp/article') >>> s.post(f) '240 Article posted successfully.' >>> s.quit() - '205 news.cwi.nl closing connection. Goodbye.' + '205 Bye!' The module itself defines the following items: From python-checkins at python.org Tue Sep 7 20:44:52 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 20:44:52 +0200 (CEST) Subject: [Python-checkins] r84593 - in python/branches/release31-maint: Doc/library/nntplib.rst Message-ID: <20100907184452.6382FFA05@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 20:44:52 2010 New Revision: 84593 Log: Merged revisions 84592 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84592 | antoine.pitrou | 2010-09-07 20:44:12 +0200 (mar., 07 sept. 2010) | 5 lines Update nntplib examples to use a public news server. The example still doesn't work as-is under py3k, due to incomplete or buggy porting of the nntplib module. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/nntplib.rst Modified: python/branches/release31-maint/Doc/library/nntplib.rst ============================================================================== --- python/branches/release31-maint/Doc/library/nntplib.rst (original) +++ python/branches/release31-maint/Doc/library/nntplib.rst Tue Sep 7 20:44:52 2010 @@ -18,35 +18,35 @@ Here are two small examples of how it can be used. To list some statistics about a newsgroup and print the subjects of the last 10 articles:: - >>> s = NNTP('news.cwi.nl') - >>> resp, count, first, last, name = s.group('comp.lang.python') + >>> s = NNTP('news.gmane.org') + >>> resp, count, first, last, name = s.group('gmane.comp.python.committers') >>> print('Group', name, 'has', count, 'articles, range', first, 'to', last) - Group comp.lang.python has 59 articles, range 3742 to 3803 + Group gmane.comp.python.committers has 1071 articles, range 1 to 1071 >>> resp, subs = s.xhdr('subject', first + '-' + last) >>> for id, sub in subs[-10:]: print(id, sub) ... - 3792 Re: Removing elements from a list while iterating... - 3793 Re: Who likes Info files? - 3794 Emacs and doc strings - 3795 a few questions about the Mac implementation - 3796 Re: executable python scripts - 3797 Re: executable python scripts - 3798 Re: a few questions about the Mac implementation - 3799 Re: PROPOSAL: A Generic Python Object Interface for Python C Modules - 3802 Re: executable python scripts - 3803 Re: \POSIX{} wait and SIGCHLD + 1062 Re: Mercurial Status? + 1063 Re: [python-committers] (Windows) buildbots on 3.x + 1064 Re: Mercurial Status? + 1065 Re: Mercurial Status? + 1066 Python 2.6.6 status + 1067 Commit Privileges for Ask Solem + 1068 Re: Commit Privileges for Ask Solem + 1069 Re: Commit Privileges for Ask Solem + 1070 Re: Commit Privileges for Ask Solem + 1071 2.6.6 rc 2 >>> s.quit() - '205 news.cwi.nl closing connection. Goodbye.' + '205 Bye!' To post an article from a file (this assumes that the article has valid -headers):: +headers, and that you have right to post on the particular newsgroup):: - >>> s = NNTP('news.cwi.nl') + >>> s = NNTP('news.gmane.org') >>> f = open('/tmp/article') >>> s.post(f) '240 Article posted successfully.' >>> s.quit() - '205 news.cwi.nl closing connection. Goodbye.' + '205 Bye!' The module itself defines the following items: From python-checkins at python.org Tue Sep 7 20:46:41 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 20:46:41 +0200 (CEST) Subject: [Python-checkins] r84594 - in python/branches/release27-maint: Doc/library/nntplib.rst Message-ID: <20100907184641.891B2EEA38@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 20:46:41 2010 New Revision: 84594 Log: Merged revisions 84592 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84592 | antoine.pitrou | 2010-09-07 20:44:12 +0200 (mar., 07 sept. 2010) | 5 lines Update nntplib examples to use a public news server. The example still doesn't work as-is under py3k, due to incomplete or buggy porting of the nntplib module. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/nntplib.rst Modified: python/branches/release27-maint/Doc/library/nntplib.rst ============================================================================== --- python/branches/release27-maint/Doc/library/nntplib.rst (original) +++ python/branches/release27-maint/Doc/library/nntplib.rst Tue Sep 7 20:46:41 2010 @@ -18,35 +18,35 @@ Here are two small examples of how it can be used. To list some statistics about a newsgroup and print the subjects of the last 10 articles:: - >>> s = NNTP('news.cwi.nl') - >>> resp, count, first, last, name = s.group('comp.lang.python') + >>> s = NNTP('news.gmane.org') + >>> resp, count, first, last, name = s.group('gmane.comp.python.committers') >>> print 'Group', name, 'has', count, 'articles, range', first, 'to', last - Group comp.lang.python has 59 articles, range 3742 to 3803 + Group gmane.comp.python.committers has 1071 articles, range 1 to 1071 >>> resp, subs = s.xhdr('subject', first + '-' + last) >>> for id, sub in subs[-10:]: print id, sub ... - 3792 Re: Removing elements from a list while iterating... - 3793 Re: Who likes Info files? - 3794 Emacs and doc strings - 3795 a few questions about the Mac implementation - 3796 Re: executable python scripts - 3797 Re: executable python scripts - 3798 Re: a few questions about the Mac implementation - 3799 Re: PROPOSAL: A Generic Python Object Interface for Python C Modules - 3802 Re: executable python scripts - 3803 Re: \POSIX{} wait and SIGCHLD + 1062 Re: Mercurial Status? + 1063 Re: [python-committers] (Windows) buildbots on 3.x + 1064 Re: Mercurial Status? + 1065 Re: Mercurial Status? + 1066 Python 2.6.6 status + 1067 Commit Privileges for Ask Solem + 1068 Re: Commit Privileges for Ask Solem + 1069 Re: Commit Privileges for Ask Solem + 1070 Re: Commit Privileges for Ask Solem + 1071 2.6.6 rc 2 >>> s.quit() - '205 news.cwi.nl closing connection. Goodbye.' + '205 Bye!' To post an article from a file (this assumes that the article has valid -headers):: +headers, and that you have right to post on the particular newsgroup):: - >>> s = NNTP('news.cwi.nl') + >>> s = NNTP('news.gmane.org') >>> f = open('/tmp/article') >>> s.post(f) '240 Article posted successfully.' >>> s.quit() - '205 news.cwi.nl closing connection. Goodbye.' + '205 Bye!' The module itself defines the following items: From python-checkins at python.org Tue Sep 7 21:19:33 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 21:19:33 +0200 (CEST) Subject: [Python-checkins] r84595 - python/branches/py3k/Lib/random.py Message-ID: <20100907191933.C4308F92D@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 21:19:33 2010 New Revision: 84595 Log: Minor refactoring and cleanup. Extend looping randrange() technique to subclasses. Modified: python/branches/py3k/Lib/random.py Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Tue Sep 7 21:19:33 2010 @@ -212,25 +212,33 @@ return self.randrange(a, b+1) - def _randbelow(self, n, int=int, _maxwidth=1<= n: r = getrandbits(k) return r - if n >= _maxwidth: + # There's an overriden random() method but no new getrandbits() method, + # so we can only use random() from here. + if k > bpf: _warn("Underlying random() generator does not supply \n" "enough bits to choose from a population range this large") - return int(self.random() * n) + return int(self.random() * n) + random = self.random + N = 1 << k + r = int(N * random()) + while r >= n: + r = int(N * random()) + return r ## -------------------- sequence methods ------------------- @@ -249,16 +257,11 @@ float in [0.0, 1.0); by default, the standard random.random. """ - if random is None: - for i in reversed(range(1, len(x))): - # pick an element in x[:i+1] with which to exchange x[i] - j = self._randbelow(i+1) - x[i], x[j] = x[j], x[i] - else: - for i in reversed(range(1, len(x))): - # pick an element in x[:i+1] with which to exchange x[i] - j = int(random() * (i+1)) - x[i], x[j] = x[j], x[i] + randbelow = self._randbelow + for i in reversed(range(1, len(x))): + # pick an element in x[:i+1] with which to exchange x[i] + j = randbelow(i+1) if random is None else int(random() * (i+1)) + x[i], x[j] = x[j], x[i] def sample(self, population, k): """Chooses k unique random elements from a population sequence or set. @@ -292,7 +295,7 @@ population = tuple(population) if not isinstance(population, _collections.Sequence): raise TypeError("Population must be a sequence or Set. For dicts, use list(d).") - random = self.random + randbelow = self._randbelow n = len(population) if not 0 <= k <= n: raise ValueError("Sample larger than population") @@ -304,16 +307,16 @@ # An n-length list is smaller than a k-length set pool = list(population) for i in range(k): # invariant: non-selected at [0,n-i) - j = self._randbelow(n-i) + j = randbelow(n-i) result[i] = pool[j] pool[j] = pool[n-i-1] # move non-selected item into vacancy else: selected = set() selected_add = selected.add for i in range(k): - j = self._randbelow(n) + j = randbelow(n) while j in selected: - j = self._randbelow(n) + j = randbelow(n) selected_add(j) result[i] = population[j] return result From python-checkins at python.org Tue Sep 7 22:04:42 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 7 Sep 2010 22:04:42 +0200 (CEST) Subject: [Python-checkins] r84596 - python/branches/py3k/Lib/random.py Message-ID: <20100907200442.EBE13EEA43@mail.python.org> Author: raymond.hettinger Date: Tue Sep 7 22:04:42 2010 New Revision: 84596 Log: Neaten-up comments and warning message. Modified: python/branches/py3k/Lib/random.py Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Tue Sep 7 22:04:42 2010 @@ -221,9 +221,8 @@ getrandbits = self.getrandbits # Only call self.getrandbits if the original random() builtin method # has not been overridden or if a new getrandbits() was supplied. - # This assures that the two methods correspond. if type(self.random) is BuiltinMethod or type(getrandbits) is Method: - r = getrandbits(k) # 0 <= r < 2**k + r = getrandbits(k) # 0 <= r < 2**k while r >= n: r = getrandbits(k) return r @@ -231,11 +230,12 @@ # so we can only use random() from here. if k > bpf: _warn("Underlying random() generator does not supply \n" - "enough bits to choose from a population range this large") + "enough bits to choose from a population range this large.\n" + "To remove the range limitation, add a getrandbits() method.") return int(self.random() * n) random = self.random N = 1 << k - r = int(N * random()) + r = int(N * random()) # 0 <= r < 2**k while r >= n: r = int(N * random()) return r From python-checkins at python.org Tue Sep 7 22:42:20 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 22:42:20 +0200 (CEST) Subject: [Python-checkins] r84597 - in python/branches/py3k/Lib/test: support.py test_ssl.py Message-ID: <20100907204220.2B3E9EEA62@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 22:42:19 2010 New Revision: 84597 Log: Issue #8574: better implementation of test.support.transient_internet(). Original patch by Victor. Modified: python/branches/py3k/Lib/test/support.py python/branches/py3k/Lib/test/test_ssl.py Modified: python/branches/py3k/Lib/test/support.py ============================================================================== --- python/branches/py3k/Lib/test/support.py (original) +++ python/branches/py3k/Lib/test/support.py Tue Sep 7 22:42:19 2010 @@ -35,7 +35,7 @@ "check_warnings", "CleanImport", "EnvironmentVarGuard", "TransientResource", "captured_output", "captured_stdout", "time_out", "socket_peer_reset", "ioerror_peer_reset", - "run_with_locale", 'temp_umask', + "run_with_locale", 'temp_umask', "transient_internet", "set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner", "run_unittest", "run_doctest", "threading_setup", "threading_cleanup", "reap_children", "cpython_only", "check_impl_detail", "get_attribute", @@ -775,23 +775,47 @@ else: raise ResourceDenied("an optional resource is not available") - # Context managers that raise ResourceDenied when various issues # with the Internet connection manifest themselves as exceptions. +# XXX deprecate these and use transient_internet() instead time_out = TransientResource(IOError, errno=errno.ETIMEDOUT) socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET) ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET) @contextlib.contextmanager -def transient_internet(): +def transient_internet(resource_name, *, timeout=30.0, errnos=()): """Return a context manager that raises ResourceDenied when various issues with the Internet connection manifest themselves as exceptions.""" - time_out = TransientResource(IOError, errno=errno.ETIMEDOUT) - socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET) - ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET) - with time_out, socket_peer_reset, ioerror_peer_reset: + denied = ResourceDenied("Resource '%s' is not available" % resource_name) + captured_errnos = errnos or (errno.ETIMEDOUT, errno.ECONNRESET) + + def filter_error(err): + if (isinstance(err, socket.timeout) or + getattr(err, 'errno', None) in captured_errnos): + if not verbose: + sys.stderr.write(denied.args[0] + "\n") + raise denied from err + + old_timeout = socket.getdefaulttimeout() + try: + if timeout is not None: + socket.setdefaulttimeout(timeout) yield + except IOError as err: + # socket.error inherits IOError + filter_error(err) + # urllib.request wraps the original socket.error with IOerror: + # + # except socket.error as msg: + # raise IOError('socket error', msg).with_traceback(sys.exc_info()[2]) + if len(err.args) >= 2 and isinstance(err.args[1], socket.error): + filter_error(err.args[1]) + raise + # XXX should we catch generic exceptions and look for their + # __cause__ or __context__? + finally: + socket.setdefaulttimeout(old_timeout) @contextlib.contextmanager Modified: python/branches/py3k/Lib/test/test_ssl.py ============================================================================== --- python/branches/py3k/Lib/test/test_ssl.py (original) +++ python/branches/py3k/Lib/test/test_ssl.py Tue Sep 7 22:42:19 2010 @@ -477,10 +477,10 @@ # NOTE: https://sha256.tbs-internet.com is another possible test host remote = ("sha2.hboeck.de", 443) sha256_cert = os.path.join(os.path.dirname(__file__), "sha256.pem") - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=sha256_cert,) - with support.transient_internet(): + with support.transient_internet("sha2.hboeck.de"): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=sha256_cert,) try: s.connect(remote) if support.verbose: From python-checkins at python.org Tue Sep 7 23:05:49 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 23:05:49 +0200 (CEST) Subject: [Python-checkins] r84598 - in python/branches/py3k: Lib/socket.py Lib/test/test_socket.py Misc/NEWS Message-ID: <20100907210549.D987EEEA6C@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 23:05:49 2010 New Revision: 84598 Log: Issue #9792: In case of connection failure, socket.create_connection() would swallow the exception and raise a new one, making it impossible to fetch the original errno, or to filter timeout errors. Now the original error is re-raised. Modified: python/branches/py3k/Lib/socket.py python/branches/py3k/Lib/test/test_socket.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/socket.py ============================================================================== --- python/branches/py3k/Lib/socket.py (original) +++ python/branches/py3k/Lib/socket.py Tue Sep 7 23:05:49 2010 @@ -297,8 +297,8 @@ An host of '' or port 0 tells the OS to use the default. """ - msg = "getaddrinfo returns an empty list" host, port = address + err = None for res in getaddrinfo(host, port, 0, SOCK_STREAM): af, socktype, proto, canonname, sa = res sock = None @@ -311,9 +311,12 @@ sock.connect(sa) return sock - except error as err: - msg = err + except error as _: + err = _ if sock is not None: sock.close() - raise error(msg) + if err is not None: + raise err + else: + raise error("getaddrinfo returns an empty list") Modified: python/branches/py3k/Lib/test/test_socket.py ============================================================================== --- python/branches/py3k/Lib/test/test_socket.py (original) +++ python/branches/py3k/Lib/test/test_socket.py Tue Sep 7 23:05:49 2010 @@ -13,6 +13,7 @@ import sys import os import array +import contextlib from weakref import proxy import signal @@ -1203,12 +1204,42 @@ class NetworkConnectionNoServer(unittest.TestCase): - def testWithoutServer(self): + class MockSocket(socket.socket): + def connect(self, *args): + raise socket.timeout('timed out') + + @contextlib.contextmanager + def mocked_socket_module(self): + """Return a socket which times out on connect""" + old_socket = socket.socket + socket.socket = self.MockSocket + try: + yield + finally: + socket.socket = old_socket + + def test_connect(self): port = support.find_unused_port() - self.assertRaises( - socket.error, - lambda: socket.create_connection((HOST, port)) - ) + cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + with self.assertRaises(socket.error) as cm: + cli.connect((HOST, port)) + self.assertEqual(cm.exception.errno, errno.ECONNREFUSED) + + def test_create_connection(self): + # Issue #9792: errors raised by create_connection() should have + # a proper errno attribute. + port = support.find_unused_port() + with self.assertRaises(socket.error) as cm: + socket.create_connection((HOST, port)) + self.assertEqual(cm.exception.errno, errno.ECONNREFUSED) + + def test_create_connection_timeout(self): + # Issue #9792: create_connection() should not recast timeout errors + # as generic socket errors. + with self.mocked_socket_module(): + with self.assertRaises(socket.timeout): + socket.create_connection((HOST, 1234)) + @unittest.skipUnless(thread, 'Threading required for this test.') class NetworkConnectionAttributesTest(SocketTCPTest, ThreadableTest): Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 7 23:05:49 2010 @@ -13,6 +13,11 @@ Library ------- +- Issue #9792: In case of connection failure, socket.create_connection() + would swallow the exception and raise a new one, making it impossible + to fetch the original errno, or to filter timeout errors. Now the + original error is re-raised. + - Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True, and the passed buffer was exactly 1024 bytes long, the buffer wouldn't be updated back after the system call. Original patch by Brian Brazil. From python-checkins at python.org Tue Sep 7 23:09:09 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 23:09:09 +0200 (CEST) Subject: [Python-checkins] r84599 - in python/branches/py3k/Lib/test: support.py test_robotparser.py Message-ID: <20100907210909.C23FFEEA6C@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 23:09:09 2010 New Revision: 84599 Log: Improve transient_internet() again to detect more network errors, and use it in test_robotparser. Fixes #8574. Modified: python/branches/py3k/Lib/test/support.py python/branches/py3k/Lib/test/test_robotparser.py Modified: python/branches/py3k/Lib/test/support.py ============================================================================== --- python/branches/py3k/Lib/test/support.py (original) +++ python/branches/py3k/Lib/test/support.py Tue Sep 7 23:09:09 2010 @@ -787,8 +787,18 @@ def transient_internet(resource_name, *, timeout=30.0, errnos=()): """Return a context manager that raises ResourceDenied when various issues with the Internet connection manifest themselves as exceptions.""" + default_errnos = [ + ('ECONNREFUSED', 111), + ('ECONNRESET', 104), + ('ENETUNREACH', 101), + ('ETIMEDOUT', 110), + ] + denied = ResourceDenied("Resource '%s' is not available" % resource_name) - captured_errnos = errnos or (errno.ETIMEDOUT, errno.ECONNRESET) + captured_errnos = errnos + if not captured_errnos: + captured_errnos = [getattr(errno, name, num) + for (name, num) in default_errnos] def filter_error(err): if (isinstance(err, socket.timeout) or @@ -803,14 +813,20 @@ socket.setdefaulttimeout(timeout) yield except IOError as err: - # socket.error inherits IOError + # urllib can wrap original socket errors multiple times (!), we must + # unwrap to get at the original error. + while True: + a = err.args + if len(a) >= 1 and isinstance(a[0], IOError): + err = a[0] + # The error can also be wrapped as args[1]: + # except socket.error as msg: + # raise IOError('socket error', msg).with_traceback(sys.exc_info()[2]) + elif len(a) >= 2 and isinstance(a[1], IOError): + err = a[1] + else: + break filter_error(err) - # urllib.request wraps the original socket.error with IOerror: - # - # except socket.error as msg: - # raise IOError('socket error', msg).with_traceback(sys.exc_info()[2]) - if len(err.args) >= 2 and isinstance(err.args[1], socket.error): - filter_error(err.args[1]) raise # XXX should we catch generic exceptions and look for their # __cause__ or __context__? Modified: python/branches/py3k/Lib/test/test_robotparser.py ============================================================================== --- python/branches/py3k/Lib/test/test_robotparser.py (original) +++ python/branches/py3k/Lib/test/test_robotparser.py Tue Sep 7 23:09:09 2010 @@ -235,23 +235,24 @@ def testPasswordProtectedSite(self): support.requires('network') - # XXX it depends on an external resource which could be unavailable - url = 'http://mueblesmoraleda.com' - parser = urllib.robotparser.RobotFileParser() - parser.set_url(url) - try: - parser.read() - except URLError: - self.skipTest('%s is unavailable' % url) - self.assertEqual(parser.can_fetch("*", url+"/robots.txt"), False) + with support.transient_internet('mueblesmoraleda.com'): + url = 'http://mueblesmoraleda.com' + parser = urllib.robotparser.RobotFileParser() + parser.set_url(url) + try: + parser.read() + except URLError: + self.skipTest('%s is unavailable' % url) + self.assertEqual(parser.can_fetch("*", url+"/robots.txt"), False) def testPythonOrg(self): support.requires('network') - parser = urllib.robotparser.RobotFileParser( - "http://www.python.org/robots.txt") - parser.read() - self.assertTrue(parser.can_fetch("*", - "http://www.python.org/robots.txt")) + with support.transient_internet('www.python.org'): + parser = urllib.robotparser.RobotFileParser( + "http://www.python.org/robots.txt") + parser.read() + self.assertTrue( + parser.can_fetch("*", "http://www.python.org/robots.txt")) def test_main(): support.run_unittest(NetworkTestCase) From python-checkins at python.org Tue Sep 7 23:22:56 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 23:22:56 +0200 (CEST) Subject: [Python-checkins] r84600 - in python/branches/release31-maint: Lib/socket.py Lib/test/support.py Lib/test/test_robotparser.py Lib/test/test_socket.py Lib/test/test_ssl.py Misc/NEWS Message-ID: <20100907212256.4EC81EE9EA@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 23:22:56 2010 New Revision: 84600 Log: Merged revisions 84597-84599 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84597 | antoine.pitrou | 2010-09-07 22:42:19 +0200 (mar., 07 sept. 2010) | 5 lines Issue #8574: better implementation of test.support.transient_internet(). Original patch by Victor. ........ r84598 | antoine.pitrou | 2010-09-07 23:05:49 +0200 (mar., 07 sept. 2010) | 6 lines Issue #9792: In case of connection failure, socket.create_connection() would swallow the exception and raise a new one, making it impossible to fetch the original errno, or to filter timeout errors. Now the original error is re-raised. ........ r84599 | antoine.pitrou | 2010-09-07 23:09:09 +0200 (mar., 07 sept. 2010) | 4 lines Improve transient_internet() again to detect more network errors, and use it in test_robotparser. Fixes #8574. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/socket.py python/branches/release31-maint/Lib/test/support.py python/branches/release31-maint/Lib/test/test_robotparser.py python/branches/release31-maint/Lib/test/test_socket.py python/branches/release31-maint/Lib/test/test_ssl.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/socket.py ============================================================================== --- python/branches/release31-maint/Lib/socket.py (original) +++ python/branches/release31-maint/Lib/socket.py Tue Sep 7 23:22:56 2010 @@ -287,8 +287,8 @@ is used. """ - msg = "getaddrinfo returns an empty list" host, port = address + err = None for res in getaddrinfo(host, port, 0, SOCK_STREAM): af, socktype, proto, canonname, sa = res sock = None @@ -299,9 +299,12 @@ sock.connect(sa) return sock - except error as err: - msg = err + except error as _: + err = _ if sock is not None: sock.close() - raise error(msg) + if err is not None: + raise err + else: + raise error("getaddrinfo returns an empty list") Modified: python/branches/release31-maint/Lib/test/support.py ============================================================================== --- python/branches/release31-maint/Lib/test/support.py (original) +++ python/branches/release31-maint/Lib/test/support.py Tue Sep 7 23:22:56 2010 @@ -17,19 +17,21 @@ import importlib import collections -__all__ = ["Error", "TestFailed", "ResourceDenied", "import_module", - "verbose", "use_resources", "max_memuse", "record_original_stdout", - "get_original_stdout", "unload", "unlink", "rmtree", "forget", - "is_resource_enabled", "requires", "find_unused_port", "bind_port", - "fcmp", "is_jython", "TESTFN", "HOST", "FUZZ", "findfile", "verify", - "vereq", "sortdict", "check_syntax_error", "open_urlresource", - "check_warnings", "CleanImport", "EnvironmentVarGuard", - "TransientResource", "captured_output", "captured_stdout", - "time_out", "socket_peer_reset", "ioerror_peer_reset", - "run_with_locale", - "set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner", - "run_unittest", "run_doctest", "threading_setup", "threading_cleanup", - "reap_children", "cpython_only", "check_impl_detail", "get_attribute"] +__all__ = [ + "Error", "TestFailed", "ResourceDenied", "import_module", + "verbose", "use_resources", "max_memuse", "record_original_stdout", + "get_original_stdout", "unload", "unlink", "rmtree", "forget", + "is_resource_enabled", "requires", "find_unused_port", "bind_port", + "fcmp", "is_jython", "TESTFN", "HOST", "FUZZ", "findfile", "verify", + "vereq", "sortdict", "check_syntax_error", "open_urlresource", + "check_warnings", "CleanImport", "EnvironmentVarGuard", + "TransientResource", "captured_output", "captured_stdout", + "time_out", "socket_peer_reset", "ioerror_peer_reset", + "run_with_locale", "transient_internet", + "set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner", + "run_unittest", "run_doctest", "threading_setup", "threading_cleanup", + "reap_children", "cpython_only", "check_impl_detail", "get_attribute", + ] class Error(Exception): """Base class for regression test exceptions.""" @@ -604,23 +606,63 @@ else: raise ResourceDenied("an optional resource is not available") - # Context managers that raise ResourceDenied when various issues # with the Internet connection manifest themselves as exceptions. +# XXX deprecate these and use transient_internet() instead time_out = TransientResource(IOError, errno=errno.ETIMEDOUT) socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET) ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET) @contextlib.contextmanager -def transient_internet(): +def transient_internet(resource_name, *, timeout=30.0, errnos=()): """Return a context manager that raises ResourceDenied when various issues with the Internet connection manifest themselves as exceptions.""" - time_out = TransientResource(IOError, errno=errno.ETIMEDOUT) - socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET) - ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET) - with time_out, socket_peer_reset, ioerror_peer_reset: + default_errnos = [ + ('ECONNREFUSED', 111), + ('ECONNRESET', 104), + ('ENETUNREACH', 101), + ('ETIMEDOUT', 110), + ] + + denied = ResourceDenied("Resource '%s' is not available" % resource_name) + captured_errnos = errnos + if not captured_errnos: + captured_errnos = [getattr(errno, name, num) + for (name, num) in default_errnos] + + def filter_error(err): + if (isinstance(err, socket.timeout) or + getattr(err, 'errno', None) in captured_errnos): + if not verbose: + sys.stderr.write(denied.args[0] + "\n") + raise denied from err + + old_timeout = socket.getdefaulttimeout() + try: + if timeout is not None: + socket.setdefaulttimeout(timeout) yield + except IOError as err: + # urllib can wrap original socket errors multiple times (!), we must + # unwrap to get at the original error. + while True: + a = err.args + if len(a) >= 1 and isinstance(a[0], IOError): + err = a[0] + # The error can also be wrapped as args[1]: + # except socket.error as msg: + # raise IOError('socket error', msg).with_traceback(sys.exc_info()[2]) + elif len(a) >= 2 and isinstance(a[1], IOError): + err = a[1] + else: + break + filter_error(err) + raise + # XXX should we catch generic exceptions and look for their + # __cause__ or __context__? + finally: + socket.setdefaulttimeout(old_timeout) @contextlib.contextmanager Modified: python/branches/release31-maint/Lib/test/test_robotparser.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_robotparser.py (original) +++ python/branches/release31-maint/Lib/test/test_robotparser.py Tue Sep 7 23:22:56 2010 @@ -235,23 +235,24 @@ def testPasswordProtectedSite(self): support.requires('network') - # XXX it depends on an external resource which could be unavailable - url = 'http://mueblesmoraleda.com' - parser = urllib.robotparser.RobotFileParser() - parser.set_url(url) - try: - parser.read() - except URLError: - self.skipTest('%s is unavailable' % url) - self.assertEqual(parser.can_fetch("*", url+"/robots.txt"), False) + with support.transient_internet('mueblesmoraleda.com'): + url = 'http://mueblesmoraleda.com' + parser = urllib.robotparser.RobotFileParser() + parser.set_url(url) + try: + parser.read() + except URLError: + self.skipTest('%s is unavailable' % url) + self.assertEqual(parser.can_fetch("*", url+"/robots.txt"), False) def testPythonOrg(self): support.requires('network') - parser = urllib.robotparser.RobotFileParser( - "http://www.python.org/robots.txt") - parser.read() - self.assertTrue(parser.can_fetch("*", - "http://www.python.org/robots.txt")) + with support.transient_internet('www.python.org'): + parser = urllib.robotparser.RobotFileParser( + "http://www.python.org/robots.txt") + parser.read() + self.assertTrue( + parser.can_fetch("*", "http://www.python.org/robots.txt")) def test_main(): support.run_unittest(NetworkTestCase) Modified: python/branches/release31-maint/Lib/test/test_socket.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_socket.py (original) +++ python/branches/release31-maint/Lib/test/test_socket.py Tue Sep 7 23:22:56 2010 @@ -14,6 +14,7 @@ import sys import os import array +import contextlib from weakref import proxy import signal @@ -1026,12 +1027,48 @@ class NetworkConnectionNoServer(unittest.TestCase): - def testWithoutServer(self): + class MockSocket(socket.socket): + def connect(self, *args): + raise socket.timeout('timed out') + + @contextlib.contextmanager + def mocked_socket_module(self): + """Return a socket which times out on connect""" + old_socket = socket.socket + socket.socket = self.MockSocket + try: + yield + finally: + socket.socket = old_socket + + def test_connect(self): port = support.find_unused_port() - self.assertRaises( - socket.error, - lambda: socket.create_connection((HOST, port)) - ) + cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + cli.connect((HOST, port)) + except socket.error as err: + self.assertEqual(err.errno, errno.ECONNREFUSED) + else: + self.fail("socket.error not raised") + + def test_create_connection(self): + # Issue #9792: errors raised by create_connection() should have + # a proper errno attribute. + port = support.find_unused_port() + try: + socket.create_connection((HOST, port)) + except socket.error as err: + self.assertEqual(err.errno, errno.ECONNREFUSED) + else: + self.fail("socket.error not raised") + + def test_create_connection_timeout(self): + # Issue #9792: create_connection() should not recast timeout errors + # as generic socket errors. + with self.mocked_socket_module(): + with self.assertRaises(socket.timeout): + socket.create_connection((HOST, 1234)) + class NetworkConnectionAttributesTest(SocketTCPTest, ThreadableTest): Modified: python/branches/release31-maint/Lib/test/test_ssl.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_ssl.py (original) +++ python/branches/release31-maint/Lib/test/test_ssl.py Tue Sep 7 23:22:56 2010 @@ -218,10 +218,10 @@ # NOTE: https://sha256.tbs-internet.com is another possible test host remote = ("sha2.hboeck.de", 443) sha256_cert = os.path.join(os.path.dirname(__file__), "sha256.pem") - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=sha256_cert,) - with support.transient_internet(): + with support.transient_internet("sha2.hboeck.de"): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=sha256_cert,) try: s.connect(remote) if support.verbose: Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Sep 7 23:22:56 2010 @@ -105,6 +105,11 @@ Library ------- +- Issue #9792: In case of connection failure, socket.create_connection() + would swallow the exception and raise a new one, making it impossible + to fetch the original errno, or to filter timeout errors. Now the + original error is re-raised. + - Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True, and the passed buffer was exactly 1024 bytes long, the buffer wouldn't be updated back after the system call. Original patch by Brian Brazil. From python-checkins at python.org Tue Sep 7 23:31:18 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Tue, 7 Sep 2010 23:31:18 +0200 (CEST) Subject: [Python-checkins] r84601 - in python/branches/py3k: Doc/library/os.rst Doc/whatsnew/3.2.rst Lib/test/test_os.py Misc/ACKS Misc/NEWS Modules/posixmodule.c Message-ID: <20100907213118.2712CEEA6B@mail.python.org> Author: amaury.forgeotdarc Date: Tue Sep 7 23:31:17 2010 New Revision: 84601 Log: #6394: Add os.getppid() support for Windows. Modified: python/branches/py3k/Doc/library/os.rst python/branches/py3k/Doc/whatsnew/3.2.rst python/branches/py3k/Lib/test/test_os.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/posixmodule.c Modified: python/branches/py3k/Doc/library/os.rst ============================================================================== --- python/branches/py3k/Doc/library/os.rst (original) +++ python/branches/py3k/Doc/library/os.rst Tue Sep 7 23:31:17 2010 @@ -281,10 +281,14 @@ .. index:: single: process; id of parent - Return the parent's process id. + Return the parent's process id. When the parent process has exited, on Unix + the id returned is the one of the init process (1), on Windows it is still + the same id, which may be already reused by another process. - Availability: Unix. + Availability: Unix, Windows + .. versionchanged:: 3.2 + Added support for Windows. .. function:: getresuid() Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Tue Sep 7 23:31:17 2010 @@ -312,6 +312,11 @@ (Patch by Adam Jackson; :issue:`7647`.) +* :func:`os.getppid` is now supported on Windows. Note that it will continue to + return the same pid even after the parent process has exited. + + (Patch by Jon Anglin; :issue:`6394`.) + * The :func:`shutil.copytree` function has two new options: * *ignore_dangling_symlinks*: when ``symlinks=False`` so that the function Modified: python/branches/py3k/Lib/test/test_os.py ============================================================================== --- python/branches/py3k/Lib/test/test_os.py (original) +++ python/branches/py3k/Lib/test/test_os.py Tue Sep 7 23:31:17 2010 @@ -1183,6 +1183,17 @@ check('iso-8859-15', b'\xef\xa4', '\xef\u20ac') +class PidTests(unittest.TestCase): + @unittest.skipUnless(hasattr(os, 'getppid'), "test needs os.getppid") + def test_getppid(self): + p = subprocess.Popen([sys.executable, '-c', + 'import os; print(os.getppid())'], + stdout=subprocess.PIPE) + stdout, _ = p.communicate() + # We are the parent of our subprocess + self.assertEqual(int(stdout), os.getpid()) + + def test_main(): support.run_unittest( FileTests, @@ -1200,6 +1211,7 @@ Win32KillTests, Win32SymlinkTests, FSEncodingTests, + PidTests, ) if __name__ == "__main__": Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Tue Sep 7 23:31:17 2010 @@ -28,6 +28,7 @@ Erik Anders?n Oliver Andrich Ross Andrus +Jon Anglin ?ric Araujo Jason Asbahr David Ascher Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 7 23:31:17 2010 @@ -13,6 +13,10 @@ Library ------- +- Issue #6394: os.getppid() is now supported on Windows. Note that it will + still return the id of the parent process after it has exited. This process + id may even have been reused by another unrelated process. + - Issue #9792: In case of connection failure, socket.create_connection() would swallow the exception and raise a new one, making it impossible to fetch the original errno, or to filter timeout errors. Now the Modified: python/branches/py3k/Modules/posixmodule.c ============================================================================== --- python/branches/py3k/Modules/posixmodule.c (original) +++ python/branches/py3k/Modules/posixmodule.c Tue Sep 7 23:31:17 2010 @@ -121,6 +121,7 @@ #else #ifdef _MSC_VER /* Microsoft compiler */ #define HAVE_GETCWD 1 +#define HAVE_GETPPID 1 #define HAVE_SPAWNV 1 #define HAVE_EXECV 1 #define HAVE_PIPE 1 @@ -4363,16 +4364,65 @@ #endif /* HAVE_SETPGRP */ #ifdef HAVE_GETPPID + +#ifdef MS_WINDOWS +#include + +static PyObject* +win32_getppid() +{ + HANDLE snapshot; + pid_t mypid; + PyObject* result = NULL; + BOOL have_record; + PROCESSENTRY32 pe; + + mypid = getpid(); /* This function never fails */ + + snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) + return PyErr_SetFromWindowsErr(GetLastError()); + + pe.dwSize = sizeof(pe); + have_record = Process32First(snapshot, &pe); + while (have_record) { + if (mypid == (pid_t)pe.th32ProcessID) { + /* We could cache the ulong value in a static variable. */ + result = PyLong_FromPid((pid_t)pe.th32ParentProcessID); + break; + } + + have_record = Process32Next(snapshot, &pe); + } + + /* If our loop exits and our pid was not found (result will be NULL) + * then GetLastError will return ERROR_NO_MORE_FILES. This is an + * error anyway, so let's raise it. */ + if (!result) + result = PyErr_SetFromWindowsErr(GetLastError()); + + CloseHandle(snapshot); + + return result; +} +#endif /*MS_WINDOWS*/ + PyDoc_STRVAR(posix_getppid__doc__, "getppid() -> ppid\n\n\ -Return the parent's process id."); +Return the parent's process id. If the parent process has already exited,\n\ +Windows machines will still return its id; others systems will return the id\n\ +of the 'init' process (1)."); static PyObject * posix_getppid(PyObject *self, PyObject *noargs) { +#ifdef MS_WINDOWS + return win32_getppid(); +#else return PyLong_FromPid(getppid()); -} #endif +} +#endif /* HAVE_GETPPID */ #ifdef HAVE_GETLOGIN From python-checkins at python.org Tue Sep 7 23:35:36 2010 From: python-checkins at python.org (eric.araujo) Date: Tue, 7 Sep 2010 23:35:36 +0200 (CEST) Subject: [Python-checkins] r84602 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100907213536.21969EEA5A@mail.python.org> Author: eric.araujo Date: Tue Sep 7 23:35:35 2010 New Revision: 84602 Log: Fix typo in whatsnew (#9793) Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Tue Sep 7 23:35:35 2010 @@ -194,7 +194,7 @@ by calling :func:`getattr` and throwing away the results. This is necessary because dynamic attribute creation is possible using :meth:`__getattribute__` or :meth:`__getattr__`. If :func:`hasattr` were to just scan instance and class - dictionaries it would miss the dynmaic methods and make it difficult to + dictionaries it would miss the dynamic methods and make it difficult to implement proxy objects. (Discovered by Yury Selivanov and fixed by Benjamin Peterson; :issue:`9666`.) From python-checkins at python.org Tue Sep 7 23:40:25 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 23:40:25 +0200 (CEST) Subject: [Python-checkins] r84603 - in python/branches/release27-maint: Lib/socket.py Lib/test/test_robotparser.py Lib/test/test_socket.py Lib/test/test_ssl.py Lib/test/test_support.py Lib/test/test_urllib2net.py Misc/NEWS Message-ID: <20100907214025.CF06CEEA51@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 23:40:25 2010 New Revision: 84603 Log: Merged revisions 84597-84599 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84597 | antoine.pitrou | 2010-09-07 22:42:19 +0200 (mar., 07 sept. 2010) | 5 lines Issue #8574: better implementation of test.support.transient_internet(). Original patch by Victor. ........ r84598 | antoine.pitrou | 2010-09-07 23:05:49 +0200 (mar., 07 sept. 2010) | 6 lines Issue #9792: In case of connection failure, socket.create_connection() would swallow the exception and raise a new one, making it impossible to fetch the original errno, or to filter timeout errors. Now the original error is re-raised. ........ r84599 | antoine.pitrou | 2010-09-07 23:09:09 +0200 (mar., 07 sept. 2010) | 4 lines Improve transient_internet() again to detect more network errors, and use it in test_robotparser. Fixes #8574. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/socket.py python/branches/release27-maint/Lib/test/test_robotparser.py python/branches/release27-maint/Lib/test/test_socket.py python/branches/release27-maint/Lib/test/test_ssl.py python/branches/release27-maint/Lib/test/test_support.py python/branches/release27-maint/Lib/test/test_urllib2net.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/socket.py ============================================================================== --- python/branches/release27-maint/Lib/socket.py (original) +++ python/branches/release27-maint/Lib/socket.py Tue Sep 7 23:40:25 2010 @@ -548,8 +548,8 @@ An host of '' or port 0 tells the OS to use the default. """ - msg = "getaddrinfo returns an empty list" host, port = address + err = None for res in getaddrinfo(host, port, 0, SOCK_STREAM): af, socktype, proto, canonname, sa = res sock = None @@ -562,8 +562,12 @@ sock.connect(sa) return sock - except error, msg: + except error as _: + err = _ if sock is not None: sock.close() - raise error, msg + if err is not None: + raise err + else: + raise error("getaddrinfo returns an empty list") Modified: python/branches/release27-maint/Lib/test/test_robotparser.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_robotparser.py (original) +++ python/branches/release27-maint/Lib/test/test_robotparser.py Tue Sep 7 23:40:25 2010 @@ -232,23 +232,24 @@ def testPasswordProtectedSite(self): test_support.requires('network') - # XXX it depends on an external resource which could be unavailable - url = 'http://mueblesmoraleda.com' - parser = robotparser.RobotFileParser() - parser.set_url(url) - try: - parser.read() - except IOError: - self.skipTest('%s is unavailable' % url) - self.assertEqual(parser.can_fetch("*", url+"/robots.txt"), False) + with test_support.transient_internet('mueblesmoraleda.com'): + url = 'http://mueblesmoraleda.com' + parser = robotparser.RobotFileParser() + parser.set_url(url) + try: + parser.read() + except IOError: + self.skipTest('%s is unavailable' % url) + self.assertEqual(parser.can_fetch("*", url+"/robots.txt"), False) def testPythonOrg(self): test_support.requires('network') - parser = robotparser.RobotFileParser( - "http://www.python.org/robots.txt") - parser.read() - self.assertTrue(parser.can_fetch("*", - "http://www.python.org/robots.txt")) + with test_support.transient_internet('www.python.org'): + parser = robotparser.RobotFileParser( + "http://www.python.org/robots.txt") + parser.read() + self.assertTrue( + parser.can_fetch("*", "http://www.python.org/robots.txt")) def test_main(): Modified: python/branches/release27-maint/Lib/test/test_socket.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_socket.py (original) +++ python/branches/release27-maint/Lib/test/test_socket.py Tue Sep 7 23:40:25 2010 @@ -12,6 +12,7 @@ import sys import os import array +import contextlib from weakref import proxy import signal @@ -1048,12 +1049,42 @@ """ class NetworkConnectionNoServer(unittest.TestCase): - def testWithoutServer(self): + class MockSocket(socket.socket): + def connect(self, *args): + raise socket.timeout('timed out') + + @contextlib.contextmanager + def mocked_socket_module(self): + """Return a socket which times out on connect""" + old_socket = socket.socket + socket.socket = self.MockSocket + try: + yield + finally: + socket.socket = old_socket + + def test_connect(self): port = test_support.find_unused_port() - self.assertRaises( - socket.error, - lambda: socket.create_connection((HOST, port)) - ) + cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + with self.assertRaises(socket.error) as cm: + cli.connect((HOST, port)) + self.assertEqual(cm.exception.errno, errno.ECONNREFUSED) + + def test_create_connection(self): + # Issue #9792: errors raised by create_connection() should have + # a proper errno attribute. + port = test_support.find_unused_port() + with self.assertRaises(socket.error) as cm: + socket.create_connection((HOST, port)) + self.assertEqual(cm.exception.errno, errno.ECONNREFUSED) + + def test_create_connection_timeout(self): + # Issue #9792: create_connection() should not recast timeout errors + # as generic socket errors. + with self.mocked_socket_module(): + with self.assertRaises(socket.timeout): + socket.create_connection((HOST, 1234)) + @unittest.skipUnless(thread, 'Threading required for this test.') class NetworkConnectionAttributesTest(SocketTCPTest, ThreadableTest): Modified: python/branches/release27-maint/Lib/test/test_ssl.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_ssl.py (original) +++ python/branches/release27-maint/Lib/test/test_ssl.py Tue Sep 7 23:40:25 2010 @@ -287,10 +287,10 @@ # NOTE: https://sha256.tbs-internet.com is another possible test host remote = ("sha2.hboeck.de", 443) sha256_cert = os.path.join(os.path.dirname(__file__), "sha256.pem") - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=sha256_cert,) - with test_support.transient_internet(): + with test_support.transient_internet("sha2.hboeck.de"): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=sha256_cert,) try: s.connect(remote) if test_support.verbose: Modified: python/branches/release27-maint/Lib/test/test_support.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_support.py (original) +++ python/branches/release27-maint/Lib/test/test_support.py Tue Sep 7 23:40:25 2010 @@ -750,32 +750,55 @@ raise ResourceDenied("an optional resource is not available") -_transients = { - IOError: (errno.ECONNRESET, errno.ETIMEDOUT), - socket.error: (errno.ECONNRESET,), - socket.gaierror: [getattr(socket, t) - for t in ('EAI_NODATA', 'EAI_NONAME') - if hasattr(socket, t)], - } @contextlib.contextmanager -def transient_internet(): +def transient_internet(resource_name, timeout=30.0, errnos=()): """Return a context manager that raises ResourceDenied when various issues - with the Internet connection manifest themselves as exceptions. + with the Internet connection manifest themselves as exceptions.""" + default_errnos = [ + ('ECONNREFUSED', 111), + ('ECONNRESET', 104), + ('ENETUNREACH', 101), + ('ETIMEDOUT', 110), + ] + + denied = ResourceDenied("Resource '%s' is not available" % resource_name) + captured_errnos = errnos + if not captured_errnos: + captured_errnos = [getattr(errno, name, num) + for (name, num) in default_errnos] + + def filter_error(err): + if (isinstance(err, socket.timeout) or + getattr(err, 'errno', None) in captured_errnos): + if not verbose: + sys.stderr.write(denied.args[0] + "\n") + raise denied - Errors caught: - timeout IOError errno = ETIMEDOUT - socket reset socket.error, IOError errno = ECONNRESET - dns no data socket.gaierror errno = EAI_NODATA - dns no name socket.gaierror errno = EAI_NONAME - """ + old_timeout = socket.getdefaulttimeout() try: + if timeout is not None: + socket.setdefaulttimeout(timeout) yield - except tuple(_transients) as err: - for errtype in _transients: - if isinstance(err, errtype) and err.errno in _transients[errtype]: - raise ResourceDenied("could not establish network " - "connection ({})".format(err)) + except IOError as err: + # urllib can wrap original socket errors multiple times (!), we must + # unwrap to get at the original error. + while True: + a = err.args + if len(a) >= 1 and isinstance(a[0], IOError): + err = a[0] + # The error can also be wrapped as args[1]: + # except socket.error as msg: + # raise IOError('socket error', msg).with_traceback(sys.exc_info()[2]) + elif len(a) >= 2 and isinstance(a[1], IOError): + err = a[1] + else: + break + filter_error(err) raise + # XXX should we catch generic exceptions and look for their + # __cause__ or __context__? + finally: + socket.setdefaulttimeout(old_timeout) @contextlib.contextmanager Modified: python/branches/release27-maint/Lib/test/test_urllib2net.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_urllib2net.py (original) +++ python/branches/release27-maint/Lib/test/test_urllib2net.py Tue Sep 7 23:40:25 2010 @@ -192,7 +192,7 @@ raise else: try: - with test_support.transient_internet(): + with test_support.transient_internet(url): buf = f.read() debug("read %d bytes" % len(buf)) except socket.timeout: Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Sep 7 23:40:25 2010 @@ -36,6 +36,11 @@ Library ------- +- Issue #9792: In case of connection failure, socket.create_connection() + would swallow the exception and raise a new one, making it impossible + to fetch the original errno, or to filter timeout errors. Now the + original error is re-raised. + - Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True, and the passed buffer was exactly 1024 bytes long, the buffer wouldn't be updated back after the system call. Original patch by Brian Brazil. From python-checkins at python.org Tue Sep 7 23:43:31 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 23:43:31 +0200 (CEST) Subject: [Python-checkins] r84604 - python/branches/py3k/Lib/test/support.py Message-ID: <20100907214331.C67B7EEA64@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 23:43:31 2010 New Revision: 84604 Log: Also catch some gaierrors Modified: python/branches/py3k/Lib/test/support.py Modified: python/branches/py3k/Lib/test/support.py ============================================================================== --- python/branches/py3k/Lib/test/support.py (original) +++ python/branches/py3k/Lib/test/support.py Tue Sep 7 23:43:31 2010 @@ -793,16 +793,25 @@ ('ENETUNREACH', 101), ('ETIMEDOUT', 110), ] + default_gai_errnos = [ + ('EAI_NONAME', -2), + ('EAI_NODATA', -5), + ] denied = ResourceDenied("Resource '%s' is not available" % resource_name) captured_errnos = errnos + gai_errnos = [] if not captured_errnos: captured_errnos = [getattr(errno, name, num) for (name, num) in default_errnos] + gai_errnos = [getattr(socket, name, num) + for (name, num) in default_gai_errnos] def filter_error(err): + n = getattr(err, 'errno', None) if (isinstance(err, socket.timeout) or - getattr(err, 'errno', None) in captured_errnos): + (isinstance(err, socket.gaierror) and n in gai_errnos) or + n in captured_errnos): if not verbose: sys.stderr.write(denied.args[0] + "\n") raise denied from err From python-checkins at python.org Tue Sep 7 23:44:17 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 23:44:17 +0200 (CEST) Subject: [Python-checkins] r84605 - in python/branches/release31-maint: Lib/test/support.py Message-ID: <20100907214417.3EEACEEA66@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 23:44:17 2010 New Revision: 84605 Log: Merged revisions 84604 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84604 | antoine.pitrou | 2010-09-07 23:43:31 +0200 (mar., 07 sept. 2010) | 3 lines Also catch some gaierrors ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/support.py Modified: python/branches/release31-maint/Lib/test/support.py ============================================================================== --- python/branches/release31-maint/Lib/test/support.py (original) +++ python/branches/release31-maint/Lib/test/support.py Tue Sep 7 23:44:17 2010 @@ -624,16 +624,25 @@ ('ENETUNREACH', 101), ('ETIMEDOUT', 110), ] + default_gai_errnos = [ + ('EAI_NONAME', -2), + ('EAI_NODATA', -5), + ] denied = ResourceDenied("Resource '%s' is not available" % resource_name) captured_errnos = errnos + gai_errnos = [] if not captured_errnos: captured_errnos = [getattr(errno, name, num) for (name, num) in default_errnos] + gai_errnos = [getattr(socket, name, num) + for (name, num) in default_gai_errnos] def filter_error(err): + n = getattr(err, 'errno', None) if (isinstance(err, socket.timeout) or - getattr(err, 'errno', None) in captured_errnos): + (isinstance(err, socket.gaierror) and n in gai_errnos) or + n in captured_errnos): if not verbose: sys.stderr.write(denied.args[0] + "\n") raise denied from err From python-checkins at python.org Tue Sep 7 23:46:05 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 7 Sep 2010 23:46:05 +0200 (CEST) Subject: [Python-checkins] r84606 - in python/branches/release27-maint: Lib/test/test_support.py Message-ID: <20100907214605.A3C6DEEA66@mail.python.org> Author: antoine.pitrou Date: Tue Sep 7 23:46:05 2010 New Revision: 84606 Log: Merged revisions 84604 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84604 | antoine.pitrou | 2010-09-07 23:43:31 +0200 (mar., 07 sept. 2010) | 3 lines Also catch some gaierrors ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_support.py Modified: python/branches/release27-maint/Lib/test/test_support.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_support.py (original) +++ python/branches/release27-maint/Lib/test/test_support.py Tue Sep 7 23:46:05 2010 @@ -760,16 +760,25 @@ ('ENETUNREACH', 101), ('ETIMEDOUT', 110), ] + default_gai_errnos = [ + ('EAI_NONAME', -2), + ('EAI_NODATA', -5), + ] denied = ResourceDenied("Resource '%s' is not available" % resource_name) captured_errnos = errnos + gai_errnos = [] if not captured_errnos: captured_errnos = [getattr(errno, name, num) for (name, num) in default_errnos] + gai_errnos = [getattr(socket, name, num) + for (name, num) in default_gai_errnos] def filter_error(err): + n = getattr(err, 'errno', None) if (isinstance(err, socket.timeout) or - getattr(err, 'errno', None) in captured_errnos): + (isinstance(err, socket.gaierror) and n in gai_errnos) or + n in captured_errnos): if not verbose: sys.stderr.write(denied.args[0] + "\n") raise denied From barry at python.org Tue Sep 7 23:58:04 2010 From: barry at python.org (Barry Warsaw) Date: Tue, 7 Sep 2010 17:58:04 -0400 Subject: [Python-checkins] r84536 - sandbox/trunk/release/release.py In-Reply-To: <20100905182846.4E3DAEEA02@mail.python.org> References: <20100905182846.4E3DAEEA02@mail.python.org> Message-ID: <20100907175804.1eba70d7@mission> On Sep 05, 2010, at 08:28 PM, georg.brandl wrote: >Author: georg.brandl >Date: Sun Sep 5 20:28:46 2010 >New Revision: 84536 > >Log: >Fix after changing NEWS layout. > >Modified: > sandbox/trunk/release/release.py > >Modified: sandbox/trunk/release/release.py >============================================================================== >--- sandbox/trunk/release/release.py (original) >+++ sandbox/trunk/release/release.py Sun Sep 5 20:28:46 2010 >@@ -396,13 +396,13 @@ > with open('Misc/NEWS', encoding="utf-8") as fp: > lines = fp.readlines() > for i, line in enumerate(lines): >- if line.startswith("(editors"): >+ if line.startswith("Python News"): > start = i > if line.startswith("What's"): > end = i > break > with open('Misc/NEWS', 'w', encoding="utf-8") as fp: >- fp.writelines(lines[:start+1]) >+ fp.writelines(lines[:start+2]) > fp.write(NEWS_TEMPLATE) > fp.writelines(lines[end-1:]) > print("Please fill in the the name of the next version.") Will this still work with the Python 2.7 NEWS file? -Barry -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 836 bytes Desc: not available URL: From python-checkins at python.org Wed Sep 8 00:06:17 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 8 Sep 2010 00:06:17 +0200 (CEST) Subject: [Python-checkins] r84607 - in python/branches/py3k: Lib/_threading_local.py Lib/test/test_threading_local.py Misc/NEWS Message-ID: <20100907220617.E2FC8EEA31@mail.python.org> Author: antoine.pitrou Date: Wed Sep 8 00:06:17 2010 New Revision: 84607 Log: Issue #9707: Rewritten reference implementation of threading.local which is friendlier towards reference cycles. This change is not normally visible since an optimized C implementation (_thread._local) is used instead. Modified: python/branches/py3k/Lib/_threading_local.py python/branches/py3k/Lib/test/test_threading_local.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/_threading_local.py ============================================================================== --- python/branches/py3k/Lib/_threading_local.py (original) +++ python/branches/py3k/Lib/_threading_local.py Wed Sep 8 00:06:17 2010 @@ -132,6 +132,9 @@ >>> del mydata """ +from weakref import ref +from contextlib import contextmanager + __all__ = ["local"] # We need to use objects from the threading module, but the threading @@ -139,112 +142,105 @@ # isn't compiled in to the `thread` module. This creates potential problems # with circular imports. For that reason, we don't import `threading` # until the bottom of this file (a hack sufficient to worm around the -# potential problems). Note that almost all platforms do have support for -# locals in the `thread` module, and there is no circular import problem +# potential problems). Note that all platforms on CPython do have support +# for locals in the `thread` module, and there is no circular import problem # then, so problems introduced by fiddling the order of imports here won't -# manifest on most boxes. +# manifest. -class _localbase(object): - __slots__ = '_local__key', '_local__args', '_local__lock' +class _localimpl: + """A class managing thread-local dicts""" + __slots__ = 'key', 'dicts', 'localargs', 'locallock', '__weakref__' + + def __init__(self): + # The key used in the Thread objects' attribute dicts. + # We keep it a string for speed but make it unlikely to clash with + # a "real" attribute. + self.key = '_threading_local._localimpl.' + str(id(self)) + # { id(Thread) -> (ref(Thread), thread-local dict) } + self.dicts = {} + + def get_dict(self): + """Return the dict for the current thread. Raises KeyError if none + defined.""" + thread = current_thread() + return self.dicts[id(thread)][1] + + def create_dict(self): + """Create a new dict for the current thread, and return it.""" + localdict = {} + key = self.key + thread = current_thread() + idt = id(thread) + def local_deleted(_, key=key): + # When the localimpl is deleted, remove the thread attribute. + thread = wrthread() + if thread is not None: + del thread.__dict__[key] + def thread_deleted(_, idt=idt): + # When the thread is deleted, remove the local dict. + # Note that this is suboptimal if the thread object gets + # caught in a reference loop. We would like to be called + # as soon as the OS-level thread ends instead. + local = wrlocal() + if local is not None: + dct = local.dicts.pop(idt) + wrlocal = ref(self, local_deleted) + wrthread = ref(thread, thread_deleted) + thread.__dict__[key] = wrlocal + self.dicts[idt] = wrthread, localdict + return localdict + + + at contextmanager +def _patch(self): + impl = object.__getattribute__(self, '_local__impl') + try: + dct = impl.get_dict() + except KeyError: + dct = impl.create_dict() + args, kw = impl.localargs + self.__init__(*args, **kw) + with impl.locallock: + object.__setattr__(self, '__dict__', dct) + yield - def __new__(cls, *args, **kw): - self = object.__new__(cls) - key = '_local__key', 'thread.local.' + str(id(self)) - object.__setattr__(self, '_local__key', key) - object.__setattr__(self, '_local__args', (args, kw)) - object.__setattr__(self, '_local__lock', RLock()) +class local: + __slots__ = '_local__impl', '__dict__' + + def __new__(cls, *args, **kw): if (args or kw) and (cls.__init__ is object.__init__): raise TypeError("Initialization arguments are not supported") - + self = object.__new__(cls) + impl = _localimpl() + impl.localargs = (args, kw) + impl.locallock = RLock() + object.__setattr__(self, '_local__impl', impl) # We need to create the thread dict in anticipation of # __init__ being called, to make sure we don't call it # again ourselves. - dict = object.__getattribute__(self, '__dict__') - current_thread().__dict__[key] = dict - + impl.create_dict() return self -def _patch(self): - key = object.__getattribute__(self, '_local__key') - d = current_thread().__dict__.get(key) - if d is None: - d = {} - current_thread().__dict__[key] = d - object.__setattr__(self, '__dict__', d) - - # we have a new instance dict, so call out __init__ if we have - # one - cls = type(self) - if cls.__init__ is not object.__init__: - args, kw = object.__getattribute__(self, '_local__args') - cls.__init__(self, *args, **kw) - else: - object.__setattr__(self, '__dict__', d) - -class local(_localbase): - def __getattribute__(self, name): - lock = object.__getattribute__(self, '_local__lock') - lock.acquire() - try: - _patch(self) + with _patch(self): return object.__getattribute__(self, name) - finally: - lock.release() def __setattr__(self, name, value): if name == '__dict__': raise AttributeError( "%r object attribute '__dict__' is read-only" % self.__class__.__name__) - lock = object.__getattribute__(self, '_local__lock') - lock.acquire() - try: - _patch(self) + with _patch(self): return object.__setattr__(self, name, value) - finally: - lock.release() def __delattr__(self, name): if name == '__dict__': raise AttributeError( "%r object attribute '__dict__' is read-only" % self.__class__.__name__) - lock = object.__getattribute__(self, '_local__lock') - lock.acquire() - try: - _patch(self) + with _patch(self): return object.__delattr__(self, name) - finally: - lock.release() - - def __del__(self): - import threading - - key = object.__getattribute__(self, '_local__key') - try: - # We use the non-locking API since we might already hold the lock - # (__del__ can be called at any point by the cyclic GC). - threads = threading._enumerate() - except: - # If enumerating the current threads fails, as it seems to do - # during shutdown, we'll skip cleanup under the assumption - # that there is nothing to clean up. - return - - for thread in threads: - try: - __dict__ = thread.__dict__ - except AttributeError: - # Thread is dying, rest in peace. - continue - - if key in __dict__: - try: - del __dict__[key] - except KeyError: - pass # didn't have anything in this thread from threading import current_thread, RLock Modified: python/branches/py3k/Lib/test/test_threading_local.py ============================================================================== --- python/branches/py3k/Lib/test/test_threading_local.py (original) +++ python/branches/py3k/Lib/test/test_threading_local.py Wed Sep 8 00:06:17 2010 @@ -184,11 +184,6 @@ """To test that subclasses behave properly.""" self._test_dict_attribute(LocalSubclass) - -class ThreadLocalTest(unittest.TestCase, BaseLocalTest): - _local = _thread._local - - # Fails for the pure Python implementation def test_cycle_collection(self): class X: pass @@ -201,6 +196,10 @@ gc.collect() self.assertIs(wr(), None) + +class ThreadLocalTest(unittest.TestCase, BaseLocalTest): + _local = _thread._local + class PyThreadingLocalTest(unittest.TestCase, BaseLocalTest): _local = _threading_local.local Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 8 00:06:17 2010 @@ -13,6 +13,11 @@ Library ------- +- Issue #9707: Rewritten reference implementation of threading.local which + is friendlier towards reference cycles. This change is not normally + visible since an optimized C implementation (_thread._local) is used + instead. + - Issue #6394: os.getppid() is now supported on Windows. Note that it will still return the id of the parent process after it has exited. This process id may even have been reused by another unrelated process. From python-checkins at python.org Wed Sep 8 00:11:52 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 00:11:52 +0200 (CEST) Subject: [Python-checkins] r84608 - python/branches/py3k/Lib/distutils/command/build_clib.py Message-ID: <20100907221152.A9BA1EEA5A@mail.python.org> Author: eric.araujo Date: Wed Sep 8 00:11:52 2010 New Revision: 84608 Log: Fix eon-old bug in build_clib options (#1718574) Modified: python/branches/py3k/Lib/distutils/command/build_clib.py Modified: python/branches/py3k/Lib/distutils/command/build_clib.py ============================================================================== --- python/branches/py3k/Lib/distutils/command/build_clib.py (original) +++ python/branches/py3k/Lib/distutils/command/build_clib.py Wed Sep 8 00:11:52 2010 @@ -32,9 +32,9 @@ description = "build C/C++ libraries used by Python extensions" user_options = [ - ('build-clib', 'b', + ('build-clib=', 'b', "directory to build C/C++ libraries to"), - ('build-temp', 't', + ('build-temp=', 't', "directory to put temporary build by-products"), ('debug', 'g', "compile with debugging information"), From python-checkins at python.org Wed Sep 8 00:17:04 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 00:17:04 +0200 (CEST) Subject: [Python-checkins] r84609 - in python/branches/release31-maint: Lib/distutils/command/build_clib.py Message-ID: <20100907221704.75535F9E4@mail.python.org> Author: eric.araujo Date: Wed Sep 8 00:17:04 2010 New Revision: 84609 Log: Merged revisions 84608 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84608 | eric.araujo | 2010-09-08 00:11:52 +0200 (mer., 08 sept. 2010) | 2 lines Fix eon-old bug in build_clib options (#1718574) ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/distutils/command/build_clib.py Modified: python/branches/release31-maint/Lib/distutils/command/build_clib.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/command/build_clib.py (original) +++ python/branches/release31-maint/Lib/distutils/command/build_clib.py Wed Sep 8 00:17:04 2010 @@ -32,9 +32,9 @@ description = "build C/C++ libraries used by Python extensions" user_options = [ - ('build-clib', 'b', + ('build-clib=', 'b', "directory to build C/C++ libraries to"), - ('build-temp', 't', + ('build-temp=', 't', "directory to put temporary build by-products"), ('debug', 'g', "compile with debugging information"), From ncoghlan at gmail.com Wed Sep 8 00:09:13 2010 From: ncoghlan at gmail.com (Nick Coghlan) Date: Wed, 8 Sep 2010 08:09:13 +1000 Subject: [Python-checkins] [Python-Dev] r84562 - in python/branches/py3k: Doc/library/io.rst Lib/_pyio.py Lib/test/test_memoryio.py Misc/NEWS Modules/_io/_iomodule.c Modules/_io/_iomodule.h Modules/_io/bytesio.c In-Reply-To: References: <20100906184822.26A92FA6F@mail.python.org> Message-ID: On Wed, Sep 8, 2010 at 3:39 AM, Benjamin Peterson wrote: > 2010/9/7 Nick Coghlan : >> I've raised an RFE (http://bugs.python.org/issue9789) to point out >> that the need for that GC collect call in there to make the test >> portable to other implementations is rather ugly > > Why? You're testing garbage collection, so you should call garbage collection. Having that test *as well* as a specific release test (to check implicit as well as explicit release) is a different story (we do that for generators, files and assorted other things). Having forced GC invocation be the only way to do explicit release is the part I found ugly. Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From python-checkins at python.org Wed Sep 8 00:18:34 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 00:18:34 +0200 (CEST) Subject: [Python-checkins] r84610 - in python/branches/release27-maint: Lib/distutils/command/build_clib.py Message-ID: <20100907221834.560C8EEA81@mail.python.org> Author: eric.araujo Date: Wed Sep 8 00:18:34 2010 New Revision: 84610 Log: Merged revisions 84608 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84608 | eric.araujo | 2010-09-08 00:11:52 +0200 (mer., 08 sept. 2010) | 2 lines Fix eon-old bug in build_clib options (#1718574) ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/distutils/command/build_clib.py Modified: python/branches/release27-maint/Lib/distutils/command/build_clib.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/command/build_clib.py (original) +++ python/branches/release27-maint/Lib/distutils/command/build_clib.py Wed Sep 8 00:18:34 2010 @@ -32,9 +32,9 @@ description = "build C/C++ libraries used by Python extensions" user_options = [ - ('build-clib', 'b', + ('build-clib=', 'b', "directory to build C/C++ libraries to"), - ('build-temp', 't', + ('build-temp=', 't', "directory to put temporary build by-products"), ('debug', 'g', "compile with debugging information"), From python-checkins at python.org Wed Sep 8 01:08:57 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 01:08:57 +0200 (CEST) Subject: [Python-checkins] r84611 - python/branches/py3k/Lib/distutils/command/upload.py Message-ID: <20100907230857.EA997EEA82@mail.python.org> Author: eric.araujo Date: Wed Sep 8 01:08:57 2010 New Revision: 84611 Log: Fix incorrect use of Command.announce (#9199) Modified: python/branches/py3k/Lib/distutils/command/upload.py Modified: python/branches/py3k/Lib/distutils/command/upload.py ============================================================================== --- python/branches/py3k/Lib/distutils/command/upload.py (original) +++ python/branches/py3k/Lib/distutils/command/upload.py Wed Sep 8 01:08:57 2010 @@ -194,4 +194,5 @@ self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - self.announce('-'*75, r.read(), '-'*75) + msg = ''.join('-' * 75, r.read(), '-' * 75) + self.announce(msg, log.INFO) From python-checkins at python.org Wed Sep 8 01:09:45 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 01:09:45 +0200 (CEST) Subject: [Python-checkins] r84612 - in python/branches/release31-maint: Lib/distutils/command/upload.py Message-ID: <20100907230945.04B81EEA0D@mail.python.org> Author: eric.araujo Date: Wed Sep 8 01:09:44 2010 New Revision: 84612 Log: Merged revisions 84611 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84611 | eric.araujo | 2010-09-08 01:08:57 +0200 (mer., 08 sept. 2010) | 2 lines Fix incorrect use of Command.announce (#9199) ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/distutils/command/upload.py Modified: python/branches/release31-maint/Lib/distutils/command/upload.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/command/upload.py (original) +++ python/branches/release31-maint/Lib/distutils/command/upload.py Wed Sep 8 01:09:44 2010 @@ -194,4 +194,5 @@ self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - self.announce('-'*75, r.read(), '-'*75) + msg = ''.join('-' * 75, r.read(), '-' * 75) + self.announce(msg, log.INFO) From python-checkins at python.org Wed Sep 8 01:12:59 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 01:12:59 +0200 (CEST) Subject: [Python-checkins] r84613 - in python/branches/release27-maint: Lib/distutils/command/upload.py Message-ID: <20100907231259.C8530EEAA2@mail.python.org> Author: eric.araujo Date: Wed Sep 8 01:12:59 2010 New Revision: 84613 Log: Merged revisions 84611 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84611 | eric.araujo | 2010-09-08 01:08:57 +0200 (mer., 08 sept. 2010) | 2 lines Fix incorrect use of Command.announce (#9199) ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/distutils/command/upload.py Modified: python/branches/release27-maint/Lib/distutils/command/upload.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/command/upload.py (original) +++ python/branches/release27-maint/Lib/distutils/command/upload.py Wed Sep 8 01:12:59 2010 @@ -186,4 +186,5 @@ self.announce('Upload failed (%s): %s' % (status, reason), log.ERROR) if self.show_response: - self.announce('-'*75, result.read(), '-'*75) + msg = ''.join('-' * 75, r.read(), '-' * 75) + self.announce(msg, log.INFO) From python-checkins at python.org Wed Sep 8 02:00:45 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 02:00:45 +0200 (CEST) Subject: [Python-checkins] r84614 - python/branches/py3k/Lib/distutils/command/upload.py Message-ID: <20100908000045.C3192EE9B8@mail.python.org> Author: eric.araujo Date: Wed Sep 8 02:00:45 2010 New Revision: 84614 Log: Follow-up to #9199: Fix str.join use, add newlines. Thanks to Konrad Delong for writing a test for upload_docs --show-response in distutils2, letting me catch my mistake. Modified: python/branches/py3k/Lib/distutils/command/upload.py Modified: python/branches/py3k/Lib/distutils/command/upload.py ============================================================================== --- python/branches/py3k/Lib/distutils/command/upload.py (original) +++ python/branches/py3k/Lib/distutils/command/upload.py Wed Sep 8 02:00:45 2010 @@ -194,5 +194,5 @@ self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - msg = ''.join('-' * 75, r.read(), '-' * 75) + msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) self.announce(msg, log.INFO) From python-checkins at python.org Wed Sep 8 02:02:01 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 02:02:01 +0200 (CEST) Subject: [Python-checkins] r84615 - in python/branches/release31-maint: Lib/distutils/command/upload.py Message-ID: <20100908000201.05B76E0E8@mail.python.org> Author: eric.araujo Date: Wed Sep 8 02:02:00 2010 New Revision: 84615 Log: Merged revisions 84614 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84614 | eric.araujo | 2010-09-08 02:00:45 +0200 (mer., 08 sept. 2010) | 5 lines Follow-up to #9199: Fix str.join use, add newlines. Thanks to Konrad Delong for writing a test for upload_docs --show-response in distutils2, letting me catch my mistake. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/distutils/command/upload.py Modified: python/branches/release31-maint/Lib/distutils/command/upload.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/command/upload.py (original) +++ python/branches/release31-maint/Lib/distutils/command/upload.py Wed Sep 8 02:02:00 2010 @@ -194,5 +194,5 @@ self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - msg = ''.join('-' * 75, r.read(), '-' * 75) + msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) self.announce(msg, log.INFO) From python-checkins at python.org Wed Sep 8 02:02:29 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 02:02:29 +0200 (CEST) Subject: [Python-checkins] r84616 - in python/branches/release27-maint: Lib/distutils/command/upload.py Message-ID: <20100908000229.67204E0E8@mail.python.org> Author: eric.araujo Date: Wed Sep 8 02:02:29 2010 New Revision: 84616 Log: Merged revisions 84614 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84614 | eric.araujo | 2010-09-08 02:00:45 +0200 (mer., 08 sept. 2010) | 5 lines Follow-up to #9199: Fix str.join use, add newlines. Thanks to Konrad Delong for writing a test for upload_docs --show-response in distutils2, letting me catch my mistake. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/distutils/command/upload.py Modified: python/branches/release27-maint/Lib/distutils/command/upload.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/command/upload.py (original) +++ python/branches/release27-maint/Lib/distutils/command/upload.py Wed Sep 8 02:02:29 2010 @@ -186,5 +186,5 @@ self.announce('Upload failed (%s): %s' % (status, reason), log.ERROR) if self.show_response: - msg = ''.join('-' * 75, r.read(), '-' * 75) + msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) self.announce(msg, log.INFO) From python-checkins at python.org Wed Sep 8 02:30:28 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 8 Sep 2010 02:30:28 +0200 (CEST) Subject: [Python-checkins] r84617 - python/branches/py3k/Lib/random.py Message-ID: <20100908003028.5F3CFEE98F@mail.python.org> Author: raymond.hettinger Date: Wed Sep 8 02:30:28 2010 New Revision: 84617 Log: In the case where only a user supplied random() method is available, adopt a strategy that makes the fewest calls to random(). Modified: python/branches/py3k/Lib/random.py Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Wed Sep 8 02:30:28 2010 @@ -212,33 +212,33 @@ return self.randrange(a, b+1) - def _randbelow(self, n, int=int, bpf=BPF, type=type, + def _randbelow(self, n, int=int, maxsize=1<= n: r = getrandbits(k) return r # There's an overriden random() method but no new getrandbits() method, # so we can only use random() from here. - if k > bpf: + random = self.random + if n >= maxsize: _warn("Underlying random() generator does not supply \n" "enough bits to choose from a population range this large.\n" "To remove the range limitation, add a getrandbits() method.") - return int(self.random() * n) - random = self.random - N = 1 << k - r = int(N * random()) # 0 <= r < 2**k - while r >= n: - r = int(N * random()) - return r + return int(random() * n) + rem = maxsize % n + limit = (maxsize - rem) / maxsize # int(limit * maxsize) % n == 0 + r = random() + while r >= limit: + r = random() + return int(r*maxsize) % n ## -------------------- sequence methods ------------------- From solipsis at pitrou.net Wed Sep 8 05:03:05 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 08 Sep 2010 05:03:05 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84617): sum=0 Message-ID: py3k results for svn r84617 (hg cset dc1f3b16d26a) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/refloguhmhVy', '-x'] From g.brandl at gmx.net Wed Sep 8 09:41:16 2010 From: g.brandl at gmx.net (Georg Brandl) Date: Wed, 08 Sep 2010 09:41:16 +0200 Subject: [Python-checkins] r84536 - sandbox/trunk/release/release.py In-Reply-To: <20100907175804.1eba70d7@mission> References: <20100905182846.4E3DAEEA02@mail.python.org> <20100907175804.1eba70d7@mission> Message-ID: Am 07.09.2010 23:58, schrieb Barry Warsaw: > On Sep 05, 2010, at 08:28 PM, georg.brandl wrote: > >>Author: georg.brandl >>Date: Sun Sep 5 20:28:46 2010 >>New Revision: 84536 >> >>Log: >>Fix after changing NEWS layout. >> >>Modified: >> sandbox/trunk/release/release.py >> >>Modified: sandbox/trunk/release/release.py >>============================================================================== >>--- sandbox/trunk/release/release.py (original) >>+++ sandbox/trunk/release/release.py Sun Sep 5 20:28:46 2010 >>@@ -396,13 +396,13 @@ >> with open('Misc/NEWS', encoding="utf-8") as fp: >> lines = fp.readlines() >> for i, line in enumerate(lines): >>- if line.startswith("(editors"): >>+ if line.startswith("Python News"): >> start = i >> if line.startswith("What's"): >> end = i >> break >> with open('Misc/NEWS', 'w', encoding="utf-8") as fp: >>- fp.writelines(lines[:start+1]) >>+ fp.writelines(lines[:start+2]) >> fp.write(NEWS_TEMPLATE) >> fp.writelines(lines[end-1:]) >> print("Please fill in the the name of the next version.") > > Will this still work with the Python 2.7 NEWS file? I intended to backport my removal of NEWS.help, so yes. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out. From python-checkins at python.org Wed Sep 8 12:43:45 2010 From: python-checkins at python.org (georg.brandl) Date: Wed, 8 Sep 2010 12:43:45 +0200 (CEST) Subject: [Python-checkins] r84619 - python/branches/py3k/Misc/developers.txt Message-ID: <20100908104345.BA23DEE981@mail.python.org> Author: georg.brandl Date: Wed Sep 8 12:43:45 2010 New Revision: 84619 Log: Add Lukasz. Modified: python/branches/py3k/Misc/developers.txt Modified: python/branches/py3k/Misc/developers.txt ============================================================================== --- python/branches/py3k/Misc/developers.txt (original) +++ python/branches/py3k/Misc/developers.txt Wed Sep 8 12:43:45 2010 @@ -20,6 +20,9 @@ Permissions History ------------------- +- Lukasz Langa was given commit access on Sep 08 2010 by GFB, + at suggestion of Antoine Pitrou, for general bug fixing. + - Daniel Stutzbach was given commit access on Aug 22 2010 by MvL, for general bug fixing. From python-checkins at python.org Wed Sep 8 12:46:15 2010 From: python-checkins at python.org (vinay.sajip) Date: Wed, 8 Sep 2010 12:46:15 +0200 (CEST) Subject: [Python-checkins] r84620 - in python/branches/py3k: Doc/library/logging.rst Lib/logging/handlers.py Misc/NEWS Message-ID: <20100908104615.8CA63EEAED@mail.python.org> Author: vinay.sajip Date: Wed Sep 8 12:46:15 2010 New Revision: 84620 Log: logging: Added QueueHandler. Modified: python/branches/py3k/Doc/library/logging.rst python/branches/py3k/Lib/logging/handlers.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/logging.rst ============================================================================== --- python/branches/py3k/Doc/library/logging.rst (original) +++ python/branches/py3k/Doc/library/logging.rst Wed Sep 8 12:46:15 2010 @@ -594,10 +594,10 @@ In addition to the base :class:`Handler` class, many useful subclasses are provided: -#. :class:`StreamHandler` instances send error messages to streams (file-like +#. :class:`StreamHandler` instances send messages to streams (file-like objects). -#. :class:`FileHandler` instances send error messages to disk files. +#. :class:`FileHandler` instances send messages to disk files. .. module:: logging.handlers @@ -606,31 +606,31 @@ directly. Instead, use :class:`RotatingFileHandler` or :class:`TimedRotatingFileHandler`. -#. :class:`RotatingFileHandler` instances send error messages to disk +#. :class:`RotatingFileHandler` instances send messages to disk files, with support for maximum log file sizes and log file rotation. -#. :class:`TimedRotatingFileHandler` instances send error messages to +#. :class:`TimedRotatingFileHandler` instances send messages to disk files, rotating the log file at certain timed intervals. -#. :class:`SocketHandler` instances send error messages to TCP/IP +#. :class:`SocketHandler` instances send messages to TCP/IP sockets. -#. :class:`DatagramHandler` instances send error messages to UDP +#. :class:`DatagramHandler` instances send messages to UDP sockets. -#. :class:`SMTPHandler` instances send error messages to a designated +#. :class:`SMTPHandler` instances send messages to a designated email address. -#. :class:`SysLogHandler` instances send error messages to a Unix +#. :class:`SysLogHandler` instances send messages to a Unix syslog daemon, possibly on a remote machine. -#. :class:`NTEventLogHandler` instances send error messages to a +#. :class:`NTEventLogHandler` instances send messages to a Windows NT/2000/XP event log. -#. :class:`MemoryHandler` instances send error messages to a buffer +#. :class:`MemoryHandler` instances send messages to a buffer in memory, which is flushed whenever specific criteria are met. -#. :class:`HTTPHandler` instances send error messages to an HTTP +#. :class:`HTTPHandler` instances send messages to an HTTP server using either ``GET`` or ``POST`` semantics. #. :class:`WatchedFileHandler` instances watch the file they are @@ -638,6 +638,9 @@ name. This handler is only useful on Unix-like systems; Windows does not support the underlying mechanism used. +#. :class:`QueueHandler` instances send messages to a queue, such as + those implemented in the :mod:`queue` or :mod:`multiprocessing` modules. + .. currentmodule:: logging #. :class:`NullHandler` instances do nothing with error messages. They are used @@ -650,6 +653,10 @@ The :class:`NullHandler` class was not present in previous versions. +.. versionadded:: 3.2 + +The :class:`QueueHandler` class was not present in previous versions. + The :class:`NullHandler`, :class:`StreamHandler` and :class:`FileHandler` classes are defined in the core logging package. The other handlers are defined in a sub- module, :mod:`logging.handlers`. (There is also another @@ -1506,16 +1513,16 @@ threads in a single process *is* supported, logging to a single file from *multiple processes* is *not* supported, because there is no standard way to serialize access to a single file across multiple processes in Python. If you -need to log to a single file from multiple processes, the best way of doing -this is to have all the processes log to a :class:`SocketHandler`, and have a -separate process which implements a socket server which reads from the socket -and logs to file. (If you prefer, you can dedicate one thread in one of the -existing processes to perform this function.) The following section documents -this approach in more detail and includes a working socket receiver which can -be used as a starting point for you to adapt in your own applications. +need to log to a single file from multiple processes, one way of doing this is +to have all the processes log to a :class:`SocketHandler`, and have a separate +process which implements a socket server which reads from the socket and logs +to file. (If you prefer, you can dedicate one thread in one of the existing +processes to perform this function.) The following section documents this +approach in more detail and includes a working socket receiver which can be +used as a starting point for you to adapt in your own applications. If you are using a recent version of Python which includes the -:mod:`multiprocessing` module, you can write your own handler which uses the +:mod:`multiprocessing` module, you could write your own handler which uses the :class:`Lock` class from this module to serialize access to the file from your processes. The existing :class:`FileHandler` and subclasses do not make use of :mod:`multiprocessing` at present, though they may do so in the future. @@ -1523,6 +1530,128 @@ working lock functionality on all platforms (see http://bugs.python.org/issue3770). +.. currentmodule:: logging.handlers + +Alternatively, you can use a ``Queue`` and a :class:`QueueHandler` to send +all logging events to one of the processes in your multi-process application. +The following example script demonstrates how you can do this; in the example +a separate listener process listens for events sent by other processes and logs +them according to its own logging configuration. Although the example only +demonstrates one way of doing it (for example, you may want to use a listener +thread rather than a separate listener process - the implementation would be +analogous) it does allow for completely different logging configurations for +the listener and the other processes in your application, and can be used as +the basis for code meeting your own specific requirements:: + + # You'll need these imports in your own code + import logging + import logging.handlers + import multiprocessing + + # Next two import lines for this demo only + from random import choice, random + import time + + # + # Because you'll want to define the logging configurations for listener and workers, the + # listener and worker process functions take a configurer parameter which is a callable + # for configuring logging for that process. These functions are also passed the queue, + # which they use for communication. + # + # In practice, you can configure the listener however you want, but note that in this + # simple example, the listener does not apply level or filter logic to received records. + # In practice, you would probably want to do ths logic in the worker processes, to avoid + # sending events which would be filtered out between processes. + # + # The size of the rotated files is made small so you can see the results easily. + def listener_configurer(): + root = logging.getLogger() + h = logging.handlers.RotatingFileHandler('/tmp/mptest.log', 'a', 300, 10) + f = logging.Formatter('%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s') + h.setFormatter(f) + root.addHandler(h) + + # This is the listener process top-level loop: wait for logging events + # (LogRecords)on the queue and handle them, quit when you get a None for a + # LogRecord. + def listener_process(queue, configurer): + configurer() + while True: + try: + record = queue.get() + if record is None: # We send this as a sentinel to tell the listener to quit. + break + logger = logging.getLogger(record.name) + logger.handle(record) # No level or filter logic applied - just do it! + except (KeyboardInterrupt, SystemExit): + raise + except: + import sys, traceback + print >> sys.stderr, 'Whoops! Problem:' + traceback.print_exc(file=sys.stderr) + + # Arrays used for random selections in this demo + + LEVELS = [logging.DEBUG, logging.INFO, logging.WARNING, + logging.ERROR, logging.CRITICAL] + + LOGGERS = ['a.b.c', 'd.e.f'] + + MESSAGES = [ + 'Random message #1', + 'Random message #2', + 'Random message #3', + ] + + # The worker configuration is done at the start of the worker process run. + # Note that on Windows you can't rely on fork semantics, so each process + # will run the logging configuration code when it starts. + def worker_configurer(queue): + h = logging.handlers.QueueHandler(queue) # Just the one handler needed + root = logging.getLogger() + root.addHandler(h) + root.setLevel(logging.DEBUG) # send all messages, for demo; no other level or filter logic applied. + + # This is the worker process top-level loop, which just logs ten events with + # random intervening delays before terminating. + # The print messages are just so you know it's doing something! + def worker_process(queue, configurer): + configurer(queue) + name = multiprocessing.current_process().name + print('Worker started: %s' % name) + for i in range(10): + time.sleep(random()) + logger = logging.getLogger(choice(LOGGERS)) + level = choice(LEVELS) + message = choice(MESSAGES) + logger.log(level, message) + print('Worker finished: %s' % name) + + # Here's where the demo gets orchestrated. Create the queue, create and start + # the listener, create ten workers and start them, wait for them to finish, + # then send a None to the queue to tell the listener to finish. + def main(): + queue = multiprocessing.Queue(-1) + listener = multiprocessing.Process(target=listener_process, + args=(queue, listener_configurer)) + listener.start() + workers = [] + for i in range(10): + worker = multiprocessing.Process(target=worker_process, + args=(queue, worker_configurer)) + workers.append(worker) + worker.start() + for w in workers: + w.join() + queue.put_nowait(None) + listener.join() + + if __name__ == '__main__': + main() + + +.. currentmodule:: logging + .. _network-logging: @@ -2458,6 +2587,39 @@ Sends the record to the Web server as a percent-encoded dictionary. +.. _queue-handler: + + +QueueHandler +^^^^^^^^^^^^ + +The :class:`QueueHandler` class, located in the :mod:`logging.handlers` module, +supports sending logging messages to a queue, such as those implemented in the +:mod:`queue` or :mod:`multiprocessing` modules. + + +.. class:: QueueHandler(queue) + + Returns a new instance of the :class:`QueueHandler` class. The instance is + initialized with the queue to send messages to. + + + .. method:: emit(record) + + Sends the record to the handler's queue. + + .. method:: enqueue(record) + + Enqueues the record on the queue using ``put_nowait()``; you may + want to override this if you want to use blocking behaviour, or a + timeout, or a customised queue implementation. + + +.. versionadded:: 3.2 + +The :class:`QueueHandler` class was not present in previous versions. + + .. _formatter-objects: Formatter Objects @@ -2528,6 +2690,8 @@ +-------------------------+-----------------------------------------------+ | ``%(process)d`` | Process ID (if available). | +-------------------------+-----------------------------------------------+ +| ``%(processName)s`` | Process name (if available). | ++-------------------------+-----------------------------------------------+ | ``%(message)s`` | The logged message, computed as ``msg % | | | args``. | +-------------------------+-----------------------------------------------+ Modified: python/branches/py3k/Lib/logging/handlers.py ============================================================================== --- python/branches/py3k/Lib/logging/handlers.py (original) +++ python/branches/py3k/Lib/logging/handlers.py Wed Sep 8 12:46:15 2010 @@ -1134,3 +1134,55 @@ self.flush() self.target = None BufferingHandler.close(self) + + +class QueueHandler(logging.Handler): + """ + This handler sends events to a queue. Typically, it would be used together + with a multiprocessing Queue to centralise logging to file in one process + (in a multi-process application), so as to avoid file write contention + between processes. + + This code is new in Python 3.2, but this class can be copy pasted into + user code for use with earlier Python versions. + """ + + def __init__(self, queue): + """ + Initialise an instance, using the passed queue. + """ + logging.Handler.__init__(self) + self.queue = queue + + def enqueue(self, record): + """ + Enqueue a record. + + The base implementation uses put_nowait. You may want to override + this method if you want to use blocking, timeouts or custom queue + implementations. + """ + self.queue.put_nowait(record) + + def emit(self, record): + """ + Emit a record. + + Writes the LogRecord to the queue, preparing it for pickling first. + """ + try: + # The format operation gets traceback text into record.exc_text + # (if there's exception data), and also puts the message into + # record.message. We can then use this to replace the original + # msg + args, as these might be unpickleable. We also zap the + # exc_info attribute, as it's no longer needed and, if not None, + # will typically not be pickleable. + self.format(record) + record.msg = record.message + record.args = None + record.exc_info = None + self.enqueue(record) + except (KeyboardInterrupt, SystemExit): + raise + except: + self.handleError(record) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 8 12:46:15 2010 @@ -13,6 +13,9 @@ Library ------- +- Logging: Added QueueHandler class to facilitate logging usage with + multiprocessing. + - Issue #9707: Rewritten reference implementation of threading.local which is friendlier towards reference cycles. This change is not normally visible since an optimized C implementation (_thread._local) is used From python-checkins at python.org Wed Sep 8 12:51:01 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 8 Sep 2010 12:51:01 +0200 (CEST) Subject: [Python-checkins] r84621 - in python/branches/release27-maint: Lib/test/test_file2k.py Misc/NEWS Objects/fileobject.c Message-ID: <20100908105101.755BFEEB05@mail.python.org> Author: victor.stinner Date: Wed Sep 8 12:51:01 2010 New Revision: 84621 Log: Issue #4947: The write() method of sys.stdout and sys.stderr uses their encoding and errors attributes instead of using utf-8 in strict mode, to get the same behaviour than the print statement. Modified: python/branches/release27-maint/Lib/test/test_file2k.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Objects/fileobject.c Modified: python/branches/release27-maint/Lib/test/test_file2k.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_file2k.py (original) +++ python/branches/release27-maint/Lib/test/test_file2k.py Wed Sep 8 12:51:01 2010 @@ -619,6 +619,39 @@ finally: sys.stdout = save_stdout + def test_unicode(self): + import subprocess + + def get_message(encoding, *code): + code = '\n'.join(code) + env = os.environ.copy() + env['PYTHONIOENCODING'] = encoding + process = subprocess.Popen([sys.executable, "-c", code], + stdout=subprocess.PIPE, env=env) + stdout, stderr = process.communicate() + self.assertEqual(process.returncode, 0) + return stdout + + def check_message(text, encoding, expected): + stdout = get_message(encoding, + "import sys", + "sys.stdout.write(%r)" % text, + "sys.stdout.flush()") + self.assertEqual(stdout, expected) + + check_message(u'\u20ac\n', "iso-8859-15", "\xa4\n") + check_message(u'\u20ac\n', "utf-16-le", '\xac\x20\n\x00') + check_message(u'15\u20ac\n', "iso-8859-1:ignore", "15\n") + check_message(u'15\u20ac\n', "iso-8859-1:replace", "15?\n") + check_message(u'15\u20ac\n', "iso-8859-1:backslashreplace", + "15\\u20ac\n") + + for objtype in ('buffer', 'bytearray'): + stdout = get_message('ascii', + 'import sys', + r'sys.stdout.write(%s("\xe9\n"))' % objtype) + self.assertEqual(stdout, "\xe9\n") + def test_main(): # Historically, these tests have been sloppy about removing TESTFN. Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Wed Sep 8 12:51:01 2010 @@ -12,6 +12,10 @@ Core and Builtins ----------------- +- Issue #4947: The write() method of sys.stdout and sys.stderr uses their + encoding and errors attributes instead of using utf-8 in strict mode, to get + the same behaviour than the print statement. + - Issue #9737: Fix a crash when trying to delete a slice or an item from a memoryview object. @@ -63,7 +67,7 @@ - Issue #8750: Fixed MutableSet's methods to correctly handle reflexive operations, namely x -= x and x ^= x. -- Issue #9129: smtpd.py is vulnerable to DoS attacks deriving from missing +- Issue #9129: smtpd.py is vulnerable to DoS attacks deriving from missing error handling when accepting a new connection. - Issue #658749: asyncore's connect() method now correctly interprets winsock Modified: python/branches/release27-maint/Objects/fileobject.c ============================================================================== --- python/branches/release27-maint/Objects/fileobject.c (original) +++ python/branches/release27-maint/Objects/fileobject.c Wed Sep 8 12:51:01 2010 @@ -1735,8 +1735,10 @@ file_write(PyFileObject *f, PyObject *args) { Py_buffer pbuf; - char *s; + const char *s; Py_ssize_t n, n2; + PyObject *encoded = NULL; + if (f->f_fp == NULL) return err_closed(); if (!f->writable) @@ -1746,14 +1748,41 @@ return NULL; s = pbuf.buf; n = pbuf.len; - } else - if (!PyArg_ParseTuple(args, "t#", &s, &n)) - return NULL; + } + else { + const char *encoding, *errors; + PyObject *text; + if (!PyArg_ParseTuple(args, "O", &text)) + return NULL; + + if (PyString_Check(text)) { + s = PyString_AS_STRING(text); + n = PyString_GET_SIZE(text); + } else if (PyUnicode_Check(text)) { + if (f->f_encoding != Py_None) + encoding = PyString_AS_STRING(f->f_encoding); + else + encoding = PyUnicode_GetDefaultEncoding(); + if (f->f_errors != Py_None) + errors = PyString_AS_STRING(f->f_errors); + else + errors = "strict"; + encoded = PyUnicode_AsEncodedString(text, encoding, errors); + if (encoded == NULL) + return NULL; + s = PyString_AS_STRING(encoded); + n = PyString_GET_SIZE(encoded); + } else { + if (PyObject_AsCharBuffer(text, &s, &n)) + return NULL; + } + } f->f_softspace = 0; FILE_BEGIN_ALLOW_THREADS(f) errno = 0; n2 = fwrite(s, 1, n, f->f_fp); FILE_END_ALLOW_THREADS(f) + Py_XDECREF(encoded); if (f->f_binary) PyBuffer_Release(&pbuf); if (n2 != n) { From python-checkins at python.org Wed Sep 8 13:45:16 2010 From: python-checkins at python.org (victor.stinner) Date: Wed, 8 Sep 2010 13:45:16 +0200 (CEST) Subject: [Python-checkins] r84622 - python/branches/release27-maint/Lib/test/test_file2k.py Message-ID: <20100908114516.6AB9BEE99C@mail.python.org> Author: victor.stinner Date: Wed Sep 8 13:45:16 2010 New Revision: 84622 Log: StdoutTests.test_unicode(): avoid newlines to fix the test on windows * Add also a test for utf-8 * Add some comments * Flush stdout for the buffer API tests Modified: python/branches/release27-maint/Lib/test/test_file2k.py Modified: python/branches/release27-maint/Lib/test/test_file2k.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_file2k.py (original) +++ python/branches/release27-maint/Lib/test/test_file2k.py Wed Sep 8 13:45:16 2010 @@ -639,18 +639,23 @@ "sys.stdout.flush()") self.assertEqual(stdout, expected) - check_message(u'\u20ac\n', "iso-8859-15", "\xa4\n") - check_message(u'\u20ac\n', "utf-16-le", '\xac\x20\n\x00') - check_message(u'15\u20ac\n', "iso-8859-1:ignore", "15\n") - check_message(u'15\u20ac\n', "iso-8859-1:replace", "15?\n") - check_message(u'15\u20ac\n', "iso-8859-1:backslashreplace", - "15\\u20ac\n") + # test the encoding + check_message(u'15\u20ac', "iso-8859-15", "15\xa4") + check_message(u'15\u20ac', "utf-8", '15\xe2\x82\xac') + check_message(u'15\u20ac', "utf-16-le", '1\x005\x00\xac\x20') + # test the error handler + check_message(u'15\u20ac', "iso-8859-1:ignore", "15") + check_message(u'15\u20ac', "iso-8859-1:replace", "15?") + check_message(u'15\u20ac', "iso-8859-1:backslashreplace", "15\\u20ac") + + # test the buffer API for objtype in ('buffer', 'bytearray'): stdout = get_message('ascii', 'import sys', - r'sys.stdout.write(%s("\xe9\n"))' % objtype) - self.assertEqual(stdout, "\xe9\n") + r'sys.stdout.write(%s("\xe9"))' % objtype, + 'sys.stdout.flush()') + self.assertEqual(stdout, "\xe9") def test_main(): From python-checkins at python.org Wed Sep 8 14:37:10 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 8 Sep 2010 14:37:10 +0200 (CEST) Subject: [Python-checkins] r84623 - in python/branches/py3k: Misc/NEWS Python/pystate.c Message-ID: <20100908123710.C2BE5F275@mail.python.org> Author: antoine.pitrou Date: Wed Sep 8 14:37:10 2010 New Revision: 84623 Log: Issue #9797: pystate.c wrongly assumed that zero couldn't be a valid thread-local storage key. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Python/pystate.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 8 14:37:10 2010 @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #9797: pystate.c wrongly assumed that zero couldn't be a valid + thread-local storage key. + Library ------- Modified: python/branches/py3k/Python/pystate.c ============================================================================== --- python/branches/py3k/Python/pystate.c (original) +++ python/branches/py3k/Python/pystate.c Wed Sep 8 14:37:10 2010 @@ -340,7 +340,7 @@ Py_FatalError("PyThreadState_Delete: tstate is still current"); tstate_delete_common(tstate); #ifdef WITH_THREAD - if (autoTLSkey && PyThread_get_key_value(autoTLSkey) == tstate) + if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); #endif /* WITH_THREAD */ } @@ -357,7 +357,7 @@ "PyThreadState_DeleteCurrent: no current tstate"); _Py_atomic_store_relaxed(&_PyThreadState_Current, NULL); tstate_delete_common(tstate); - if (autoTLSkey && PyThread_get_key_value(autoTLSkey) == tstate) + if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); PyEval_ReleaseLock(); } @@ -580,7 +580,6 @@ _PyGILState_Fini(void) { PyThread_delete_key(autoTLSkey); - autoTLSkey = 0; autoInterpreterState = NULL; } @@ -592,10 +591,10 @@ static void _PyGILState_NoteThreadState(PyThreadState* tstate) { - /* If autoTLSkey is 0, this must be the very first threadstate created - in Py_Initialize(). Don't do anything for now (we'll be back here - when _PyGILState_Init is called). */ - if (!autoTLSkey) + /* If autoTLSkey isn't initialized, this must be the very first + threadstate created in Py_Initialize(). Don't do anything for now + (we'll be back here when _PyGILState_Init is called). */ + if (!autoInterpreterState) return; /* Stick the thread state for this thread in thread local storage. @@ -623,7 +622,7 @@ PyThreadState * PyGILState_GetThisThreadState(void) { - if (autoInterpreterState == NULL || autoTLSkey == 0) + if (autoInterpreterState == NULL) return NULL; return (PyThreadState *)PyThread_get_key_value(autoTLSkey); } From python-checkins at python.org Wed Sep 8 14:39:01 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 14:39:01 +0200 (CEST) Subject: [Python-checkins] r84624 - python/branches/release31-maint/Misc/maintainers.rst Message-ID: <20100908123901.11140F275@mail.python.org> Author: eric.araujo Date: Wed Sep 8 14:39:00 2010 New Revision: 84624 Log: Synchronize maintainers.rst Modified: python/branches/release31-maint/Misc/maintainers.rst Modified: python/branches/release31-maint/Misc/maintainers.rst ============================================================================== --- python/branches/release31-maint/Misc/maintainers.rst (original) +++ python/branches/release31-maint/Misc/maintainers.rst Wed Sep 8 14:39:00 2010 @@ -52,8 +52,8 @@ argparse bethard array ast -asynchat josiahcarlson, giampaolo.rodola -asyncore josiahcarlson, giampaolo.rodola +asynchat josiahcarlson, giampaolo.rodola, stutzbach +asyncore josiahcarlson, giampaolo.rodola, stutzbach atexit audioop base64 @@ -72,7 +72,7 @@ code codecs lemburg, doerwalter codeop -collections rhettinger +collections rhettinger, stutzbach colorsys compileall configparser @@ -83,7 +83,7 @@ crypt csv ctypes theller -curses andrew.kuchling +curses akuchling datetime alexander.belopolsky dbm decimal facundobatista, rhettinger, mark.dickinson @@ -113,7 +113,7 @@ grp gzip hashlib -heapq rhettinger +heapq rhettinger, stutzbach hmac html http @@ -123,19 +123,19 @@ imp importlib brett.cannon inspect -io pitrou, benjamin.peterson +io pitrou, benjamin.peterson, stutzbach itertools rhettinger json bob.ippolito (inactive) keyword lib2to3 benjamin.peterson linecache locale loewis, lemburg -logging vsajip +logging vinay.sajip macpath -mailbox andrew.kuchling +mailbox akuchling mailcap marshal -math mark.dickinson, rhettinger +math mark.dickinson, rhettinger, stutzbach mimetypes mmap modulefinder theller, jvr @@ -231,7 +231,7 @@ wave weakref fdrake, pitrou webbrowser georg.brandl -winreg brian.curtin* +winreg brian.curtin*, stutzbach winsound effbot (inactive) wsgiref pje xdrlib @@ -262,7 +262,7 @@ Platform Maintainers ------------------ ----------- AIX -Cygwin jlt63 +Cygwin jlt63, stutzbach FreeBSD HP-UX Linux @@ -290,9 +290,9 @@ GUI i18n lemburg import machinery brett.cannon, ncoghlan -io pitrou, benjamin.peterson +io pitrou, benjamin.peterson, stutzbach locale lemburg, loewis -mathematics mark.dickinson, eric.smith, lemburg +mathematics mark.dickinson, eric.smith, lemburg, stutzbach memory management tim_one, lemburg networking giampaolo.rodola packaging tarek, lemburg From python-checkins at python.org Wed Sep 8 14:40:15 2010 From: python-checkins at python.org (eric.araujo) Date: Wed, 8 Sep 2010 14:40:15 +0200 (CEST) Subject: [Python-checkins] r84625 - python/branches/release27-maint/Misc/maintainers.rst Message-ID: <20100908124015.E4AEFEEB17@mail.python.org> Author: eric.araujo Date: Wed Sep 8 14:40:15 2010 New Revision: 84625 Log: Synchronize maintainers.rst Modified: python/branches/release27-maint/Misc/maintainers.rst Modified: python/branches/release27-maint/Misc/maintainers.rst ============================================================================== --- python/branches/release27-maint/Misc/maintainers.rst (original) +++ python/branches/release27-maint/Misc/maintainers.rst Wed Sep 8 14:40:15 2010 @@ -86,7 +86,7 @@ crypt csv ctypes theller -curses andrew.kuchling +curses akuchling datetime alexander.belopolsky dbm decimal facundobatista, rhettinger, mark.dickinson @@ -137,7 +137,7 @@ locale loewis, lemburg logging vinay.sajip macpath -mailbox andrew.kuchling +mailbox akuchling mailcap marshal math mark.dickinson, rhettinger, stutzbach From python-checkins at python.org Wed Sep 8 14:40:45 2010 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 8 Sep 2010 14:40:45 +0200 (CEST) Subject: [Python-checkins] r84626 - python/branches/release27-maint/Lib/string.py Message-ID: <20100908124045.E11B9EEA96@mail.python.org> Author: senthil.kumaran Date: Wed Sep 8 14:40:45 2010 New Revision: 84626 Log: Issue5416 - Revert a documentatin change made to explain replace on negative value. Minor changes to doc: s/maxsplit/maxreplace Modified: python/branches/release27-maint/Lib/string.py Modified: python/branches/release27-maint/Lib/string.py ============================================================================== --- python/branches/release27-maint/Lib/string.py (original) +++ python/branches/release27-maint/Lib/string.py Wed Sep 8 14:40:45 2010 @@ -508,16 +508,15 @@ return s.capitalize() # Substring replacement (global) -def replace(s, old, new, maxsplit=-1): - """replace (str, old, new[, maxsplit]) -> string +def replace(s, old, new, maxreplace=-1): + """replace (str, old, new[, maxreplace]) -> string Return a copy of string str with all occurrences of substring - old replaced by new. If the optional argument maxsplit is - given, only the first maxsplit occurrences are replaced. A - negative value of maxsplit signifies all occurances. + old replaced by new. If the optional argument maxreplace is + given, only the first maxreplace occurrences are replaced. """ - return s.replace(old, new, maxsplit) + return s.replace(old, new, maxreplace) # Try importing optional built-in module "strop" -- if it exists, From python-checkins at python.org Wed Sep 8 14:40:49 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 8 Sep 2010 14:40:49 +0200 (CEST) Subject: [Python-checkins] r84627 - in python/branches/release27-maint: Misc/NEWS Python/pystate.c Message-ID: <20100908124049.9D9D4EEB04@mail.python.org> Author: antoine.pitrou Date: Wed Sep 8 14:40:49 2010 New Revision: 84627 Log: Merged revisions 84623 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84623 | antoine.pitrou | 2010-09-08 14:37:10 +0200 (mer., 08 sept. 2010) | 4 lines Issue #9797: pystate.c wrongly assumed that zero couldn't be a valid thread-local storage key. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Python/pystate.c Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Wed Sep 8 14:40:49 2010 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #9797: pystate.c wrongly assumed that zero couldn't be a valid + thread-local storage key. + - Issue #4947: The write() method of sys.stdout and sys.stderr uses their encoding and errors attributes instead of using utf-8 in strict mode, to get the same behaviour than the print statement. Modified: python/branches/release27-maint/Python/pystate.c ============================================================================== --- python/branches/release27-maint/Python/pystate.c (original) +++ python/branches/release27-maint/Python/pystate.c Wed Sep 8 14:40:49 2010 @@ -298,7 +298,7 @@ Py_FatalError("PyThreadState_Delete: tstate is still current"); tstate_delete_common(tstate); #ifdef WITH_THREAD - if (autoTLSkey && PyThread_get_key_value(autoTLSkey) == tstate) + if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); #endif /* WITH_THREAD */ } @@ -314,7 +314,7 @@ "PyThreadState_DeleteCurrent: no current tstate"); _PyThreadState_Current = NULL; tstate_delete_common(tstate); - if (autoTLSkey && PyThread_get_key_value(autoTLSkey) == tstate) + if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); PyEval_ReleaseLock(); } @@ -534,7 +534,6 @@ _PyGILState_Fini(void) { PyThread_delete_key(autoTLSkey); - autoTLSkey = 0; autoInterpreterState = NULL; } @@ -546,10 +545,10 @@ static void _PyGILState_NoteThreadState(PyThreadState* tstate) { - /* If autoTLSkey is 0, this must be the very first threadstate created - in Py_Initialize(). Don't do anything for now (we'll be back here - when _PyGILState_Init is called). */ - if (!autoTLSkey) + /* If autoTLSkey isn't initialized, this must be the very first + threadstate created in Py_Initialize(). Don't do anything for now + (we'll be back here when _PyGILState_Init is called). */ + if (!autoInterpreterState) return; /* Stick the thread state for this thread in thread local storage. @@ -577,7 +576,7 @@ PyThreadState * PyGILState_GetThisThreadState(void) { - if (autoInterpreterState == NULL || autoTLSkey == 0) + if (autoInterpreterState == NULL) return NULL; return (PyThreadState *)PyThread_get_key_value(autoTLSkey); } From python-checkins at python.org Wed Sep 8 14:48:12 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 8 Sep 2010 14:48:12 +0200 (CEST) Subject: [Python-checkins] r84628 - in python/branches/release31-maint: Misc/NEWS Python/pystate.c Message-ID: <20100908124812.CADDDEE985@mail.python.org> Author: antoine.pitrou Date: Wed Sep 8 14:48:12 2010 New Revision: 84628 Log: Merged revisions 84623 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84623 | antoine.pitrou | 2010-09-08 14:37:10 +0200 (mer., 08 sept. 2010) | 4 lines Issue #9797: pystate.c wrongly assumed that zero couldn't be a valid thread-local storage key. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Python/pystate.c Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Wed Sep 8 14:48:12 2010 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #9797: pystate.c wrongly assumed that zero couldn't be a valid + thread-local storage key. + - Issue #9737: Fix a crash when trying to delete a slice or an item from a memoryview object. Modified: python/branches/release31-maint/Python/pystate.c ============================================================================== --- python/branches/release31-maint/Python/pystate.c (original) +++ python/branches/release31-maint/Python/pystate.c Wed Sep 8 14:48:12 2010 @@ -338,7 +338,7 @@ Py_FatalError("PyThreadState_Delete: tstate is still current"); tstate_delete_common(tstate); #ifdef WITH_THREAD - if (autoTLSkey && PyThread_get_key_value(autoTLSkey) == tstate) + if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); #endif /* WITH_THREAD */ } @@ -354,7 +354,7 @@ "PyThreadState_DeleteCurrent: no current tstate"); _PyThreadState_Current = NULL; tstate_delete_common(tstate); - if (autoTLSkey && PyThread_get_key_value(autoTLSkey) == tstate) + if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey); PyEval_ReleaseLock(); } @@ -574,7 +574,6 @@ _PyGILState_Fini(void) { PyThread_delete_key(autoTLSkey); - autoTLSkey = 0; autoInterpreterState = NULL; } @@ -586,10 +585,10 @@ static void _PyGILState_NoteThreadState(PyThreadState* tstate) { - /* If autoTLSkey is 0, this must be the very first threadstate created - in Py_Initialize(). Don't do anything for now (we'll be back here - when _PyGILState_Init is called). */ - if (!autoTLSkey) + /* If autoTLSkey isn't initialized, this must be the very first + threadstate created in Py_Initialize(). Don't do anything for now + (we'll be back here when _PyGILState_Init is called). */ + if (!autoInterpreterState) return; /* Stick the thread state for this thread in thread local storage. @@ -617,7 +616,7 @@ PyThreadState * PyGILState_GetThisThreadState(void) { - if (autoInterpreterState == NULL || autoTLSkey == 0) + if (autoInterpreterState == NULL) return NULL; return (PyThreadState *)PyThread_get_key_value(autoTLSkey); } From python-checkins at python.org Wed Sep 8 14:50:29 2010 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 8 Sep 2010 14:50:29 +0200 (CEST) Subject: [Python-checkins] r84629 - python/branches/py3k/Objects/bytesobject.c Message-ID: <20100908125029.92BC4EE9AA@mail.python.org> Author: senthil.kumaran Date: Wed Sep 8 14:50:29 2010 New Revision: 84629 Log: Revert the doc change done in r83880. str.replace with negative count value is not a feature. Modified: python/branches/py3k/Objects/bytesobject.c Modified: python/branches/py3k/Objects/bytesobject.c ============================================================================== --- python/branches/py3k/Objects/bytesobject.c (original) +++ python/branches/py3k/Objects/bytesobject.c Wed Sep 8 14:50:29 2010 @@ -2131,8 +2131,7 @@ \n\ Return a copy of B with all occurrences of subsection\n\ old replaced by new. If the optional argument count is\n\ -positive, only the first count occurrences are replaced. A\n\ -negative value of count replaces all occurrences"); +given, only first count occurances are replaced."); static PyObject * bytes_replace(PyBytesObject *self, PyObject *args) From python-checkins at python.org Wed Sep 8 15:00:08 2010 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 8 Sep 2010 15:00:08 +0200 (CEST) Subject: [Python-checkins] r84630 - in python/branches/release31-maint: Objects/bytesobject.c Message-ID: <20100908130008.02A31EEB0D@mail.python.org> Author: senthil.kumaran Date: Wed Sep 8 15:00:07 2010 New Revision: 84630 Log: Merged revisions 84629 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84629 | senthil.kumaran | 2010-09-08 18:20:29 +0530 (Wed, 08 Sep 2010) | 3 lines Revert the doc change done in r83880. str.replace with negative count value is not a feature. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Objects/bytesobject.c Modified: python/branches/release31-maint/Objects/bytesobject.c ============================================================================== --- python/branches/release31-maint/Objects/bytesobject.c (original) +++ python/branches/release31-maint/Objects/bytesobject.c Wed Sep 8 15:00:07 2010 @@ -2557,8 +2557,7 @@ \n\ Return a copy of B with all occurrences of subsection\n\ old replaced by new. If the optional argument count is\n\ -positive, only the first count occurrences are replaced. A\n\ -negative value of count replaces all occurrences"); +given, only first count occurances are replaced."); static PyObject * bytes_replace(PyBytesObject *self, PyObject *args) From python-checkins at python.org Wed Sep 8 18:22:11 2010 From: python-checkins at python.org (matthias.klose) Date: Wed, 8 Sep 2010 18:22:11 +0200 (CEST) Subject: [Python-checkins] r84631 - python/branches/py3k/Python/dynload_shlib.c Message-ID: <20100908162211.3E7D3EEAC2@mail.python.org> Author: matthias.klose Date: Wed Sep 8 18:22:10 2010 New Revision: 84631 Log: PEP 3149: Try to load the extension with the SOABI before trying to load the one without the SOABI in the name. Modified: python/branches/py3k/Python/dynload_shlib.c Modified: python/branches/py3k/Python/dynload_shlib.c ============================================================================== --- python/branches/py3k/Python/dynload_shlib.c (original) +++ python/branches/py3k/Python/dynload_shlib.c Wed Sep 8 18:22:10 2010 @@ -52,8 +52,8 @@ {"MODULE.EXE", "rb", C_EXTENSION}, #else /* !__VMS */ {"." SOABI ".so", "rb", C_EXTENSION}, - {".so", "rb", C_EXTENSION}, {"module." SOABI ".so", "rb", C_EXTENSION}, + {".so", "rb", C_EXTENSION}, {"module.so", "rb", C_EXTENSION}, #endif /* __VMS */ #endif /* defined(PYOS_OS2) && defined(PYCC_GCC) */ From python-checkins at python.org Wed Sep 8 20:48:21 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 8 Sep 2010 20:48:21 +0200 (CEST) Subject: [Python-checkins] r84632 - python/branches/py3k/Lib/random.py Message-ID: <20100908184821.A874AEEB22@mail.python.org> Author: raymond.hettinger Date: Wed Sep 8 20:48:21 2010 New Revision: 84632 Log: * Remove dependency on binascii.hexlify by using int.from_bytes(). * Use the new super() with no arguments. * Replace pow() call with the ** operator. * Increase urandom seeding from 16 bytes to 32 bytes. * Clean-up docstring. Modified: python/branches/py3k/Lib/random.py Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Wed Sep 8 20:48:21 2010 @@ -42,7 +42,6 @@ from math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin from os import urandom as _urandom -from binascii import hexlify as _hexlify import collections as _collections __all__ = ["Random","seed","random","uniform","randint","choice","sample", @@ -97,16 +96,16 @@ None or no argument seeds from current time or from an operating system specific randomness source if available. - For version 2 (the default), all of the bits are used if a is a str, - bytes, or bytearray. For version 1, the hash() of a is used instead. + For version 2 (the default), all of the bits are used if *a *is a str, + bytes, or bytearray. For version 1, the hash() of *a* is used instead. - If a is an int, all bits are used. + If *a* is an int, all bits are used. """ if a is None: try: - a = int(_hexlify(_urandom(16)), 16) + a = int.from_bytes(_urandom(32), 'big') except NotImplementedError: import time a = int(time.time() * 256) # use fractional seconds @@ -114,7 +113,7 @@ if version == 2 and isinstance(a, (str, bytes, bytearray)): if isinstance(a, str): a = a.encode("utf8") - a = int(_hexlify(a), 16) + a = int.from_bytes(a, 'big') super().seed(a) self.gauss_next = None @@ -139,7 +138,7 @@ internalstate = tuple(x % (2**32) for x in internalstate) except ValueError as e: raise TypeError from e - super(Random, self).setstate(internalstate) + super().setstate(internalstate) else: raise ValueError("state with version %s passed to " "Random.setstate() of version %s" % @@ -613,7 +612,7 @@ # Jain, pg. 499; bug fix courtesy Bill Arms u = 1.0 - self.random() - return alpha * pow(-_log(u), 1.0/beta) + return alpha * (-_log(u)) ** (1.0/beta) ## --------------- Operating System Random Source ------------------ @@ -627,7 +626,7 @@ def random(self): """Get the next random number in the range [0.0, 1.0).""" - return (int(_hexlify(_urandom(7)), 16) >> 3) * RECIP_BPF + return (int.from_bytes(_urandom(7), 'big') >> 3) * RECIP_BPF def getrandbits(self, k): """getrandbits(k) -> x. Generates a long int with k random bits.""" @@ -636,7 +635,7 @@ if k != int(k): raise TypeError('number of bits should be an integer') bytes = (k + 7) // 8 # bits / 8 and rounded up - x = int(_hexlify(_urandom(bytes)), 16) + x = int.from_bytes(_urandom(bytes), 'big') return x >> (bytes * 8 - k) # trim excess bits def seed(self, *args, **kwds): From python-checkins at python.org Wed Sep 8 20:58:34 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 8 Sep 2010 20:58:34 +0200 (CEST) Subject: [Python-checkins] r84633 - python/branches/py3k/Lib/random.py Message-ID: <20100908185834.26C82EEB38@mail.python.org> Author: raymond.hettinger Date: Wed Sep 8 20:58:33 2010 New Revision: 84633 Log: One more conversion from pow() to **. Modified: python/branches/py3k/Lib/random.py Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Wed Sep 8 20:58:33 2010 @@ -599,7 +599,7 @@ # Jain, pg. 495 u = 1.0 - self.random() - return 1.0 / pow(u, 1.0/alpha) + return 1.0 / u ** (1.0/alpha) ## -------------------- Weibull -------------------- From python-checkins at python.org Wed Sep 8 21:27:59 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 8 Sep 2010 21:27:59 +0200 (CEST) Subject: [Python-checkins] r84634 - python/branches/py3k/Lib/random.py Message-ID: <20100908192759.A3750EF07@mail.python.org> Author: raymond.hettinger Date: Wed Sep 8 21:27:59 2010 New Revision: 84634 Log: Improve variable name (don't shadow a builtin). Modified: python/branches/py3k/Lib/random.py Modified: python/branches/py3k/Lib/random.py ============================================================================== --- python/branches/py3k/Lib/random.py (original) +++ python/branches/py3k/Lib/random.py Wed Sep 8 21:27:59 2010 @@ -634,9 +634,9 @@ raise ValueError('number of bits must be greater than zero') if k != int(k): raise TypeError('number of bits should be an integer') - bytes = (k + 7) // 8 # bits / 8 and rounded up - x = int.from_bytes(_urandom(bytes), 'big') - return x >> (bytes * 8 - k) # trim excess bits + numbytes = (k + 7) // 8 # bits / 8 and rounded up + x = int.from_bytes(_urandom(numbytes), 'big') + return x >> (numbytes * 8 - k) # trim excess bits def seed(self, *args, **kwds): "Stub method. Not used for a system random number generator." From python-checkins at python.org Wed Sep 8 22:57:49 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 8 Sep 2010 22:57:49 +0200 (CEST) Subject: [Python-checkins] r84635 - in python/branches/py3k: Misc/NEWS Tools/gdb/libpython.py Message-ID: <20100908205749.14E14FD44@mail.python.org> Author: antoine.pitrou Date: Wed Sep 8 22:57:48 2010 New Revision: 84635 Log: Issue #9188: The gdb extension now handles correctly narrow (UCS2) as well as wide (UCS4) unicode builds for both the host interpreter (embedded inside gdb) and the interpreter under test. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Tools/gdb/libpython.py Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Sep 8 22:57:48 2010 @@ -76,6 +76,13 @@ guaranteed to exist in all Python implementations and the names of hash algorithms available in the current process. +Tools/Demos +----------- + +- Issue #9188: The gdb extension now handles correctly narrow (UCS2) as well + as wide (UCS4) unicode builds for both the host interpreter (embedded + inside gdb) and the interpreter under test. + Build ----- Modified: python/branches/py3k/Tools/gdb/libpython.py ============================================================================== --- python/branches/py3k/Tools/gdb/libpython.py (original) +++ python/branches/py3k/Tools/gdb/libpython.py Wed Sep 8 22:57:48 2010 @@ -1065,7 +1065,19 @@ if char == u" ": return True import unicodedata - return unicodedata.category(char)[0] not in ("C", "Z") + return unicodedata.category(char) not in ("C", "Z") + +if sys.maxunicode >= 0x10000: + _unichr = unichr +else: + # Needed for proper surrogate support if sizeof(Py_UNICODE) is 2 in gdb + def _unichr(x): + if x < 0x10000: + return unichr(x) + x -= 0x10000 + ch1 = 0xD800 | (x >> 10) + ch2 = 0xDC00 | (x & 0x3FF) + return unichr(ch1) + unichr(ch2) class PyUnicodeObjectPtr(PyObjectPtr): @@ -1084,11 +1096,33 @@ # Gather a list of ints from the Py_UNICODE array; these are either # UCS-2 or UCS-4 code points: - Py_UNICODEs = [int(field_str[i]) for i in safe_range(field_length)] + if self.char_width() > 2: + Py_UNICODEs = [int(field_str[i]) for i in safe_range(field_length)] + else: + # A more elaborate routine if sizeof(Py_UNICODE) is 2 in the + # inferior process: we must join surrogate pairs. + Py_UNICODEs = [] + i = 0 + while i < field_length: + ucs = int(field_str[i]) + i += 1 + if ucs < 0xD800 or ucs >= 0xDC00 or i == field_length: + Py_UNICODEs.append(ucs) + continue + # This could be a surrogate pair. + ucs2 = int(field_str[i]) + if ucs2 < 0xDC00 or ucs2 > 0xDFFF: + continue + code = (ucs & 0x03FF) << 10 + code |= ucs2 & 0x03FF + code += 0x00010000 + Py_UNICODEs.append(code) + i += 1 # Convert the int code points to unicode characters, and generate a - # local unicode instance: - result = u''.join([unichr(ucs) for ucs in Py_UNICODEs]) + # local unicode instance. + # This splits surrogate pairs if sizeof(Py_UNICODE) is 2 here (in gdb). + result = u''.join([_unichr(ucs) for ucs in Py_UNICODEs]) return result def write_repr(self, out, visited): @@ -1137,20 +1171,16 @@ else: ucs = ch orig_ucs = None + ch2 = None if self.char_width() == 2: - # Get code point from surrogate pair + # If sizeof(Py_UNICODE) is 2 here (in gdb), join + # surrogate pairs before calling _unichr_is_printable. if (i < len(proxy) and 0xD800 <= ord(ch) < 0xDC00 \ and 0xDC00 <= ord(proxy[i]) <= 0xDFFF): ch2 = proxy[i] - code = (ord(ch) & 0x03FF) << 10 - code |= ord(ch2) & 0x03FF - code += 0x00010000 - orig_ucs = ucs - ucs = unichr(code) + ucs = ch + ch2 i += 1 - else: - ch2 = None printable = _unichr_is_printable(ucs) if printable: @@ -1195,7 +1225,7 @@ else: # Copy characters as-is out.write(ch) - if self.char_width() == 2 and (ch2 is not None): + if ch2 is not None: out.write(ch2) out.write(quote) From python-checkins at python.org Wed Sep 8 23:07:40 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 8 Sep 2010 23:07:40 +0200 (CEST) Subject: [Python-checkins] r84636 - python/branches/py3k/Tools/gdb/libpython.py Message-ID: <20100908210740.6B606EE9AB@mail.python.org> Author: antoine.pitrou Date: Wed Sep 8 23:07:40 2010 New Revision: 84636 Log: Add a safety limit to the number of unicode characters we fetch (followup to r84635, suggested by Dave Malcolm). Modified: python/branches/py3k/Tools/gdb/libpython.py Modified: python/branches/py3k/Tools/gdb/libpython.py ============================================================================== --- python/branches/py3k/Tools/gdb/libpython.py (original) +++ python/branches/py3k/Tools/gdb/libpython.py Wed Sep 8 23:07:40 2010 @@ -1103,7 +1103,8 @@ # inferior process: we must join surrogate pairs. Py_UNICODEs = [] i = 0 - while i < field_length: + limit = safety_limit(field_length) + while i < limit: ucs = int(field_str[i]) i += 1 if ucs < 0xD800 or ucs >= 0xDC00 or i == field_length: From python-checkins at python.org Wed Sep 8 23:12:36 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 8 Sep 2010 23:12:36 +0200 (CEST) Subject: [Python-checkins] r84637 - in python/branches/release27-maint: Misc/NEWS Tools/gdb/libpython.py Message-ID: <20100908211236.3FC32EE9A5@mail.python.org> Author: antoine.pitrou Date: Wed Sep 8 23:12:36 2010 New Revision: 84637 Log: Merged revisions 84635-84636 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84635 | antoine.pitrou | 2010-09-08 22:57:48 +0200 (mer., 08 sept. 2010) | 5 lines Issue #9188: The gdb extension now handles correctly narrow (UCS2) as well as wide (UCS4) unicode builds for both the host interpreter (embedded inside gdb) and the interpreter under test. ........ r84636 | antoine.pitrou | 2010-09-08 23:07:40 +0200 (mer., 08 sept. 2010) | 4 lines Add a safety limit to the number of unicode characters we fetch (followup to r84635, suggested by Dave Malcolm). ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Tools/gdb/libpython.py Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Wed Sep 8 23:12:36 2010 @@ -288,6 +288,13 @@ - Issue #7567: Don't call `setupterm' twice. +Tools/Demos +----------- + +- Issue #9188: The gdb extension now handles correctly narrow (UCS2) as well + as wide (UCS4) unicode builds for both the host interpreter (embedded + inside gdb) and the interpreter under test. + Build ----- Modified: python/branches/release27-maint/Tools/gdb/libpython.py ============================================================================== --- python/branches/release27-maint/Tools/gdb/libpython.py (original) +++ python/branches/release27-maint/Tools/gdb/libpython.py Wed Sep 8 23:12:36 2010 @@ -1011,6 +1011,18 @@ _typename = 'PyTypeObject' +if sys.maxunicode >= 0x10000: + _unichr = unichr +else: + # Needed for proper surrogate support if sizeof(Py_UNICODE) is 2 in gdb + def _unichr(x): + if x < 0x10000: + return unichr(x) + x -= 0x10000 + ch1 = 0xD800 | (x >> 10) + ch2 = 0xDC00 | (x & 0x3FF) + return unichr(ch1) + unichr(ch2) + class PyUnicodeObjectPtr(PyObjectPtr): _typename = 'PyUnicodeObject' @@ -1027,37 +1039,36 @@ # Gather a list of ints from the Py_UNICODE array; these are either # UCS-2 or UCS-4 code points: - Py_UNICODEs = [int(field_str[i]) for i in safe_range(field_length)] + if self.char_width() > 2: + Py_UNICODEs = [int(field_str[i]) for i in safe_range(field_length)] + else: + # A more elaborate routine if sizeof(Py_UNICODE) is 2 in the + # inferior process: we must join surrogate pairs. + Py_UNICODEs = [] + i = 0 + limit = safety_limit(field_length) + while i < limit: + ucs = int(field_str[i]) + i += 1 + if ucs < 0xD800 or ucs >= 0xDC00 or i == field_length: + Py_UNICODEs.append(ucs) + continue + # This could be a surrogate pair. + ucs2 = int(field_str[i]) + if ucs2 < 0xDC00 or ucs2 > 0xDFFF: + continue + code = (ucs & 0x03FF) << 10 + code |= ucs2 & 0x03FF + code += 0x00010000 + Py_UNICODEs.append(code) + i += 1 # Convert the int code points to unicode characters, and generate a - # local unicode instance: - result = u''.join([unichr(ucs) for ucs in Py_UNICODEs]) + # local unicode instance. + # This splits surrogate pairs if sizeof(Py_UNICODE) is 2 here (in gdb). + result = u''.join([_unichr(ucs) for ucs in Py_UNICODEs]) return result - def write_repr(self, out, visited): - proxy = self.proxyval(visited) - if self.char_width() == 2: - # sizeof(Py_UNICODE)==2: join surrogates - proxy2 = [] - i = 0 - while i < len(proxy): - ch = proxy[i] - i += 1 - if (i < len(proxy) - and 0xD800 <= ord(ch) < 0xDC00 \ - and 0xDC00 <= ord(proxy[i]) <= 0xDFFF): - # Get code point from surrogate pair - ch2 = proxy[i] - code = (ord(ch) & 0x03FF) << 10 - code |= ord(ch2) & 0x03FF - code += 0x00010000 - i += 1 - proxy2.append(unichr(code)) - else: - proxy2.append(ch) - proxy = u''.join(proxy2) - out.write(repr(proxy)) - def int_from_int(gdbval): return int(str(gdbval)) From python-checkins at python.org Wed Sep 8 23:57:37 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 8 Sep 2010 23:57:37 +0200 (CEST) Subject: [Python-checkins] r84638 - in python/branches/py3k: Lib/test/test_gdb.py Tools/gdb/libpython.py Message-ID: <20100908215737.D506DF831@mail.python.org> Author: antoine.pitrou Date: Wed Sep 8 23:57:37 2010 New Revision: 84638 Log: gdb: fix representation of non-printable surrogate pairs, and workaround a bug in ascii(). Modified: python/branches/py3k/Lib/test/test_gdb.py python/branches/py3k/Tools/gdb/libpython.py Modified: python/branches/py3k/Lib/test/test_gdb.py ============================================================================== --- python/branches/py3k/Lib/test/test_gdb.py (original) +++ python/branches/py3k/Lib/test/test_gdb.py Wed Sep 8 23:57:37 2010 @@ -234,7 +234,9 @@ text.encode(encoding) printable = True except UnicodeEncodeError: - self.assertGdbRepr(text, ascii(text)) + # Workaround ascii() bug on UCS-2 builds: issue #9804 + asc = "'" + text.encode('unicode-escape').decode('ascii') + "'" + self.assertGdbRepr(text, asc) else: self.assertGdbRepr(text) Modified: python/branches/py3k/Tools/gdb/libpython.py ============================================================================== --- python/branches/py3k/Tools/gdb/libpython.py (original) +++ python/branches/py3k/Tools/gdb/libpython.py Wed Sep 8 23:57:37 2010 @@ -1171,9 +1171,8 @@ # Non-ASCII characters else: ucs = ch - orig_ucs = None ch2 = None - if self.char_width() == 2: + if sys.maxunicode < 0x10000: # If sizeof(Py_UNICODE) is 2 here (in gdb), join # surrogate pairs before calling _unichr_is_printable. if (i < len(proxy) @@ -1183,22 +1182,26 @@ ucs = ch + ch2 i += 1 + # Unfortuately, Python 2's unicode type doesn't seem + # to expose the "isprintable" method printable = _unichr_is_printable(ucs) if printable: try: ucs.encode(ENCODING) except UnicodeEncodeError: printable = False - if orig_ucs is not None: - ucs = orig_ucs - i -= 1 # Map Unicode whitespace and control characters # (categories Z* and C* except ASCII space) if not printable: - # Unfortuately, Python 2's unicode type doesn't seem - # to expose the "isprintable" method - code = ord(ucs) + if ch2 is not None: + # Match Python 3's representation of non-printable + # wide characters. + code = (ord(ch) & 0x03FF) << 10 + code |= ord(ch2) & 0x03FF + code += 0x00010000 + else: + code = ord(ucs) # Map 8-bit characters to '\\xhh' if code <= 0xff: From python-checkins at python.org Thu Sep 9 00:44:13 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Thu, 9 Sep 2010 00:44:13 +0200 (CEST) Subject: [Python-checkins] r84639 - in python/branches/py3k: Doc/library/socket.rst Doc/whatsnew/3.2.rst Lib/socket.py Lib/test/test_socket.py Message-ID: <20100908224413.0E373F79A@mail.python.org> Author: giampaolo.rodola Date: Thu Sep 9 00:44:12 2010 New Revision: 84639 Log: Fix issue 9794: adds context manager protocol to socket.socket so that socket.create_connection() can be used with the 'with' statement. Modified: python/branches/py3k/Doc/library/socket.rst python/branches/py3k/Doc/whatsnew/3.2.rst python/branches/py3k/Lib/socket.py python/branches/py3k/Lib/test/test_socket.py Modified: python/branches/py3k/Doc/library/socket.rst ============================================================================== --- python/branches/py3k/Doc/library/socket.rst (original) +++ python/branches/py3k/Doc/library/socket.rst Thu Sep 9 00:44:12 2010 @@ -213,6 +213,9 @@ .. versionchanged:: 3.2 *source_address* was added. + .. versionchanged:: 3.2 + support for the :keyword:`with` statement was added. + .. function:: getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Thu Sep 9 00:44:12 2010 @@ -389,6 +389,12 @@ (Contributed by Giampaolo Rodol?; :issue:`8807`.) +* :func:`socket.create_connection` now supports the context manager protocol + to unconditionally consume :exc:`socket.error` exceptions and to close the + socket when done. + + (Contributed by Giampaolo Rodol?; :issue:`9794`.) + Multi-threading =============== Modified: python/branches/py3k/Lib/socket.py ============================================================================== --- python/branches/py3k/Lib/socket.py (original) +++ python/branches/py3k/Lib/socket.py Thu Sep 9 00:44:12 2010 @@ -93,6 +93,13 @@ self._io_refs = 0 self._closed = False + def __enter__(self): + return self + + def __exit__(self, *args): + if not self._closed: + self.close() + def __repr__(self): """Wrap __repr__() to reveal the real class name.""" s = _socket.socket.__repr__(self) Modified: python/branches/py3k/Lib/test/test_socket.py ============================================================================== --- python/branches/py3k/Lib/test/test_socket.py (original) +++ python/branches/py3k/Lib/test/test_socket.py Thu Sep 9 00:44:12 2010 @@ -1595,6 +1595,49 @@ self.cli.close() + at unittest.skipUnless(thread, 'Threading required for this test.') +class ContextManagersTest(ThreadedTCPSocketTest): + + def _testSocketClass(self): + # base test + with socket.socket() as sock: + self.assertFalse(sock._closed) + self.assertTrue(sock._closed) + # close inside with block + with socket.socket() as sock: + sock.close() + self.assertTrue(sock._closed) + # exception inside with block + with socket.socket() as sock: + self.assertRaises(socket.error, sock.sendall, b'foo') + self.assertTrue(sock._closed) + + def testCreateConnectionBase(self): + conn, addr = self.serv.accept() + data = conn.recv(1024) + conn.sendall(data) + + def _testCreateConnectionBase(self): + address = self.serv.getsockname() + with socket.create_connection(address) as sock: + self.assertFalse(sock._closed) + sock.sendall(b'foo') + self.assertEqual(sock.recv(1024), b'foo') + self.assertTrue(sock._closed) + + def testCreateConnectionClose(self): + conn, addr = self.serv.accept() + data = conn.recv(1024) + conn.sendall(data) + + def _testCreateConnectionClose(self): + address = self.serv.getsockname() + with socket.create_connection(address) as sock: + sock.close() + self.assertTrue(sock._closed) + self.assertRaises(socket.error, sock.sendall, b'foo') + + def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, TestExceptions, BufferIOTest, BasicTCPTest2, BasicUDPTest, UDPTimeoutTest ] @@ -1609,6 +1652,7 @@ NetworkConnectionNoServer, NetworkConnectionAttributesTest, NetworkConnectionBehaviourTest, + ContextManagersTest, ]) if hasattr(socket, "socketpair"): tests.append(BasicSocketPairTest) From python-checkins at python.org Thu Sep 9 03:40:50 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 9 Sep 2010 03:40:50 +0200 (CEST) Subject: [Python-checkins] r84640 - python/branches/py3k/Doc/library/cmd.rst Message-ID: <20100909014050.39A7AEE9E3@mail.python.org> Author: raymond.hettinger Date: Thu Sep 9 03:40:50 2010 New Revision: 84640 Log: Add a working example for the cmd module. Modified: python/branches/py3k/Doc/library/cmd.rst Modified: python/branches/py3k/Doc/library/cmd.rst ============================================================================== --- python/branches/py3k/Doc/library/cmd.rst (original) +++ python/branches/py3k/Doc/library/cmd.rst Thu Sep 9 03:40:50 2010 @@ -203,3 +203,165 @@ :mod:`readline`, on systems that support it, the interpreter will automatically support :program:`Emacs`\ -like line editing and command-history keystrokes.) +Cmd Example +=========== + +.. sectionauthor:: Raymond Hettinger + +The :mod:`cmd` module is mainly useful for building custom shells that let a +user work with a program interactively. + +This section presents a simple example of how to build a shell around a few of +the commands in the :mod:`turtle` module. + +Basic turtle commands such as :meth:`~turtle.forward` are added to a +:class:`Cmd` subclass with method named :meth:`do_forward`. The argument is +converted to a number and dispatched to the turtle module. The docstring is +used in the help utility provided by the shell. + +The example also includes a basic record and playback facility implemented with +the :meth:`~Cmd.precmd` method which is responsible for converting the input to +lowercase and writing the commands to a file. The :meth:`do_playback` method +reads the file and adds the recorded commands to the :attr:`cmdqueue` for +immediate playback:: + + import cmd, sys + from turtle import * + + class TurtleShell(cmd.Cmd): + intro = 'Welcome to the turtle shell. Type help or ? to list commands.\n' + prompt = '(turtle) ' + file = None + + # ----- basic turtle commands ----- + def do_forward(self, arg): + 'Move the turtle forward by the specified distance: FORWARD 10' + forward(*parse(arg)) + def do_right(self, arg): + 'Turn turtle right by given number of degrees: RIGHT 20' + right(*parse(arg)) + def do_left(self, arg): + 'Turn turtle left by given number of degrees: LEFT 90' + right(*parse(arg)) + def do_goto(self, arg): + 'Move turtle to an absolute position with changing orientation. GOTO 100 200' + goto(*parse(arg)) + def do_home(self, arg): + 'Return turtle to the home postion: HOME' + home() + def do_circle(self, arg): + 'Draw circle with given radius an options extent and steps: CIRCLE 50' + circle(*parse(arg)) + def do_position(self, arg): + 'Print the current turle position: POSITION' + print('Current position is %d %d\n' % position()) + def do_heading(self, arg): + 'Print the current turle heading in degrees: HEADING' + print('Current heading is %d\n' % (heading(),)) + def do_color(self, arg): + 'Set the color: COLOR BLUE' + color(arg.lower()) + def do_undo(self, arg): + 'Undo (repeatedly) the last turtle action(s): UNDO' + def do_reset(self, arg): + 'Clear the screen and return turtle to center: RESET' + reset() + def do_bye(self, arg): + 'Stop recording, close the turtle window, and exit: BYE' + print('Thank you for using Turtle') + self.close() + bye() + sys.exit(0) + + # ----- record and playback ----- + def do_record(self, arg): + 'Save future commands to filename: RECORD rose.cmd' + self.file = open(arg, 'w') + def do_playback(self, arg): + 'Playback commands from a file: PLAYBACK rose.cmd' + self.close() + cmds = open(arg).read().splitlines() + self.cmdqueue.extend(cmds) + def precmd(self, line): + line = line.lower() + if self.file and 'playback' not in line: + print(line, file=self.file) + return line + def close(self): + if self.file: + self.file.close() + self.file = None + + def parse(arg): + 'Convert a series of zero or more numbers to an argument tuple' + return tuple(map(int, arg.split())) + + if __name__ == '__main__': + TurtleShell().cmdloop() + + +Here is a sample session with the turtle shell showing the help functions, using +blank lines to repeat commands, and the simple record and playback facility:: + + Welcome to the turtle shell. Type help or ? to list commands. + + (turtle) ? + + Documented commands (type help ): + ======================================== + bye color goto home playback record right + circle forward heading left position reset undo + + Undocumented commands: + ====================== + help + + (turtle) help forward + Move the turtle forward by the specified distance: FORWARD 10 + (turtle) record spiral.cmd + (turtle) position + Current position is 0 0 + + (turtle) heading + Current heading is 0 + + (turtle) reset + (turtle) circle 20 + (turtle) right 30 + (turtle) circle 40 + (turtle) right 30 + (turtle) circle 60 + (turtle) right 30 + (turtle) circle 80 + (turtle) right 30 + (turtle) circle 100 + (turtle) right 30 + (turtle) circle 120 + (turtle) right 30 + (turtle) circle 120 + (turtle) heading + Current heading is 180 + + (turtle) forward 100 + (turtle) + (turtle) right 90 + (turtle) forward 100 + (turtle) + (turtle) right 90 + (turtle) forward 400 + (turtle) right 90 + (turtle) forward 500 + (turtle) right 90 + (turtle) forward 400 + (turtle) right 90 + (turtle) forward 300 + (turtle) playback spiral.cmd + Current position is 0 0 + + Current heading is 0 + + Current heading is 180 + + (turtle) bye + Thank you for using Turtle + From solipsis at pitrou.net Thu Sep 9 05:03:23 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 09 Sep 2010 05:03:23 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84639): sum=0 Message-ID: py3k results for svn r84639 (hg cset 02bcae26df4d) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflog2Wob7S', '-x'] From python-checkins at python.org Thu Sep 9 05:53:22 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 9 Sep 2010 05:53:22 +0200 (CEST) Subject: [Python-checkins] r84641 - in python/branches/py3k: Doc/library/cmd.rst Lib/cmd.py Lib/test/test_cmd.py Message-ID: <20100909035322.BD46EF794@mail.python.org> Author: raymond.hettinger Date: Thu Sep 9 05:53:22 2010 New Revision: 84641 Log: Add docstring to cmd.Cmd.do_help() Modified: python/branches/py3k/Doc/library/cmd.rst python/branches/py3k/Lib/cmd.py python/branches/py3k/Lib/test/test_cmd.py Modified: python/branches/py3k/Doc/library/cmd.rst ============================================================================== --- python/branches/py3k/Doc/library/cmd.rst (original) +++ python/branches/py3k/Doc/library/cmd.rst Thu Sep 9 05:53:22 2010 @@ -312,10 +312,6 @@ bye color goto home playback record right circle forward heading left position reset undo - Undocumented commands: - ====================== - help - (turtle) help forward Move the turtle forward by the specified distance: FORWARD 10 (turtle) record spiral.cmd Modified: python/branches/py3k/Lib/cmd.py ============================================================================== --- python/branches/py3k/Lib/cmd.py (original) +++ python/branches/py3k/Lib/cmd.py Thu Sep 9 05:53:22 2010 @@ -288,6 +288,7 @@ return list(commands | topics) def do_help(self, arg): + 'List available commands with "help" or detailed help with "help cmd".' if arg: # XXX check arg syntax try: Modified: python/branches/py3k/Lib/test/test_cmd.py ============================================================================== --- python/branches/py3k/Lib/test/test_cmd.py (original) +++ python/branches/py3k/Lib/test/test_cmd.py Thu Sep 9 05:53:22 2010 @@ -84,11 +84,11 @@ Documented commands (type help ): ======================================== - add + add help Undocumented commands: ====================== - exit help shell + exit shell Test for the function print_topics(): @@ -125,11 +125,11 @@ Documented commands (type help ): ======================================== - add + add help Undocumented commands: ====================== - exit help shell + exit shell help text for add Hello from postloop From python-checkins at python.org Thu Sep 9 06:32:39 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 9 Sep 2010 06:32:39 +0200 (CEST) Subject: [Python-checkins] r84642 - in python/branches/py3k: Doc/library/tokenize.rst Lib/tokenize.py Message-ID: <20100909043239.3BAA4FAD9@mail.python.org> Author: raymond.hettinger Date: Thu Sep 9 06:32:39 2010 New Revision: 84642 Log: Improve the repr for the TokenInfo named tuple. Modified: python/branches/py3k/Doc/library/tokenize.rst python/branches/py3k/Lib/tokenize.py Modified: python/branches/py3k/Doc/library/tokenize.rst ============================================================================== --- python/branches/py3k/Doc/library/tokenize.rst (original) +++ python/branches/py3k/Doc/library/tokenize.rst Thu Sep 9 06:32:39 2010 @@ -111,7 +111,7 @@ return fp.read() -Example of a script re-writer that transforms float literals into Decimal +Example of a script rewriter that transforms float literals into Decimal objects:: from tokenize import tokenize, untokenize, NUMBER, STRING, NAME, OP Modified: python/branches/py3k/Lib/tokenize.py ============================================================================== --- python/branches/py3k/Lib/tokenize.py (original) +++ python/branches/py3k/Lib/tokenize.py Thu Sep 9 06:32:39 2010 @@ -63,7 +63,9 @@ return result def __repr__(self): - return 'TokenInfo(type=%r, string=%r, start=%r, end=%r, line=%r)' % self + typ = self[0] + return 'TokenInfo(type=%s, string=%r, start=%r, end=%r, line=%r)' % \ + ((('%d (%s)' % (typ, tok_name[typ])),) + self[1:]) def _asdict(self): 'Return a new dict which maps field names to their values' @@ -550,3 +552,28 @@ # library that expect to be able to use tokenize with strings def generate_tokens(readline): return _tokenize(readline, None) + +if __name__ == "__main__": + # Quick sanity check + s = b'''def parseline(self, line): + """Parse the line into a command name and a string containing + the arguments. Returns a tuple containing (command, args, line). + 'command' and 'args' may be None if the line couldn't be parsed. + """ + line = line.strip() + if not line: + return None, None, line + elif line[0] == '?': + line = 'help ' + line[1:] + elif line[0] == '!': + if hasattr(self, 'do_shell'): + line = 'shell ' + line[1:] + else: + return None, None, line + i, n = 0, len(line) + while i < n and line[i] in self.identchars: i = i+1 + cmd, arg = line[:i], line[i:].strip() + return cmd, arg, line + ''' + for tok in tokenize(iter(s.splitlines()).__next__): + print(tok) From python-checkins at python.org Thu Sep 9 08:08:36 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Thu, 9 Sep 2010 08:08:36 +0200 (CEST) Subject: [Python-checkins] r84643 - in python/branches/py3k/PC/VC6: pythoncore.dsp readme.txt tcl852.patch Message-ID: <20100909060836.A2D26F275@mail.python.org> Author: hirokazu.yamamoto Date: Thu Sep 9 08:08:36 2010 New Revision: 84643 Log: Updated VC6 files. * pythoncore.dsp: updated project file * readme.txt: removed dead link * tcl852.patch: fixed patch. it was doubled. Modified: python/branches/py3k/PC/VC6/pythoncore.dsp python/branches/py3k/PC/VC6/readme.txt python/branches/py3k/PC/VC6/tcl852.patch Modified: python/branches/py3k/PC/VC6/pythoncore.dsp ============================================================================== --- python/branches/py3k/PC/VC6/pythoncore.dsp (original) +++ python/branches/py3k/PC/VC6/pythoncore.dsp Thu Sep 9 08:08:36 2010 @@ -133,6 +133,10 @@ # End Source File # Begin Source File +SOURCE=..\..\Modules\_datetimemodule.c +# End Source File +# Begin Source File + SOURCE=..\..\Modules\_functoolsmodule.c # End Source File # Begin Source File @@ -185,6 +189,10 @@ # End Source File # Begin Source File +SOURCE=..\..\Modules\_time.c +# End Source File +# Begin Source File + SOURCE=..\..\Python\_warnings.c # End Source File # Begin Source File @@ -309,7 +317,7 @@ # End Source File # Begin Source File -SOURCE=..\..\Modules\datetimemodule.c +SOURCE=..\..\Python\dynamic_annotations.c # End Source File # Begin Source File @@ -635,6 +643,10 @@ # End Source File # Begin Source File +SOURCE=..\..\Python\pytime.c +# End Source File +# Begin Source File + SOURCE=..\..\Objects\rangeobject.c # End Source File # Begin Source File Modified: python/branches/py3k/PC/VC6/readme.txt ============================================================================== --- python/branches/py3k/PC/VC6/readme.txt (original) +++ python/branches/py3k/PC/VC6/readme.txt Thu Sep 9 08:08:36 2010 @@ -2,8 +2,7 @@ ------------------------------------- This directory is used to build Python for Win32 platforms, e.g. Windows 2000 and XP. It requires Microsoft Visual C++ 6.x or 5.x and Platform -SDK February 2003 Edition (Core SDK). You can download this SDK from -http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdk-full.htm. +SDK February 2003 Edition (Core SDK). (For other Windows platforms and compilers, see ../readme.txt.) All you need to do is open the workspace "pcbuild.dsw" in MSVC++, select Modified: python/branches/py3k/PC/VC6/tcl852.patch ============================================================================== --- python/branches/py3k/PC/VC6/tcl852.patch (original) +++ python/branches/py3k/PC/VC6/tcl852.patch Thu Sep 9 08:08:36 2010 @@ -9,14 +9,3 @@ typedef struct _stati64 Tcl_StatBuf; # else typedef struct _stat64 Tcl_StatBuf; ---- tcl8.5.2\generic\tcl.h Fri Jun 13 03:35:39 2008 -+++ tcl8.5.2\generic\tcl.h Sun Jan 4 16:52:30 2009 -@@ -367,7 +367,7 @@ - typedef struct stati64 Tcl_StatBuf; - # define TCL_LL_MODIFIER "L" - # else /* __BORLANDC__ */ --# if _MSC_VER < 1400 && !defined(_M_IX86) -+# if _MSC_VER < 1400 /*&& !defined(_M_IX86)*/ - typedef struct _stati64 Tcl_StatBuf; - # else - typedef struct _stat64 Tcl_StatBuf; From python-checkins at python.org Thu Sep 9 08:14:23 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Thu, 9 Sep 2010 08:14:23 +0200 (CEST) Subject: [Python-checkins] r84644 - in python/branches/py3k: Misc/ACKS PC/VS7.1/pythoncore.vcproj Message-ID: <20100909061423.CE919F4A6@mail.python.org> Author: hirokazu.yamamoto Date: Thu Sep 9 08:14:23 2010 New Revision: 84644 Log: Updated VS7.1 project file. (I cannot test this file because I don't have VS7.1) Modified: python/branches/py3k/Misc/ACKS python/branches/py3k/PC/VS7.1/pythoncore.vcproj Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Thu Sep 9 08:14:23 2010 @@ -902,6 +902,7 @@ Heiko Wundram Doug Wyatt Florent Xicluna +Hirokazu Yamamoto Ka-Ping Yee Bob Yodlowski Danny Yoo Modified: python/branches/py3k/PC/VS7.1/pythoncore.vcproj ============================================================================== --- python/branches/py3k/PC/VS7.1/pythoncore.vcproj (original) +++ python/branches/py3k/PC/VS7.1/pythoncore.vcproj Thu Sep 9 08:14:23 2010 @@ -391,9 +391,15 @@ RelativePath="..\..\Modules\_codecsmodule.c"> + + + + + RelativePath="..\..\Modules\_time.c"> + RelativePath="..\..\Modules\_weakref.c"> @@ -472,6 +478,9 @@ RelativePath="..\..\Objects\bytes_methods.c"> + + - - - - + + - - + + Author: hirokazu.yamamoto Date: Thu Sep 9 08:24:43 2010 New Revision: 84645 Log: PCBuild cosmetic fixes. * pythoncore.vcproj: Fixed indentation * _multiprocessing.vcproj: Converted ProjectGUID to uppercase. Otherwise, VS8 _multiprocessing.vcproj created by vs9to8.py was modified every time loads it in VS8 IDE. Modified: python/branches/py3k/PCbuild/_multiprocessing.vcproj python/branches/py3k/PCbuild/pythoncore.vcproj Modified: python/branches/py3k/PCbuild/_multiprocessing.vcproj ============================================================================== --- python/branches/py3k/PCbuild/_multiprocessing.vcproj (original) +++ python/branches/py3k/PCbuild/_multiprocessing.vcproj Thu Sep 9 08:24:43 2010 @@ -3,7 +3,7 @@ ProjectType="Visual C++" Version="9,00" Name="_multiprocessing" - ProjectGUID="{9e48b300-37d1-11dd-8c41-005056c00008}" + ProjectGUID="{9E48B300-37D1-11DD-8C41-005056C00008}" RootNamespace="_multiprocessing" Keyword="Win32Proj" TargetFrameworkVersion="196613" Modified: python/branches/py3k/PCbuild/pythoncore.vcproj ============================================================================== --- python/branches/py3k/PCbuild/pythoncore.vcproj (original) +++ python/branches/py3k/PCbuild/pythoncore.vcproj Thu Sep 9 08:24:43 2010 @@ -842,7 +842,10 @@ RelativePath="..\Include\pyarena.h" > - + + @@ -1391,10 +1394,10 @@ RelativePath="..\Objects\bytesobject.c" > - - + + From python-checkins at python.org Thu Sep 9 09:15:18 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 9 Sep 2010 09:15:18 +0200 (CEST) Subject: [Python-checkins] r84646 - python/branches/py3k/Lib/tokenize.py Message-ID: <20100909071518.94FC7F943@mail.python.org> Author: raymond.hettinger Date: Thu Sep 9 09:15:18 2010 New Revision: 84646 Log: Experiment: Let collections.namedtuple() do the work. This should work now that _collections is pre-built. The buildbots will tell us shortly. Modified: python/branches/py3k/Lib/tokenize.py Modified: python/branches/py3k/Lib/tokenize.py ============================================================================== --- python/branches/py3k/Lib/tokenize.py (original) +++ python/branches/py3k/Lib/tokenize.py Thu Sep 9 09:15:18 2010 @@ -28,6 +28,7 @@ import sys from token import * from codecs import lookup, BOM_UTF8 +import collections cookie_re = re.compile("coding[:=]\s*([-\w.]+)") import token @@ -44,49 +45,12 @@ tok_name[ENCODING] = 'ENCODING' N_TOKENS += 3 -class TokenInfo(tuple): - 'TokenInfo(type, string, start, end, line)' - - __slots__ = () - - _fields = ('type', 'string', 'start', 'end', 'line') - - def __new__(cls, type, string, start, end, line): - return tuple.__new__(cls, (type, string, start, end, line)) - - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - 'Make a new TokenInfo object from a sequence or iterable' - result = new(cls, iterable) - if len(result) != 5: - raise TypeError('Expected 5 arguments, got %d' % len(result)) - return result - +class TokenInfo(collections.namedtuple('TokenInfo', 'type string start end line')): def __repr__(self): - typ = self[0] + typ = self.type return 'TokenInfo(type=%s, string=%r, start=%r, end=%r, line=%r)' % \ ((('%d (%s)' % (typ, tok_name[typ])),) + self[1:]) - def _asdict(self): - 'Return a new dict which maps field names to their values' - return dict(zip(self._fields, self)) - - def _replace(self, **kwds): - 'Return a new TokenInfo object replacing specified fields with new values' - result = self._make(map(kwds.pop, ('type', 'string', 'start', 'end', 'line'), self)) - if kwds: - raise ValueError('Got unexpected field names: %r' % kwds.keys()) - return result - - def __getnewargs__(self): - return tuple(self) - - type = property(lambda t: t[0]) - string = property(lambda t: t[1]) - start = property(lambda t: t[2]) - end = property(lambda t: t[3]) - line = property(lambda t: t[4]) - def group(*choices): return '(' + '|'.join(choices) + ')' def any(*choices): return group(*choices) + '*' def maybe(*choices): return group(*choices) + '?' From python-checkins at python.org Thu Sep 9 10:29:05 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 9 Sep 2010 10:29:05 +0200 (CEST) Subject: [Python-checkins] r84647 - python/branches/py3k/Lib/tokenize.py Message-ID: <20100909082905.A93C1E4D4@mail.python.org> Author: raymond.hettinger Date: Thu Sep 9 10:29:05 2010 New Revision: 84647 Log: A little bit more readable repr method. Modified: python/branches/py3k/Lib/tokenize.py Modified: python/branches/py3k/Lib/tokenize.py ============================================================================== --- python/branches/py3k/Lib/tokenize.py (original) +++ python/branches/py3k/Lib/tokenize.py Thu Sep 9 10:29:05 2010 @@ -47,9 +47,9 @@ class TokenInfo(collections.namedtuple('TokenInfo', 'type string start end line')): def __repr__(self): - typ = self.type - return 'TokenInfo(type=%s, string=%r, start=%r, end=%r, line=%r)' % \ - ((('%d (%s)' % (typ, tok_name[typ])),) + self[1:]) + annotated_type = '%d (%s)' % (self.type, tok_name[self.type]) + return ('TokenInfo(type=%s, string=%r, start=%r, end=%r, line=%r)' % + self._replace(type=annotated_type)) def group(*choices): return '(' + '|'.join(choices) + ')' def any(*choices): return group(*choices) + '*' From python-checkins at python.org Thu Sep 9 14:31:00 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 9 Sep 2010 14:31:00 +0200 (CEST) Subject: [Python-checkins] r84648 - in python/branches/py3k: Lib/pprint.py Lib/test/test_pprint.py Misc/NEWS Message-ID: <20100909123100.795EAF4A6@mail.python.org> Author: raymond.hettinger Date: Thu Sep 9 14:31:00 2010 New Revision: 84648 Log: Have pprint() respect the order in an OrderedDict. Modified: python/branches/py3k/Lib/pprint.py python/branches/py3k/Lib/test/test_pprint.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/pprint.py ============================================================================== --- python/branches/py3k/Lib/pprint.py (original) +++ python/branches/py3k/Lib/pprint.py Thu Sep 9 14:31:00 2010 @@ -35,7 +35,7 @@ """ import sys as _sys - +from collections import OrderedDict as _OrderedDict from io import StringIO as _StringIO __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr", @@ -163,7 +163,7 @@ if sepLines: r = getattr(typ, "__repr__", None) - if issubclass(typ, dict) and r is dict.__repr__: + if issubclass(typ, dict): write('{') if self._indent_per_level > 1: write((self._indent_per_level - 1) * ' ') @@ -171,7 +171,10 @@ if length: context[objid] = 1 indent = indent + self._indent_per_level - items = sorted(object.items(), key=_safe_tuple) + if issubclass(typ, _OrderedDict): + items = list(object.items()) + else: + items = sorted(object.items(), key=_safe_tuple) key, ent = items[0] rep = self._repr(key, context, level) write(rep) Modified: python/branches/py3k/Lib/test/test_pprint.py ============================================================================== --- python/branches/py3k/Lib/test/test_pprint.py (original) +++ python/branches/py3k/Lib/test/test_pprint.py Thu Sep 9 14:31:00 2010 @@ -3,6 +3,8 @@ import unittest import test.test_set import random +import collections +import itertools # list, tuple and dict subclasses that do or don't overwrite __repr__ class list2(list): @@ -195,6 +197,20 @@ self.assertEqual(pprint.pformat({"xy\tab\n": (3,), 5: [[]], (): {}}), r"{5: [[]], 'xy\tab\n': (3,), (): {}}") + def test_ordered_dict(self): + words = 'the quick brown fox jumped over a lazy dog'.split() + d = collections.OrderedDict(zip(words, itertools.count())) + self.assertEqual(pprint.pformat(d), +"""\ +{'the': 0, + 'quick': 1, + 'brown': 2, + 'fox': 3, + 'jumped': 4, + 'over': 5, + 'a': 6, + 'lazy': 7, + 'dog': 8}""") def test_subclassing(self): o = {'names with spaces': 'should be presented using repr()', 'others.should.not.be': 'like.this'} Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Sep 9 14:31:00 2010 @@ -16,6 +16,9 @@ Library ------- +- The pprint module now supports printing OrderedDicts in their given + order (formerly, it would sort the keys). + - Logging: Added QueueHandler class to facilitate logging usage with multiprocessing. From python-checkins at python.org Thu Sep 9 14:59:39 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 9 Sep 2010 14:59:39 +0200 (CEST) Subject: [Python-checkins] r84649 - in python/branches/py3k: Doc/library/stdtypes.rst Lib/test/test_memoryview.py Misc/NEWS Objects/memoryobject.c Message-ID: <20100909125939.AB4B2EE98D@mail.python.org> Author: antoine.pitrou Date: Thu Sep 9 14:59:39 2010 New Revision: 84649 Log: Issue #9757: memoryview objects get a release() method to release the underlying buffer (previously this was only done when deallocating the memoryview), and gain support for the context management protocol. Modified: python/branches/py3k/Doc/library/stdtypes.rst python/branches/py3k/Lib/test/test_memoryview.py python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/memoryobject.c Modified: python/branches/py3k/Doc/library/stdtypes.rst ============================================================================== --- python/branches/py3k/Doc/library/stdtypes.rst (original) +++ python/branches/py3k/Doc/library/stdtypes.rst Thu Sep 9 14:59:39 2010 @@ -2311,7 +2311,40 @@ Notice how the size of the memoryview object cannot be changed. - :class:`memoryview` has two methods: + :class:`memoryview` has several methods: + + .. method:: release() + + Release the underlying buffer exposed by the memoryview object. Many + objects take special actions when a view is held on them (for example, + a :class:`bytearray` would temporarily forbid resizing); therefore, + calling release() is handy to remove these restrictions (and free any + dangling resources) as soon as possible. + + After this method has been called, any further operation on the view + raises a :class:`ValueError` (except :meth:`release()` itself which can + be called multiple times):: + + >>> m = memoryview(b'abc') + >>> m.release() + >>> m[0] + Traceback (most recent call last): + File "", line 1, in + ValueError: operation forbidden on released memoryview object + + The context management protocol can be used for a similar effect, + using the ``with`` statement:: + + >>> with memoryview(b'abc') as m: + ... m[0] + ... + b'a' + >>> m[0] + Traceback (most recent call last): + File "", line 1, in + ValueError: operation forbidden on released memoryview object + + .. versionadded:: 3.2 .. method:: tobytes() Modified: python/branches/py3k/Lib/test/test_memoryview.py ============================================================================== --- python/branches/py3k/Lib/test/test_memoryview.py (original) +++ python/branches/py3k/Lib/test/test_memoryview.py Thu Sep 9 14:59:39 2010 @@ -225,6 +225,51 @@ gc.collect() self.assertTrue(wr() is None, wr()) + def _check_released(self, m, tp): + check = self.assertRaisesRegexp(ValueError, "released") + with check: bytes(m) + with check: m.tobytes() + with check: m.tolist() + with check: m[0] + with check: m[0] = b'x' + with check: len(m) + with check: m.format + with check: m.itemsize + with check: m.ndim + with check: m.readonly + with check: m.shape + with check: m.strides + with check: + with m: + pass + # str() and repr() still function + self.assertIn("released memory", str(m)) + self.assertIn("released memory", repr(m)) + self.assertEqual(m, m) + self.assertNotEqual(m, memoryview(tp(self._source))) + self.assertNotEqual(m, tp(self._source)) + + def test_contextmanager(self): + for tp in self._types: + b = tp(self._source) + m = self._view(b) + with m as cm: + self.assertIs(cm, m) + self._check_released(m, tp) + m = self._view(b) + # Can release explicitly inside the context manager + with m: + m.release() + + def test_release(self): + for tp in self._types: + b = tp(self._source) + m = self._view(b) + m.release() + self._check_released(m, tp) + # Can be called a second time (it's a no-op) + m.release() + self._check_released(m, tp) # Variations on source objects for the buffer: bytes-like objects, then arrays # with itemsize > 1. Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Sep 9 14:59:39 2010 @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #9757: memoryview objects get a release() method to release the + underlying buffer (previously this was only done when deallocating the + memoryview), and gain support for the context management protocol. + - Issue #9797: pystate.c wrongly assumed that zero couldn't be a valid thread-local storage key. Modified: python/branches/py3k/Objects/memoryobject.c ============================================================================== --- python/branches/py3k/Objects/memoryobject.c (original) +++ python/branches/py3k/Objects/memoryobject.c Thu Sep 9 14:59:39 2010 @@ -3,6 +3,23 @@ #include "Python.h" +#define IS_RELEASED(memobj) \ + (((PyMemoryViewObject *) memobj)->view.buf == NULL) + +#define CHECK_RELEASED(memobj) \ + if (IS_RELEASED(memobj)) { \ + PyErr_SetString(PyExc_ValueError, \ + "operation forbidden on released memoryview object"); \ + return NULL; \ + } + +#define CHECK_RELEASED_INT(memobj) \ + if (IS_RELEASED(memobj)) { \ + PyErr_SetString(PyExc_ValueError, \ + "operation forbidden on released memoryview object"); \ + return -1; \ + } + static Py_ssize_t get_shape0(Py_buffer *buf) { @@ -34,6 +51,7 @@ memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags) { int res = 0; + CHECK_RELEASED_INT(self); /* XXX for whatever reason fixing the flags seems necessary */ if (self->view.readonly) flags &= ~PyBUF_WRITABLE; @@ -330,12 +348,14 @@ static PyObject * memory_format_get(PyMemoryViewObject *self) { + CHECK_RELEASED(self); return PyUnicode_FromString(self->view.format); } static PyObject * memory_itemsize_get(PyMemoryViewObject *self) { + CHECK_RELEASED(self); return PyLong_FromSsize_t(self->view.itemsize); } @@ -366,30 +386,35 @@ static PyObject * memory_shape_get(PyMemoryViewObject *self) { + CHECK_RELEASED(self); return _IntTupleFromSsizet(self->view.ndim, self->view.shape); } static PyObject * memory_strides_get(PyMemoryViewObject *self) { + CHECK_RELEASED(self); return _IntTupleFromSsizet(self->view.ndim, self->view.strides); } static PyObject * memory_suboffsets_get(PyMemoryViewObject *self) { + CHECK_RELEASED(self); return _IntTupleFromSsizet(self->view.ndim, self->view.suboffsets); } static PyObject * memory_readonly_get(PyMemoryViewObject *self) { + CHECK_RELEASED(self); return PyBool_FromLong(self->view.readonly); } static PyObject * memory_ndim_get(PyMemoryViewObject *self) { + CHECK_RELEASED(self); return PyLong_FromLong(self->view.ndim); } @@ -408,6 +433,7 @@ static PyObject * memory_tobytes(PyMemoryViewObject *mem, PyObject *noargs) { + CHECK_RELEASED(mem); return PyObject_CallFunctionObjArgs( (PyObject *) &PyBytes_Type, mem, NULL); } @@ -423,6 +449,7 @@ PyObject *res, *item; char *buf; + CHECK_RELEASED(mem); if (strcmp(view->format, "B") || view->itemsize != 1) { PyErr_SetString(PyExc_NotImplementedError, "tolist() only supports byte views"); @@ -449,17 +476,9 @@ return res; } -static PyMethodDef memory_methods[] = { - {"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, NULL}, - {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, NULL}, - {NULL, NULL} /* sentinel */ -}; - - static void -memory_dealloc(PyMemoryViewObject *self) +do_release(PyMemoryViewObject *self) { - _PyObject_GC_UNTRACK(self); if (self->view.obj != NULL) { if (self->base && PyTuple_Check(self->base)) { /* Special case when first element is generic object @@ -484,19 +503,57 @@ } Py_CLEAR(self->base); } + self->view.obj = NULL; + self->view.buf = NULL; +} + +static PyObject * +memory_enter(PyObject *self, PyObject *args) +{ + CHECK_RELEASED(self); + Py_INCREF(self); + return self; +} + +static PyObject * +memory_exit(PyObject *self, PyObject *args) +{ + do_release((PyMemoryViewObject *) self); + Py_RETURN_NONE; +} + +static PyMethodDef memory_methods[] = { + {"release", memory_exit, METH_NOARGS}, + {"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, NULL}, + {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, NULL}, + {"__enter__", memory_enter, METH_NOARGS}, + {"__exit__", memory_exit, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +static void +memory_dealloc(PyMemoryViewObject *self) +{ + _PyObject_GC_UNTRACK(self); + do_release(self); PyObject_GC_Del(self); } static PyObject * memory_repr(PyMemoryViewObject *self) { - return PyUnicode_FromFormat("", self); + if (IS_RELEASED(self)) + return PyUnicode_FromFormat("", self); + else + return PyUnicode_FromFormat("", self); } /* Sequence methods */ static Py_ssize_t memory_length(PyMemoryViewObject *self) { + CHECK_RELEASED_INT(self); return get_shape0(&self->view); } @@ -508,6 +565,7 @@ { Py_buffer *view = &(self->view); + CHECK_RELEASED(self); if (view->ndim == 0) { PyErr_SetString(PyExc_IndexError, "invalid indexing of 0-dim memory"); @@ -557,6 +615,7 @@ Py_buffer *view; view = &(self->view); + CHECK_RELEASED(self); if (view->ndim == 0) { if (key == Py_Ellipsis || (PyTuple_Check(key) && PyTuple_GET_SIZE(key)==0)) { @@ -626,6 +685,7 @@ Py_buffer *view = &(self->view); char *srcbuf, *destbuf; + CHECK_RELEASED_INT(self); if (view->readonly) { PyErr_SetString(PyExc_TypeError, "cannot modify read-only memory"); @@ -718,6 +778,11 @@ ww.obj = NULL; if (op != Py_EQ && op != Py_NE) goto _notimpl; + if ((PyMemoryView_Check(v) && IS_RELEASED(v)) || + (PyMemoryView_Check(w) && IS_RELEASED(w))) { + equal = (v == w); + goto _end; + } if (PyObject_GetBuffer(v, &vv, PyBUF_CONTIG_RO) == -1) { PyErr_Clear(); goto _notimpl; From python-checkins at python.org Thu Sep 9 15:31:47 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 9 Sep 2010 15:31:47 +0200 (CEST) Subject: [Python-checkins] r84650 - python/branches/py3k/Lib/test/test_ssl.py Message-ID: <20100909133147.278ABEE990@mail.python.org> Author: antoine.pitrou Date: Thu Sep 9 15:31:46 2010 New Revision: 84650 Log: Use transient_internet() where appropriate in test_ssl (svn.python.org is sometimes unavailable) Modified: python/branches/py3k/Lib/test/test_ssl.py Modified: python/branches/py3k/Lib/test/test_ssl.py ============================================================================== --- python/branches/py3k/Lib/test/test_ssl.py (original) +++ python/branches/py3k/Lib/test/test_ssl.py Thu Sep 9 15:31:46 2010 @@ -305,63 +305,59 @@ class NetworkedTests(unittest.TestCase): - def setUp(self): - self.old_timeout = socket.getdefaulttimeout() - socket.setdefaulttimeout(30) - - def tearDown(self): - socket.setdefaulttimeout(self.old_timeout) def test_connect(self): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE) - try: - s.connect(("svn.python.org", 443)) - self.assertEqual({}, s.getpeercert()) - finally: - s.close() + with support.transient_internet("svn.python.org"): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE) + try: + s.connect(("svn.python.org", 443)) + self.assertEqual({}, s.getpeercert()) + finally: + s.close() - # this should fail because we have no verification certs - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED) - self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed", - s.connect, ("svn.python.org", 443)) - s.close() - - # this should succeed because we specify the root cert - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=SVN_PYTHON_ORG_ROOT_CERT) - try: - s.connect(("svn.python.org", 443)) - self.assertTrue(s.getpeercert()) - finally: + # this should fail because we have no verification certs + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED) + self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed", + s.connect, ("svn.python.org", 443)) s.close() + # this should succeed because we specify the root cert + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=SVN_PYTHON_ORG_ROOT_CERT) + try: + s.connect(("svn.python.org", 443)) + self.assertTrue(s.getpeercert()) + finally: + s.close() + def test_connect_with_context(self): - # Same as test_connect, but with a separately created context - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - s.connect(("svn.python.org", 443)) - try: - self.assertEqual({}, s.getpeercert()) - finally: - s.close() - # This should fail because we have no verification certs - ctx.verify_mode = ssl.CERT_REQUIRED - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed", - s.connect, ("svn.python.org", 443)) - s.close() - # This should succeed because we specify the root cert - ctx.load_verify_locations(SVN_PYTHON_ORG_ROOT_CERT) - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - s.connect(("svn.python.org", 443)) - try: - cert = s.getpeercert() - self.assertTrue(cert) - finally: + with support.transient_internet("svn.python.org"): + # Same as test_connect, but with a separately created context + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + s.connect(("svn.python.org", 443)) + try: + self.assertEqual({}, s.getpeercert()) + finally: + s.close() + # This should fail because we have no verification certs + ctx.verify_mode = ssl.CERT_REQUIRED + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed", + s.connect, ("svn.python.org", 443)) s.close() + # This should succeed because we specify the root cert + ctx.load_verify_locations(SVN_PYTHON_ORG_ROOT_CERT) + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + s.connect(("svn.python.org", 443)) + try: + cert = s.getpeercert() + self.assertTrue(cert) + finally: + s.close() def test_connect_capath(self): # Verify server certificates using the `capath` argument @@ -369,104 +365,109 @@ # OpenSSL 0.9.8n and 1.0.0, as a result the capath directory must # contain both versions of each certificate (same content, different # filename) for this test to be portable across OpenSSL releases. - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx.verify_mode = ssl.CERT_REQUIRED - ctx.load_verify_locations(capath=CAPATH) - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - s.connect(("svn.python.org", 443)) - try: - cert = s.getpeercert() - self.assertTrue(cert) - finally: - s.close() - # Same with a bytes `capath` argument - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx.verify_mode = ssl.CERT_REQUIRED - ctx.load_verify_locations(capath=BYTES_CAPATH) - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - s.connect(("svn.python.org", 443)) - try: - cert = s.getpeercert() - self.assertTrue(cert) - finally: - s.close() + with support.transient_internet("svn.python.org"): + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=CAPATH) + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + s.connect(("svn.python.org", 443)) + try: + cert = s.getpeercert() + self.assertTrue(cert) + finally: + s.close() + # Same with a bytes `capath` argument + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=BYTES_CAPATH) + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + s.connect(("svn.python.org", 443)) + try: + cert = s.getpeercert() + self.assertTrue(cert) + finally: + s.close() @unittest.skipIf(os.name == "nt", "Can't use a socket as a file under Windows") def test_makefile_close(self): # Issue #5238: creating a file-like object with makefile() shouldn't # delay closing the underlying "real socket" (here tested with its # file descriptor, hence skipping the test under Windows). - ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) - ss.connect(("svn.python.org", 443)) - fd = ss.fileno() - f = ss.makefile() - f.close() - # The fd is still open - os.read(fd, 0) - # Closing the SSL socket should close the fd too - ss.close() - gc.collect() - with self.assertRaises(OSError) as e: + with support.transient_internet("svn.python.org"): + ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) + ss.connect(("svn.python.org", 443)) + fd = ss.fileno() + f = ss.makefile() + f.close() + # The fd is still open os.read(fd, 0) - self.assertEqual(e.exception.errno, errno.EBADF) + # Closing the SSL socket should close the fd too + ss.close() + gc.collect() + with self.assertRaises(OSError) as e: + os.read(fd, 0) + self.assertEqual(e.exception.errno, errno.EBADF) def test_non_blocking_handshake(self): - s = socket.socket(socket.AF_INET) - s.connect(("svn.python.org", 443)) - s.setblocking(False) - s = ssl.wrap_socket(s, - cert_reqs=ssl.CERT_NONE, - do_handshake_on_connect=False) - count = 0 - while True: - try: - count += 1 - s.do_handshake() - break - except ssl.SSLError as err: - if err.args[0] == ssl.SSL_ERROR_WANT_READ: - select.select([s], [], []) - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - select.select([], [s], []) - else: - raise - s.close() - if support.verbose: - sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count) + with support.transient_internet("svn.python.org"): + s = socket.socket(socket.AF_INET) + s.connect(("svn.python.org", 443)) + s.setblocking(False) + s = ssl.wrap_socket(s, + cert_reqs=ssl.CERT_NONE, + do_handshake_on_connect=False) + count = 0 + while True: + try: + count += 1 + s.do_handshake() + break + except ssl.SSLError as err: + if err.args[0] == ssl.SSL_ERROR_WANT_READ: + select.select([s], [], []) + elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: + select.select([], [s], []) + else: + raise + s.close() + if support.verbose: + sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count) def test_get_server_certificate(self): - pem = ssl.get_server_certificate(("svn.python.org", 443)) - if not pem: - self.fail("No server certificate on svn.python.org:443!") + with support.transient_internet("svn.python.org"): + pem = ssl.get_server_certificate(("svn.python.org", 443)) + if not pem: + self.fail("No server certificate on svn.python.org:443!") - try: - pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE) - except ssl.SSLError as x: - #should fail - if support.verbose: - sys.stdout.write("%s\n" % x) - else: - self.fail("Got server certificate %s for svn.python.org!" % pem) + try: + pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE) + except ssl.SSLError as x: + #should fail + if support.verbose: + sys.stdout.write("%s\n" % x) + else: + self.fail("Got server certificate %s for svn.python.org!" % pem) - pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT) - if not pem: - self.fail("No server certificate on svn.python.org:443!") - if support.verbose: - sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem) + pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT) + if not pem: + self.fail("No server certificate on svn.python.org:443!") + if support.verbose: + sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem) def test_ciphers(self): remote = ("svn.python.org", 443) - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE, ciphers="ALL") - s.connect(remote) - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT") - s.connect(remote) - # Error checking can happen at instantiation or when connecting - with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"): + with support.transient_internet(remote[0]): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE, ciphers="ALL") + s.connect(remote) s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx") + cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT") s.connect(remote) + # Error checking can happen at instantiation or when connecting + with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx") + s.connect(remote) def test_algorithms(self): # Issue #8484: all algorithms should be available when verifying a From python-checkins at python.org Thu Sep 9 15:33:34 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 9 Sep 2010 15:33:34 +0200 (CEST) Subject: [Python-checkins] r84651 - in python/branches/release31-maint: Lib/test/test_ssl.py Message-ID: <20100909133334.10077EE990@mail.python.org> Author: antoine.pitrou Date: Thu Sep 9 15:33:33 2010 New Revision: 84651 Log: Merged revisions 84650 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84650 | antoine.pitrou | 2010-09-09 15:31:46 +0200 (jeu., 09 sept. 2010) | 4 lines Use transient_internet() where appropriate in test_ssl (svn.python.org is sometimes unavailable) ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_ssl.py Modified: python/branches/release31-maint/Lib/test/test_ssl.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_ssl.py (original) +++ python/branches/release31-maint/Lib/test/test_ssl.py Thu Sep 9 15:33:33 2010 @@ -103,106 +103,104 @@ class NetworkedTests(unittest.TestCase): - def setUp(self): - self.old_timeout = socket.getdefaulttimeout() - socket.setdefaulttimeout(30) - - def tearDown(self): - socket.setdefaulttimeout(self.old_timeout) def test_connect(self): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE) - s.connect(("svn.python.org", 443)) - c = s.getpeercert() - if c: - self.fail("Peer cert %s shouldn't be here!") - s.close() - - # this should fail because we have no verification certs - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED) - try: + with support.transient_internet("svn.python.org"): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE) s.connect(("svn.python.org", 443)) - except ssl.SSLError: - pass - finally: + c = s.getpeercert() + if c: + self.fail("Peer cert %s shouldn't be here!") s.close() - # this should succeed because we specify the root cert - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=SVN_PYTHON_ORG_ROOT_CERT) - try: - s.connect(("svn.python.org", 443)) - finally: - s.close() + # this should fail because we have no verification certs + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED) + try: + s.connect(("svn.python.org", 443)) + except ssl.SSLError: + pass + finally: + s.close() + + # this should succeed because we specify the root cert + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=SVN_PYTHON_ORG_ROOT_CERT) + try: + s.connect(("svn.python.org", 443)) + finally: + s.close() @unittest.skipIf(os.name == "nt", "Can't use a socket as a file under Windows") def test_makefile_close(self): # Issue #5238: creating a file-like object with makefile() shouldn't # delay closing the underlying "real socket" (here tested with its # file descriptor, hence skipping the test under Windows). - ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) - ss.connect(("svn.python.org", 443)) - fd = ss.fileno() - f = ss.makefile() - f.close() - # The fd is still open - os.read(fd, 0) - # Closing the SSL socket should close the fd too - ss.close() - gc.collect() - try: + with support.transient_internet("svn.python.org"): + ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) + ss.connect(("svn.python.org", 443)) + fd = ss.fileno() + f = ss.makefile() + f.close() + # The fd is still open os.read(fd, 0) - except OSError as e: - self.assertEqual(e.errno, errno.EBADF) - else: - self.fail("OSError wasn't raised") + # Closing the SSL socket should close the fd too + ss.close() + gc.collect() + try: + os.read(fd, 0) + except OSError as e: + self.assertEqual(e.errno, errno.EBADF) + else: + self.fail("OSError wasn't raised") def test_non_blocking_handshake(self): - s = socket.socket(socket.AF_INET) - s.connect(("svn.python.org", 443)) - s.setblocking(False) - s = ssl.wrap_socket(s, - cert_reqs=ssl.CERT_NONE, - do_handshake_on_connect=False) - count = 0 - while True: - try: - count += 1 - s.do_handshake() - break - except ssl.SSLError as err: - if err.args[0] == ssl.SSL_ERROR_WANT_READ: - select.select([s], [], []) - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - select.select([], [s], []) - else: - raise - s.close() - if support.verbose: - sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count) + with support.transient_internet("svn.python.org"): + s = socket.socket(socket.AF_INET) + s.connect(("svn.python.org", 443)) + s.setblocking(False) + s = ssl.wrap_socket(s, + cert_reqs=ssl.CERT_NONE, + do_handshake_on_connect=False) + count = 0 + while True: + try: + count += 1 + s.do_handshake() + break + except ssl.SSLError as err: + if err.args[0] == ssl.SSL_ERROR_WANT_READ: + select.select([s], [], []) + elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: + select.select([], [s], []) + else: + raise + s.close() + if support.verbose: + sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count) def test_get_server_certificate(self): - pem = ssl.get_server_certificate(("svn.python.org", 443)) - if not pem: - self.fail("No server certificate on svn.python.org:443!") + with support.transient_internet("svn.python.org"): + pem = ssl.get_server_certificate(("svn.python.org", 443)) + if not pem: + self.fail("No server certificate on svn.python.org:443!") - try: - pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE) - except ssl.SSLError as x: - #should fail - if support.verbose: - sys.stdout.write("%s\n" % x) - else: - self.fail("Got server certificate %s for svn.python.org!" % pem) + try: + pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE) + except ssl.SSLError as x: + #should fail + if support.verbose: + sys.stdout.write("%s\n" % x) + else: + self.fail("Got server certificate %s for svn.python.org!" % pem) - pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT) - if not pem: - self.fail("No server certificate on svn.python.org:443!") - if support.verbose: - sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem) + pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT) + if not pem: + self.fail("No server certificate on svn.python.org:443!") + if support.verbose: + sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem) # Test disabled: OPENSSL_VERSION* not available in Python 3.1 def test_algorithms(self): From python-checkins at python.org Thu Sep 9 15:35:44 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 9 Sep 2010 15:35:44 +0200 (CEST) Subject: [Python-checkins] r84652 - in python/branches/release27-maint: Lib/test/test_ssl.py Message-ID: <20100909133544.7F983EE990@mail.python.org> Author: antoine.pitrou Date: Thu Sep 9 15:35:44 2010 New Revision: 84652 Log: Merged revisions 84650 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84650 | antoine.pitrou | 2010-09-09 15:31:46 +0200 (jeu., 09 sept. 2010) | 4 lines Use transient_internet() where appropriate in test_ssl (svn.python.org is sometimes unavailable) ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_ssl.py Modified: python/branches/release27-maint/Lib/test/test_ssl.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_ssl.py (original) +++ python/branches/release27-maint/Lib/test/test_ssl.py Thu Sep 9 15:35:44 2010 @@ -181,102 +181,100 @@ class NetworkedTests(unittest.TestCase): - def setUp(self): - self.old_timeout = socket.getdefaulttimeout() - socket.setdefaulttimeout(30) - - def tearDown(self): - socket.setdefaulttimeout(self.old_timeout) def test_connect(self): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE) - s.connect(("svn.python.org", 443)) - c = s.getpeercert() - if c: - self.fail("Peer cert %s shouldn't be here!") - s.close() - - # this should fail because we have no verification certs - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED) - try: + with test_support.transient_internet("svn.python.org"): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE) s.connect(("svn.python.org", 443)) - except ssl.SSLError: - pass - finally: + c = s.getpeercert() + if c: + self.fail("Peer cert %s shouldn't be here!") s.close() - # this should succeed because we specify the root cert - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=SVN_PYTHON_ORG_ROOT_CERT) - try: - s.connect(("svn.python.org", 443)) - finally: - s.close() + # this should fail because we have no verification certs + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED) + try: + s.connect(("svn.python.org", 443)) + except ssl.SSLError: + pass + finally: + s.close() + + # this should succeed because we specify the root cert + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=SVN_PYTHON_ORG_ROOT_CERT) + try: + s.connect(("svn.python.org", 443)) + finally: + s.close() @unittest.skipIf(os.name == "nt", "Can't use a socket as a file under Windows") def test_makefile_close(self): # Issue #5238: creating a file-like object with makefile() shouldn't # delay closing the underlying "real socket" (here tested with its # file descriptor, hence skipping the test under Windows). - ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) - ss.connect(("svn.python.org", 443)) - fd = ss.fileno() - f = ss.makefile() - f.close() - # The fd is still open - os.read(fd, 0) - # Closing the SSL socket should close the fd too - ss.close() - gc.collect() - with self.assertRaises(OSError) as e: + with test_support.transient_internet("svn.python.org"): + ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) + ss.connect(("svn.python.org", 443)) + fd = ss.fileno() + f = ss.makefile() + f.close() + # The fd is still open os.read(fd, 0) - self.assertEqual(e.exception.errno, errno.EBADF) + # Closing the SSL socket should close the fd too + ss.close() + gc.collect() + with self.assertRaises(OSError) as e: + os.read(fd, 0) + self.assertEqual(e.exception.errno, errno.EBADF) def test_non_blocking_handshake(self): - s = socket.socket(socket.AF_INET) - s.connect(("svn.python.org", 443)) - s.setblocking(False) - s = ssl.wrap_socket(s, - cert_reqs=ssl.CERT_NONE, - do_handshake_on_connect=False) - count = 0 - while True: - try: - count += 1 - s.do_handshake() - break - except ssl.SSLError, err: - if err.args[0] == ssl.SSL_ERROR_WANT_READ: - select.select([s], [], []) - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - select.select([], [s], []) - else: - raise - s.close() - if test_support.verbose: - sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count) + with test_support.transient_internet("svn.python.org"): + s = socket.socket(socket.AF_INET) + s.connect(("svn.python.org", 443)) + s.setblocking(False) + s = ssl.wrap_socket(s, + cert_reqs=ssl.CERT_NONE, + do_handshake_on_connect=False) + count = 0 + while True: + try: + count += 1 + s.do_handshake() + break + except ssl.SSLError, err: + if err.args[0] == ssl.SSL_ERROR_WANT_READ: + select.select([s], [], []) + elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: + select.select([], [s], []) + else: + raise + s.close() + if test_support.verbose: + sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count) def test_get_server_certificate(self): - pem = ssl.get_server_certificate(("svn.python.org", 443)) - if not pem: - self.fail("No server certificate on svn.python.org:443!") + with test_support.transient_internet("svn.python.org"): + pem = ssl.get_server_certificate(("svn.python.org", 443)) + if not pem: + self.fail("No server certificate on svn.python.org:443!") - try: - pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE) - except ssl.SSLError: - #should fail - pass - else: - self.fail("Got server certificate %s for svn.python.org!" % pem) + try: + pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE) + except ssl.SSLError: + #should fail + pass + else: + self.fail("Got server certificate %s for svn.python.org!" % pem) - pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT) - if not pem: - self.fail("No server certificate on svn.python.org:443!") - if test_support.verbose: - sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem) + pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT) + if not pem: + self.fail("No server certificate on svn.python.org:443!") + if test_support.verbose: + sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem) def test_algorithms(self): # Issue #8484: all algorithms should be available when verifying a From python-checkins at python.org Thu Sep 9 20:33:26 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 9 Sep 2010 20:33:26 +0200 (CEST) Subject: [Python-checkins] r84653 - in python/branches/py3k: Lib/pickle.py Lib/test/pickletester.py Lib/test/test_pickle.py Misc/NEWS Modules/_pickle.c Message-ID: <20100909183326.B1243FD55@mail.python.org> Author: antoine.pitrou Date: Thu Sep 9 20:33:21 2010 New Revision: 84653 Log: Issue #9410: Various optimizations to the pickle module, leading to speedups up to 4x (depending on the benchmark). Mostly ported from Unladen Swallow; initial patch by Alexandre Vassalotti. Modified: python/branches/py3k/Lib/pickle.py python/branches/py3k/Lib/test/pickletester.py python/branches/py3k/Lib/test/test_pickle.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/_pickle.c Modified: python/branches/py3k/Lib/pickle.py ============================================================================== --- python/branches/py3k/Lib/pickle.py (original) +++ python/branches/py3k/Lib/pickle.py Thu Sep 9 20:33:21 2010 @@ -1287,12 +1287,6 @@ """ return int.from_bytes(data, byteorder='little', signed=True) -# Use the faster _pickle if possible -try: - from _pickle import * -except ImportError: - Pickler, Unpickler = _Pickler, _Unpickler - # Shorthands def dump(obj, file, protocol=None, *, fix_imports=True): @@ -1316,6 +1310,12 @@ return Unpickler(file, fix_imports=fix_imports, encoding=encoding, errors=errors).load() +# Use the faster _pickle if possible +try: + from _pickle import * +except ImportError: + Pickler, Unpickler = _Pickler, _Unpickler + # Doctest def _test(): import doctest Modified: python/branches/py3k/Lib/test/pickletester.py ============================================================================== --- python/branches/py3k/Lib/test/pickletester.py (original) +++ python/branches/py3k/Lib/test/pickletester.py Thu Sep 9 20:33:21 2010 @@ -1068,6 +1068,15 @@ dumped = self.dumps(set([3]), 2) self.assertEqual(dumped, DATA6) + def test_large_pickles(self): + # Test the correctness of internal buffering routines when handling + # large data. + for proto in protocols: + data = (1, b'x' * (256 * 1024)) + dumped = self.dumps(data, proto) + loaded = self.loads(dumped) + self.assertEqual(loaded, data) + # Test classes for reduce_ex Modified: python/branches/py3k/Lib/test/test_pickle.py ============================================================================== --- python/branches/py3k/Lib/test/test_pickle.py (original) +++ python/branches/py3k/Lib/test/test_pickle.py Thu Sep 9 20:33:21 2010 @@ -37,6 +37,18 @@ return u.load() +class InMemoryPickleTests(AbstractPickleTests): + + pickler = pickle._Pickler + unpickler = pickle._Unpickler + + def dumps(self, arg, proto=None): + return pickle.dumps(arg, proto) + + def loads(self, buf): + return pickle.loads(buf) + + class PyPersPicklerTests(AbstractPersistentPicklerTests): pickler = pickle._Pickler @@ -95,7 +107,8 @@ tests.extend([CPicklerTests, CPersPicklerTests, CDumpPickle_LoadPickle, DumpPickle_CLoadPickle, PyPicklerUnpicklerObjectTests, - CPicklerUnpicklerObjectTests]) + CPicklerUnpicklerObjectTests, + InMemoryPickleTests]) support.run_unittest(*tests) support.run_doctest(pickle) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Sep 9 20:33:21 2010 @@ -20,6 +20,10 @@ Library ------- +- Issue #9410: Various optimizations to the pickle module, leading to + speedups up to 4x (depending on the benchmark). Mostly ported from + Unladen Swallow; initial patch by Alexandre Vassalotti. + - The pprint module now supports printing OrderedDicts in their given order (formerly, it would sort the keys). Modified: python/branches/py3k/Modules/_pickle.c ============================================================================== --- python/branches/py3k/Modules/_pickle.c (original) +++ python/branches/py3k/Modules/_pickle.c Thu Sep 9 20:33:21 2010 @@ -10,7 +10,6 @@ DEFAULT_PROTOCOL = 3 }; - /* Pickle opcodes. These must be kept updated with pickle.py. Extensive docs are in pickletools.py. */ enum opcode { @@ -96,9 +95,12 @@ checking for self-referential data-structures. */ FAST_NESTING_LIMIT = 50, - /* Size of the write buffer of Pickler. Higher values will reduce the - number of calls to the write() method of the output stream. */ - WRITE_BUF_SIZE = 256, + /* Initial size of the write buffer of Pickler. */ + WRITE_BUF_SIZE = 4096, + + /* Maximum size of the write buffer of Pickler when pickling to a + stream. This is ignored for in-memory pickling. */ + MAX_WRITE_BUF_SIZE = 64 * 1024, }; /* Exception classes for pickle. These should override the ones defined in @@ -140,23 +142,19 @@ /* Internal data type used as the unpickling stack. */ typedef struct { - PyObject_HEAD - int length; /* number of initial slots in data currently used */ - int size; /* number of slots in data allocated */ + PyObject_VAR_HEAD PyObject **data; + Py_ssize_t allocated; /* number of slots in data allocated */ } Pdata; static void Pdata_dealloc(Pdata *self) { - int i; - PyObject **p; - - for (i = self->length, p = self->data; --i >= 0; p++) { - Py_DECREF(*p); + int i = Py_SIZE(self); + while (--i >= 0) { + Py_DECREF(self->data[i]); } - if (self->data) - PyMem_Free(self->data); + PyMem_FREE(self->data); PyObject_Del(self); } @@ -175,9 +173,9 @@ if (!(self = PyObject_New(Pdata, &Pdata_Type))) return NULL; - self->size = 8; - self->length = 0; - self->data = PyMem_Malloc(self->size * sizeof(PyObject *)); + Py_SIZE(self) = 0; + self->allocated = 8; + self->data = PyMem_MALLOC(self->allocated * sizeof(PyObject *)); if (self->data) return (PyObject *)self; Py_DECREF(self); @@ -191,42 +189,40 @@ static int Pdata_clear(Pdata *self, int clearto) { - int i; - PyObject **p; + int i = Py_SIZE(self); if (clearto < 0) return stack_underflow(); - if (clearto >= self->length) + if (clearto >= i) return 0; - for (i = self->length, p = self->data + clearto; --i >= clearto; p++) { - Py_CLEAR(*p); + while (--i >= clearto) { + Py_CLEAR(self->data[i]); } - self->length = clearto; - + Py_SIZE(self) = clearto; return 0; } static int Pdata_grow(Pdata *self) { - int bigger; - size_t nbytes; - PyObject **tmp; - - bigger = (self->size << 1) + 1; - if (bigger <= 0) /* was 0, or new value overflows */ + PyObject **data = self->data; + Py_ssize_t allocated = self->allocated; + Py_ssize_t new_allocated; + + new_allocated = (allocated >> 3) + 6; + /* check for integer overflow */ + if (new_allocated > PY_SSIZE_T_MAX - allocated) goto nomemory; - if ((int)(size_t)bigger != bigger) + new_allocated += allocated; + if (new_allocated > (PY_SSIZE_T_MAX / sizeof(PyObject *))) goto nomemory; - nbytes = (size_t)bigger * sizeof(PyObject *); - if (nbytes / sizeof(PyObject *) != (size_t)bigger) - goto nomemory; - tmp = PyMem_Realloc(self->data, nbytes); - if (tmp == NULL) + data = PyMem_REALLOC(data, new_allocated * sizeof(PyObject *)); + if (data == NULL) goto nomemory; - self->data = tmp; - self->size = bigger; + + self->data = data; + self->allocated = new_allocated; return 0; nomemory: @@ -241,21 +237,21 @@ static PyObject * Pdata_pop(Pdata *self) { - if (self->length == 0) { + if (Py_SIZE(self) == 0) { PyErr_SetString(UnpicklingError, "bad pickle data"); return NULL; } - return self->data[--(self->length)]; + return self->data[--Py_SIZE(self)]; } #define PDATA_POP(D, V) do { (V) = Pdata_pop((D)); } while (0) static int Pdata_push(Pdata *self, PyObject *obj) { - if (self->length == self->size && Pdata_grow(self) < 0) { + if (Py_SIZE(self) == self->allocated && Pdata_grow(self) < 0) { return -1; } - self->data[self->length++] = obj; + self->data[Py_SIZE(self)++] = obj; return 0; } @@ -274,14 +270,14 @@ PyObject *tuple; Py_ssize_t len, i, j; - len = self->length - start; + len = Py_SIZE(self) - start; tuple = PyTuple_New(len); if (tuple == NULL) return NULL; for (i = start, j = 0; j < len; i++, j++) PyTuple_SET_ITEM(tuple, j, self->data[i]); - self->length = start; + Py_SIZE(self) = start; return tuple; } @@ -291,31 +287,45 @@ PyObject *list; Py_ssize_t len, i, j; - len = self->length - start; + len = Py_SIZE(self) - start; list = PyList_New(len); if (list == NULL) return NULL; for (i = start, j = 0; j < len; i++, j++) PyList_SET_ITEM(list, j, self->data[i]); - self->length = start; + Py_SIZE(self) = start; return list; } +typedef struct { + PyObject *me_key; + long me_value; +} PyMemoEntry; + +typedef struct { + Py_ssize_t mt_mask; + Py_ssize_t mt_used; + Py_ssize_t mt_allocated; + PyMemoEntry *mt_table; +} PyMemoTable; + typedef struct PicklerObject { PyObject_HEAD - PyObject *write; /* write() method of the output stream */ - PyObject *memo; /* Memo dictionary, keep track of the seen + PyMemoTable *memo; /* Memo table, keep track of the seen objects to support self-referential objects - pickling. */ + pickling. */ PyObject *pers_func; /* persistent_id() method, can be NULL */ PyObject *arg; + + PyObject *write; /* write() method of the output stream. */ + PyObject *output_buffer; /* Write into a local bytearray buffer before + flushing to the stream. */ + Py_ssize_t output_len; /* Length of output_buffer. */ + Py_ssize_t max_output_len; /* Allocation size of output_buffer. */ int proto; /* Pickle protocol number, >= 0 */ int bin; /* Boolean, true if proto > 0 */ int buf_size; /* Size of the current buffered pickle data */ - char *write_buf; /* Write buffer, this is to avoid calling the - write() method of the output stream too - often. */ int fast; /* Enable fast mode if set to a true value. The fast mode disable the usage of memo, therefore speeding the pickling process by @@ -331,15 +341,23 @@ typedef struct UnpicklerObject { PyObject_HEAD Pdata *stack; /* Pickle data stack, store unpickled objects. */ - PyObject *readline; /* readline() method of the output stream */ - PyObject *read; /* read() method of the output stream */ - PyObject *memo; /* Memo dictionary, provide the objects stored - using the PUT opcodes. */ + + /* The unpickler memo is just an array of PyObject *s. Using a dict + is unnecessary, since the keys are contiguous ints. */ + PyObject **memo; + Py_ssize_t memo_size; + PyObject *arg; PyObject *pers_func; /* persistent_load() method, can be NULL. */ - PyObject *last_string; /* Reference to the last string read by the - readline() method. */ - char *buffer; /* Reading buffer. */ + + Py_buffer buffer; + char *input_buffer; + char *input_line; + Py_ssize_t input_len; + Py_ssize_t next_read_idx; + PyObject *read; /* read() method of the input stream. */ + PyObject *readline; /* readline() method of the input stream. */ + char *encoding; /* Name of the encoding to be used for decoding strings pickled using Python 2.x. The default value is "ASCII" */ @@ -362,9 +380,230 @@ static PyTypeObject Unpickler_Type; +/************************************************************************* + A custom hashtable mapping void* to longs. This is used by the pickler for + memoization. Using a custom hashtable rather than PyDict allows us to skip + a bunch of unnecessary object creation. This makes a huge performance + difference. */ + +#define MT_MINSIZE 8 +#define PERTURB_SHIFT 5 + + +static PyMemoTable * +PyMemoTable_New(void) +{ + PyMemoTable *memo = PyMem_MALLOC(sizeof(PyMemoTable)); + if (memo == NULL) { + PyErr_NoMemory(); + return NULL; + } + + memo->mt_used = 0; + memo->mt_allocated = MT_MINSIZE; + memo->mt_mask = MT_MINSIZE - 1; + memo->mt_table = PyMem_MALLOC(MT_MINSIZE * sizeof(PyMemoEntry)); + if (memo->mt_table == NULL) { + PyMem_FREE(memo); + PyErr_NoMemory(); + return NULL; + } + memset(memo->mt_table, 0, MT_MINSIZE * sizeof(PyMemoEntry)); + + return memo; +} + +static PyMemoTable * +PyMemoTable_Copy(PyMemoTable *self) +{ + Py_ssize_t i; + PyMemoTable *new = PyMemoTable_New(); + if (new == NULL) + return NULL; + + new->mt_used = self->mt_used; + new->mt_allocated = self->mt_allocated; + new->mt_mask = self->mt_mask; + /* The table we get from _New() is probably smaller than we wanted. + Free it and allocate one that's the right size. */ + PyMem_FREE(new->mt_table); + new->mt_table = PyMem_MALLOC(self->mt_allocated * sizeof(PyMemoEntry)); + if (new->mt_table == NULL) { + PyMem_FREE(new); + return NULL; + } + for (i = 0; i < self->mt_allocated; i++) { + Py_XINCREF(self->mt_table[i].me_key); + } + memcpy(new->mt_table, self->mt_table, + sizeof(PyMemoEntry) * self->mt_allocated); + + return new; +} + +static Py_ssize_t +PyMemoTable_Size(PyMemoTable *self) +{ + return self->mt_used; +} + +static int +PyMemoTable_Clear(PyMemoTable *self) +{ + Py_ssize_t i = self->mt_allocated; + + while (--i >= 0) { + Py_XDECREF(self->mt_table[i].me_key); + } + self->mt_used = 0; + memset(self->mt_table, 0, self->mt_allocated * sizeof(PyMemoEntry)); + return 0; +} + +static void +PyMemoTable_Del(PyMemoTable *self) +{ + if (self == NULL) + return; + PyMemoTable_Clear(self); + + PyMem_FREE(self->mt_table); + PyMem_FREE(self); +} + +/* Since entries cannot be deleted from this hashtable, _PyMemoTable_Lookup() + can be considerably simpler than dictobject.c's lookdict(). */ +static PyMemoEntry * +_PyMemoTable_Lookup(PyMemoTable *self, PyObject *key) +{ + size_t i; + size_t perturb; + size_t mask = (size_t)self->mt_mask; + PyMemoEntry *table = self->mt_table; + PyMemoEntry *entry; + long hash = (long)key >> 3; + + i = hash & mask; + entry = &table[i]; + if (entry->me_key == NULL || entry->me_key == key) + return entry; + + for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + entry = &table[i & mask]; + if (entry->me_key == NULL || entry->me_key == key) + return entry; + } + assert(0); /* Never reached */ + return NULL; +} + +/* Returns -1 on failure, 0 on success. */ +static int +_PyMemoTable_ResizeTable(PyMemoTable *self, Py_ssize_t min_size) +{ + PyMemoEntry *oldtable = NULL; + PyMemoEntry *oldentry, *newentry; + Py_ssize_t new_size = MT_MINSIZE; + Py_ssize_t to_process; + + assert(min_size > 0); + + /* Find the smallest valid table size >= min_size. */ + while (new_size < min_size && new_size > 0) + new_size <<= 1; + if (new_size <= 0) { + PyErr_NoMemory(); + return -1; + } + /* new_size needs to be a power of two. */ + assert((new_size & (new_size - 1)) == 0); + + /* Allocate new table. */ + oldtable = self->mt_table; + self->mt_table = PyMem_MALLOC(new_size * sizeof(PyMemoEntry)); + if (self->mt_table == NULL) { + PyMem_FREE(oldtable); + PyErr_NoMemory(); + return -1; + } + self->mt_allocated = new_size; + self->mt_mask = new_size - 1; + memset(self->mt_table, 0, sizeof(PyMemoEntry) * new_size); + + /* Copy entries from the old table. */ + to_process = self->mt_used; + for (oldentry = oldtable; to_process > 0; oldentry++) { + if (oldentry->me_key != NULL) { + to_process--; + /* newentry is a pointer to a chunk of the new + mt_table, so we're setting the key:value pair + in-place. */ + newentry = _PyMemoTable_Lookup(self, oldentry->me_key); + newentry->me_key = oldentry->me_key; + newentry->me_value = oldentry->me_value; + } + } + + /* Deallocate the old table. */ + PyMem_FREE(oldtable); + return 0; +} + +/* Returns NULL on failure, a pointer to the value otherwise. */ +static long * +PyMemoTable_Get(PyMemoTable *self, PyObject *key) +{ + PyMemoEntry *entry = _PyMemoTable_Lookup(self, key); + if (entry->me_key == NULL) + return NULL; + return &entry->me_value; +} + +/* Returns -1 on failure, 0 on success. */ +static int +PyMemoTable_Set(PyMemoTable *self, PyObject *key, long value) +{ + PyMemoEntry *entry; + + assert(key != NULL); + + entry = _PyMemoTable_Lookup(self, key); + if (entry->me_key != NULL) { + entry->me_value = value; + return 0; + } + Py_INCREF(key); + entry->me_key = key; + entry->me_value = value; + self->mt_used++; + + /* If we added a key, we can safely resize. Otherwise just return! + * If used >= 2/3 size, adjust size. Normally, this quaduples the size. + * + * Quadrupling the size improves average table sparseness + * (reducing collisions) at the cost of some memory. It also halves + * the number of expensive resize operations in a growing memo table. + * + * Very large memo tables (over 50K items) use doubling instead. + * This may help applications with severe memory constraints. + */ + if (!(self->mt_used * 3 >= (self->mt_mask + 1) * 2)) + return 0; + return _PyMemoTable_ResizeTable(self, + (self->mt_used > 50000 ? 2 : 4) * self->mt_used); +} + +#undef MT_MINSIZE +#undef PERTURB_SHIFT + +/*************************************************************************/ + /* Helpers for creating the argument tuple passed to functions. This has the - performance advantage of calling PyTuple_New() only once. */ + performance advantage of calling PyTuple_New() only once. + XXX(avassalotti): Inline directly in _Pickler_FastCall() and + _Unpickler_FastCall(). */ #define ARG_TUP(self, obj) do { \ if ((self)->arg || ((self)->arg=PyTuple_New(1))) { \ Py_XDECREF(PyTuple_GET_ITEM((self)->arg, 0)); \ @@ -401,10 +640,9 @@ XXX: And, what is the reference behavior of these? Steal, borrow? At first glance, it seems to steal the reference of 'arg' and borrow the reference - of 'func'. - */ + of 'func'. */ static PyObject * -pickler_call(PicklerObject *self, PyObject *func, PyObject *arg) +_Pickler_FastCall(PicklerObject *self, PyObject *func, PyObject *arg) { PyObject *result = NULL; @@ -416,181 +654,541 @@ return result; } +static int +_Pickler_ClearBuffer(PicklerObject *self) +{ + Py_CLEAR(self->output_buffer); + self->output_buffer = + PyBytes_FromStringAndSize(NULL, self->max_output_len); + if (self->output_buffer == NULL) + return -1; + self->output_len = 0; + return 0; +} + static PyObject * -unpickler_call(UnpicklerObject *self, PyObject *func, PyObject *arg) +_Pickler_GetString(PicklerObject *self) { - PyObject *result = NULL; + PyObject *output_buffer = self->output_buffer; - ARG_TUP(self, arg); - if (self->arg) { - result = PyObject_Call(func, self->arg, NULL); - FREE_ARG_TUP(self); - } - return result; + assert(self->output_buffer != NULL); + self->output_buffer = NULL; + /* Resize down to exact size */ + if (_PyBytes_Resize(&output_buffer, self->output_len) < 0) + return NULL; + return output_buffer; } -static Py_ssize_t -pickler_write(PicklerObject *self, const char *s, Py_ssize_t n) +static int +_Pickler_FlushToFile(PicklerObject *self) { - PyObject *data, *result; + PyObject *output, *result; + + assert(self->write != NULL); - if (self->write_buf == NULL) { - PyErr_SetString(PyExc_SystemError, "invalid write buffer"); + output = _Pickler_GetString(self); + if (output == NULL) return -1; - } - if (s == NULL) { - if (!(self->buf_size)) - return 0; - data = PyBytes_FromStringAndSize(self->write_buf, self->buf_size); - if (data == NULL) - return -1; - } - else { - if (self->buf_size && (n + self->buf_size) > WRITE_BUF_SIZE) { - if (pickler_write(self, NULL, 0) < 0) + result = _Pickler_FastCall(self, self->write, output); + Py_XDECREF(result); + return (result == NULL) ? -1 : 0; +} + +static int +_Pickler_Write(PicklerObject *self, const char *s, Py_ssize_t n) +{ + Py_ssize_t i, required; + char *buffer; + + assert(s != NULL); + + required = self->output_len + n; + if (required > self->max_output_len) { + if (self->write != NULL && required > MAX_WRITE_BUF_SIZE) { + /* XXX This reallocates a new buffer every time, which is a bit + wasteful. */ + if (_Pickler_FlushToFile(self) < 0) + return -1; + if (_Pickler_ClearBuffer(self) < 0) return -1; } - - if (n > WRITE_BUF_SIZE) { - if (!(data = PyBytes_FromStringAndSize(s, n))) + if (self->write != NULL && n > MAX_WRITE_BUF_SIZE) { + /* we already flushed above, so the buffer is empty */ + PyObject *result; + /* XXX we could spare an intermediate copy and pass + a memoryview instead */ + PyObject *output = PyBytes_FromStringAndSize(s, n); + if (s == NULL) return -1; + result = _Pickler_FastCall(self, self->write, output); + Py_XDECREF(result); + return (result == NULL) ? -1 : 0; } else { - memcpy(self->write_buf + self->buf_size, s, n); - self->buf_size += n; - return n; + if (self->output_len >= PY_SSIZE_T_MAX / 2 - n) { + PyErr_NoMemory(); + return -1; + } + self->max_output_len = (self->output_len + n) * 2; + if (_PyBytes_Resize(&self->output_buffer, self->max_output_len) < 0) + return -1; } } + buffer = PyBytes_AS_STRING(self->output_buffer); + if (n < 8) { + /* This is faster than memcpy when the string is short. */ + for (i = 0; i < n; i++) { + buffer[self->output_len + i] = s[i]; + } + } + else { + memcpy(buffer + self->output_len, s, n); + } + self->output_len += n; + return n; +} + +static PicklerObject * +_Pickler_New(void) +{ + PicklerObject *self; + + self = PyObject_GC_New(PicklerObject, &Pickler_Type); + if (self == NULL) + return NULL; + + self->pers_func = NULL; + self->arg = NULL; + self->write = NULL; + self->proto = 0; + self->bin = 0; + self->fast = 0; + self->fast_nesting = 0; + self->fix_imports = 0; + self->fast_memo = NULL; + + self->memo = PyMemoTable_New(); + if (self->memo == NULL) { + Py_DECREF(self); + return NULL; + } + self->max_output_len = WRITE_BUF_SIZE; + self->output_len = 0; + self->output_buffer = PyBytes_FromStringAndSize(NULL, + self->max_output_len); + if (self->output_buffer == NULL) { + Py_DECREF(self); + return NULL; + } + return self; +} - /* object with write method */ - result = pickler_call(self, self->write, data); - if (result == NULL) +static int +_Pickler_SetProtocol(PicklerObject *self, PyObject *proto_obj, + PyObject *fix_imports_obj) +{ + long proto = 0; + int fix_imports; + + if (proto_obj == NULL || proto_obj == Py_None) + proto = DEFAULT_PROTOCOL; + else { + proto = PyLong_AsLong(proto_obj); + if (proto == -1 && PyErr_Occurred()) + return -1; + } + if (proto < 0) + proto = HIGHEST_PROTOCOL; + if (proto > HIGHEST_PROTOCOL) { + PyErr_Format(PyExc_ValueError, "pickle protocol must be <= %d", + HIGHEST_PROTOCOL); return -1; + } + fix_imports = PyObject_IsTrue(fix_imports_obj); + if (fix_imports == -1) + return -1; + + self->proto = proto; + self->bin = proto > 0; + self->fix_imports = fix_imports && proto < 3; - Py_DECREF(result); - self->buf_size = 0; - return n; + return 0; } -/* XXX: These read/readline functions ought to be optimized. Buffered I/O - might help a lot, especially with the new (but much slower) io library. - On the other hand, the added complexity might not worth it. - */ +/* Returns -1 (with an exception set) on failure, 0 on success. This may + be called once on a freshly created Pickler. */ +static int +_Pickler_SetOutputStream(PicklerObject *self, PyObject *file) +{ + assert(file != NULL); + self->write = PyObject_GetAttrString(file, "write"); + if (self->write == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_SetString(PyExc_TypeError, + "file must have a 'write' attribute"); + return -1; + } + + return 0; +} + +/* See documentation for _Pickler_FastCall(). */ +static PyObject * +_Unpickler_FastCall(UnpicklerObject *self, PyObject *func, PyObject *arg) +{ + PyObject *result = NULL; -/* Read at least n characters from the input stream and set s to the current - reading position. */ + ARG_TUP(self, arg); + if (self->arg) { + result = PyObject_Call(func, self->arg, NULL); + FREE_ARG_TUP(self); + } + return result; +} + +/* Returns the size of the input on success, -1 on failure. This takes its + own reference to `input`. */ +static Py_ssize_t +_Unpickler_SetStringInput(UnpicklerObject *self, PyObject *input) +{ + if (self->buffer.buf != NULL) + PyBuffer_Release(&self->buffer); + if (PyObject_GetBuffer(input, &self->buffer, PyBUF_CONTIG_RO) < 0) + return -1; + self->input_buffer = self->buffer.buf; + self->input_len = self->buffer.len; + self->next_read_idx = 0; + return self->input_len; +} + +static const Py_ssize_t READ_WHOLE_LINE = -1; + +/* If reading from a file, we need to only pull the bytes we need, since there + may be multiple pickle objects arranged contiguously in the same input + buffer. + + If `n` is READ_WHOLE_LINE, read a whole line. Otherwise, read up to `n` + bytes from the input stream/buffer. + + Update the unpickler's input buffer with the newly-read data. Returns -1 on + failure; on success, returns the number of bytes read from the file. + + On success, self->input_len will be 0; this is intentional so that when + unpickling from a file, the "we've run out of data" code paths will trigger, + causing the Unpickler to go back to the file for more data. Use the returned + size to tell you how much data you can process. */ static Py_ssize_t -unpickler_read(UnpicklerObject *self, char **s, Py_ssize_t n) +_Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) { - PyObject *len; PyObject *data; + Py_ssize_t read_size; - len = PyLong_FromSsize_t(n); - if (len == NULL) - return -1; + assert(self->read != NULL); + assert(self->next_read_idx == 0); + + if (n == READ_WHOLE_LINE) + data = PyObject_Call(self->readline, empty_tuple, NULL); + else { + PyObject *len = PyLong_FromSsize_t(n); + if (len == NULL) + return -1; + data = _Unpickler_FastCall(self, self->read, len); + } - data = unpickler_call(self, self->read, len); if (data == NULL) return -1; - /* XXX: Should bytearray be supported too? */ - if (!PyBytes_Check(data)) { - PyErr_SetString(PyExc_ValueError, - "read() from the underlying stream did not " - "return bytes"); - Py_DECREF(data); - return -1; + read_size = _Unpickler_SetStringInput(self, data); + self->input_len = 0; + Py_DECREF(data); + return read_size; +} + +/* Read `n` bytes from the unpickler's data source, storing the result in `*s`. + + This should be used for all data reads, rather than accessing the unpickler's + input buffer directly. This method deals correctly with reading from input + streams, which the input buffer doesn't deal with. + + Note that when reading from a file-like object, self->next_read_idx won't + be updated (it should remain at 0 for the entire unpickling process). You + should use this function's return value to know how many bytes you can + consume. + + Returns -1 (with an exception set) on failure. On success, return the + number of chars read. */ +static Py_ssize_t +_Unpickler_Read(UnpicklerObject *self, char **s, Py_ssize_t n) +{ + if (n == 0) { + *s = NULL; + return 0; } - if (PyBytes_GET_SIZE(data) != n) { - PyErr_SetNone(PyExc_EOFError); - Py_DECREF(data); + /* This condition will always be true if self->read. */ + if (self->next_read_idx + n > self->input_len) { + if (self->read) { + Py_ssize_t num_read; + assert(self->next_read_idx == self->input_len); + num_read = _Unpickler_ReadFromFile(self, n); + if (n < 0) + return -1; + if (num_read == n) { + *s = self->input_buffer; + return num_read; + } + } + PyErr_Format(PyExc_EOFError, "Ran out of input"); return -1; } + assert(self->read == NULL); + *s = self->input_buffer + self->next_read_idx; + self->next_read_idx += n; + return n; +} - Py_XDECREF(self->last_string); - self->last_string = data; - - if (!(*s = PyBytes_AS_STRING(data))) +static Py_ssize_t +_Unpickler_CopyLine(UnpicklerObject *self, char *line, Py_ssize_t len, + char **result) +{ + char *input_line = PyMem_Realloc(self->input_line, len + 1); + if (input_line == NULL) return -1; - return n; + memcpy(input_line, line, len); + input_line[len] = '\0'; + self->input_line = input_line; + *result = self->input_line; + return len; } +/* Read a line from the input stream/buffer. If we run off the end of the input + before hitting \n, return the data we found. + + Returns the number of chars read, or -1 on failure. */ static Py_ssize_t -unpickler_readline(UnpicklerObject *self, char **s) +_Unpickler_Readline(UnpicklerObject *self, char **result) { - PyObject *data; - - data = PyObject_CallObject(self->readline, empty_tuple); - if (data == NULL) - return -1; + Py_ssize_t i, num_read; - /* XXX: Should bytearray be supported too? */ - if (!PyBytes_Check(data)) { - PyErr_SetString(PyExc_ValueError, - "readline() from the underlying stream did not " - "return bytes"); - return -1; + /* This loop will never be entered if self->read is not NULL. */ + for (i = self->next_read_idx; i < self->input_len; i++) { + assert(self->read == NULL); + if (self->input_buffer[i] == '\n') { + char *line_start = self->input_buffer + self->next_read_idx; + num_read = i - self->next_read_idx + 1; + self->next_read_idx = i + 1; + return _Unpickler_CopyLine(self, line_start, num_read, result); + } + } + if (self->read) { + assert(self->next_read_idx == self->input_len); + num_read = _Unpickler_ReadFromFile(self, READ_WHOLE_LINE); + if (num_read < 0) + return -1; + *result = self->input_buffer; + return num_read; } + + /* If we get here, we've run off the end of the input string. Return the + remaining string and let the caller figure it out. */ + *result = self->input_buffer + self->next_read_idx; + num_read = i - self->next_read_idx; + self->next_read_idx = i; + return num_read; +} + +/* Returns -1 (with an exception set) on failure, 0 on success. The memo array + will be modified in place. */ +static int +_Unpickler_ResizeMemoList(UnpicklerObject *self, Py_ssize_t new_size) +{ + Py_ssize_t i; + PyObject **memo; - Py_XDECREF(self->last_string); - self->last_string = data; + assert(new_size > self->memo_size); - if (!(*s = PyBytes_AS_STRING(data))) + memo = PyMem_REALLOC(self->memo, new_size * sizeof(PyObject *)); + if (memo == NULL) { + PyErr_NoMemory(); return -1; + } + self->memo = memo; + for (i = self->memo_size; i < new_size; i++) + self->memo[i] = NULL; + self->memo_size = new_size; + return 0; +} - return PyBytes_GET_SIZE(data); +/* Returns NULL if idx is out of bounds. */ +static PyObject * +_Unpickler_MemoGet(UnpicklerObject *self, Py_ssize_t idx) +{ + if (idx < 0 || idx >= self->memo_size) + return NULL; + + return self->memo[idx]; } -/* Generate a GET opcode for an object stored in the memo. The 'key' argument - should be the address of the object as returned by PyLong_FromVoidPtr(). */ +/* Returns -1 (with an exception set) on failure, 0 on success. + This takes its own reference to `value`. */ static int -memo_get(PicklerObject *self, PyObject *key) +_Unpickler_MemoPut(UnpicklerObject *self, Py_ssize_t idx, PyObject *value) { - PyObject *value; - PyObject *memo_id; - long x; - char pdata[30]; - int len; + PyObject *old_item; - value = PyDict_GetItemWithError(self->memo, key); - if (value == NULL) { - if (!PyErr_Occurred()) - PyErr_SetObject(PyExc_KeyError, key); + if (idx >= self->memo_size) { + if (_Unpickler_ResizeMemoList(self, idx * 2) < 0) + return -1; + assert(idx < self->memo_size); + } + Py_INCREF(value); + old_item = self->memo[idx]; + self->memo[idx] = value; + Py_XDECREF(old_item); + return 0; +} + +static PyObject ** +_Unpickler_NewMemo(Py_ssize_t new_size) +{ + PyObject **memo = PyMem_MALLOC(new_size * sizeof(PyObject *)); + if (memo == NULL) + return NULL; + memset(memo, 0, new_size * sizeof(PyObject *)); + return memo; +} + +/* Free the unpickler's memo, taking care to decref any items left in it. */ +static void +_Unpickler_MemoCleanup(UnpicklerObject *self) +{ + Py_ssize_t i; + PyObject **memo = self->memo; + + if (self->memo == NULL) + return; + self->memo = NULL; + i = self->memo_size; + while (--i >= 0) { + Py_XDECREF(memo[i]); + } + PyMem_FREE(memo); +} + +static UnpicklerObject * +_Unpickler_New(void) +{ + UnpicklerObject *self; + + self = PyObject_GC_New(UnpicklerObject, &Unpickler_Type); + if (self == NULL) + return NULL; + + self->stack = (Pdata *)Pdata_New(); + if (self->stack == NULL) { + Py_DECREF(self); + return NULL; + } + memset(&self->buffer, 0, sizeof(Py_buffer)); + + self->memo_size = 32; + self->memo = _Unpickler_NewMemo(self->memo_size); + if (self->memo == NULL) { + Py_DECREF(self); + return NULL; + } + + self->arg = NULL; + self->pers_func = NULL; + self->input_buffer = NULL; + self->input_line = NULL; + self->input_len = 0; + self->next_read_idx = 0; + self->read = NULL; + self->readline = NULL; + self->encoding = NULL; + self->errors = NULL; + self->marks = NULL; + self->num_marks = 0; + self->marks_size = 0; + self->proto = 0; + self->fix_imports = 0; + + return self; +} + +/* Returns -1 (with an exception set) on failure, 0 on success. This may + be called once on a freshly created Pickler. */ +static int +_Unpickler_SetInputStream(UnpicklerObject *self, PyObject *file) +{ + self->read = PyObject_GetAttrString(file, "read"); + self->readline = PyObject_GetAttrString(file, "readline"); + if (self->readline == NULL || self->read == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_SetString(PyExc_TypeError, + "file must have 'read' and 'readline' attributes"); + Py_CLEAR(self->read); + Py_CLEAR(self->readline); return -1; } + return 0; +} + +/* Returns -1 (with an exception set) on failure, 0 on success. This may + be called once on a freshly created Pickler. */ +static int +_Unpickler_SetInputEncoding(UnpicklerObject *self, + const char *encoding, + const char *errors) +{ + if (encoding == NULL) + encoding = "ASCII"; + if (errors == NULL) + errors = "strict"; - memo_id = PyTuple_GetItem(value, 0); - if (memo_id == NULL) + self->encoding = strdup(encoding); + self->errors = strdup(errors); + if (self->encoding == NULL || self->errors == NULL) { + PyErr_NoMemory(); return -1; + } + return 0; +} + +/* Generate a GET opcode for an object stored in the memo. */ +static int +memo_get(PicklerObject *self, PyObject *key) +{ + long *value; + char pdata[30]; + int len; - if (!PyLong_Check(memo_id)) { - PyErr_SetString(PicklingError, "memo id must be an integer"); + value = PyMemoTable_Get(self->memo, key); + if (value == NULL) { + PyErr_SetObject(PyExc_KeyError, key); return -1; } - x = PyLong_AsLong(memo_id); - if (x == -1 && PyErr_Occurred()) - return -1; if (!self->bin) { pdata[0] = GET; - PyOS_snprintf(pdata + 1, sizeof(pdata) - 1, "%ld\n", x); + PyOS_snprintf(pdata + 1, sizeof(pdata) - 1, "%ld\n", *value); len = (int)strlen(pdata); } else { - if (x < 256) { + if (*value < 256) { pdata[0] = BINGET; - pdata[1] = (unsigned char)(x & 0xff); + pdata[1] = (unsigned char)(*value & 0xff); len = 2; } - else if (x <= 0xffffffffL) { + else if (*value <= 0xffffffffL) { pdata[0] = LONG_BINGET; - pdata[1] = (unsigned char)(x & 0xff); - pdata[2] = (unsigned char)((x >> 8) & 0xff); - pdata[3] = (unsigned char)((x >> 16) & 0xff); - pdata[4] = (unsigned char)((x >> 24) & 0xff); + pdata[1] = (unsigned char)(*value & 0xff); + pdata[2] = (unsigned char)((*value >> 8) & 0xff); + pdata[3] = (unsigned char)((*value >> 16) & 0xff); + pdata[4] = (unsigned char)((*value >> 24) & 0xff); len = 5; } else { /* unlikely */ @@ -600,7 +1198,7 @@ } } - if (pickler_write(self, pdata, len) < 0) + if (_Pickler_Write(self, pdata, len) < 0) return -1; return 0; @@ -611,9 +1209,6 @@ static int memo_put(PicklerObject *self, PyObject *obj) { - PyObject *key = NULL; - PyObject *memo_id = NULL; - PyObject *tuple = NULL; long x; char pdata[30]; int len; @@ -622,23 +1217,8 @@ if (self->fast) return 0; - key = PyLong_FromVoidPtr(obj); - if (key == NULL) - goto error; - if ((x = PyDict_Size(self->memo)) < 0) - goto error; - memo_id = PyLong_FromLong(x); - if (memo_id == NULL) - goto error; - tuple = PyTuple_New(2); - if (tuple == NULL) - goto error; - - Py_INCREF(memo_id); - PyTuple_SET_ITEM(tuple, 0, memo_id); - Py_INCREF(obj); - PyTuple_SET_ITEM(tuple, 1, obj); - if (PyDict_SetItem(self->memo, key, tuple) < 0) + x = PyMemoTable_Size(self->memo); + if (PyMemoTable_Set(self->memo, obj, x) < 0) goto error; if (!self->bin) { @@ -667,7 +1247,7 @@ } } - if (pickler_write(self, pdata, len) < 0) + if (_Pickler_Write(self, pdata, len) < 0) goto error; if (0) { @@ -675,10 +1255,6 @@ status = -1; } - Py_XDECREF(key); - Py_XDECREF(memo_id); - Py_XDECREF(tuple); - return status; } @@ -821,7 +1397,7 @@ save_none(PicklerObject *self, PyObject *obj) { const char none_op = NONE; - if (pickler_write(self, &none_op, 1) < 0) + if (_Pickler_Write(self, &none_op, 1) < 0) return -1; return 0; @@ -836,10 +1412,10 @@ if (self->proto >= 2) { const char bool_op = p ? NEWTRUE : NEWFALSE; - if (pickler_write(self, &bool_op, 1) < 0) + if (_Pickler_Write(self, &bool_op, 1) < 0) return -1; } - else if (pickler_write(self, buf[p], len[p]) < 0) + else if (_Pickler_Write(self, buf[p], len[p]) < 0) return -1; return 0; @@ -861,7 +1437,7 @@ */ pdata[0] = LONG; /* use LONG for consistency with pickle.py */ PyOS_snprintf(pdata + 1, sizeof(pdata) - 1, "%ldL\n", x); - if (pickler_write(self, pdata, strlen(pdata)) < 0) + if (_Pickler_Write(self, pdata, strlen(pdata)) < 0) return -1; } else { @@ -886,7 +1462,7 @@ len = 5; } - if (pickler_write(self, pdata, len) < 0) + if (_Pickler_Write(self, pdata, len) < 0) return -1; } @@ -922,7 +1498,7 @@ if (sign == 0) { header[0] = LONG1; header[1] = 0; /* It's 0 -- an empty bytestring. */ - if (pickler_write(self, header, 2) < 0) + if (_Pickler_Write(self, header, 2) < 0) goto error; return 0; } @@ -983,8 +1559,8 @@ } size = 5; } - if (pickler_write(self, header, size) < 0 || - pickler_write(self, (char *)pdata, (int)nbytes) < 0) + if (_Pickler_Write(self, header, size) < 0 || + _Pickler_Write(self, (char *)pdata, (int)nbytes) < 0) goto error; } else { @@ -1002,9 +1578,9 @@ if (string == NULL) goto error; - if (pickler_write(self, &long_op, 1) < 0 || - pickler_write(self, string, size) < 0 || - pickler_write(self, "L\n", 2) < 0) + if (_Pickler_Write(self, &long_op, 1) < 0 || + _Pickler_Write(self, string, size) < 0 || + _Pickler_Write(self, "L\n", 2) < 0) goto error; } @@ -1027,7 +1603,7 @@ pdata[0] = BINFLOAT; if (_PyFloat_Pack8(x, (unsigned char *)&pdata[1], 0) < 0) return -1; - if (pickler_write(self, pdata, 9) < 0) + if (_Pickler_Write(self, pdata, 9) < 0) return -1; } else { @@ -1035,7 +1611,7 @@ char *buf = NULL; char op = FLOAT; - if (pickler_write(self, &op, 1) < 0) + if (_Pickler_Write(self, &op, 1) < 0) goto done; buf = PyOS_double_to_string(x, 'g', 17, 0, NULL); @@ -1044,10 +1620,10 @@ goto done; } - if (pickler_write(self, buf, strlen(buf)) < 0) + if (_Pickler_Write(self, buf, strlen(buf)) < 0) goto done; - if (pickler_write(self, "\n", 1) < 0) + if (_Pickler_Write(self, "\n", 1) < 0) goto done; result = 0; @@ -1113,10 +1689,10 @@ return -1; /* string too large */ } - if (pickler_write(self, header, len) < 0) + if (_Pickler_Write(self, header, len) < 0) return -1; - if (pickler_write(self, PyBytes_AS_STRING(obj), size) < 0) + if (_Pickler_Write(self, PyBytes_AS_STRING(obj), size) < 0) return -1; if (memo_put(self, obj) < 0) @@ -1243,10 +1819,10 @@ pdata[3] = (unsigned char)((size >> 16) & 0xff); pdata[4] = (unsigned char)((size >> 24) & 0xff); - if (pickler_write(self, pdata, 5) < 0) + if (_Pickler_Write(self, pdata, 5) < 0) goto error; - if (pickler_write(self, PyBytes_AS_STRING(encoded), size) < 0) + if (_Pickler_Write(self, PyBytes_AS_STRING(encoded), size) < 0) goto error; } else { @@ -1257,14 +1833,14 @@ if (encoded == NULL) goto error; - if (pickler_write(self, &unicode_op, 1) < 0) + if (_Pickler_Write(self, &unicode_op, 1) < 0) goto error; size = PyBytes_GET_SIZE(encoded); - if (pickler_write(self, PyBytes_AS_STRING(encoded), size) < 0) + if (_Pickler_Write(self, PyBytes_AS_STRING(encoded), size) < 0) goto error; - if (pickler_write(self, "\n", 1) < 0) + if (_Pickler_Write(self, "\n", 1) < 0) goto error; } if (memo_put(self, obj) < 0) @@ -1307,9 +1883,7 @@ static int save_tuple(PicklerObject *self, PyObject *obj) { - PyObject *memo_key = NULL; int len, i; - int status = 0; const char mark_op = MARK; const char tuple_op = TUPLE; @@ -1332,40 +1906,35 @@ pdata[1] = TUPLE; len = 2; } - if (pickler_write(self, pdata, len) < 0) + if (_Pickler_Write(self, pdata, len) < 0) return -1; return 0; } - /* id(tuple) isn't in the memo now. If it shows up there after + /* The tuple isn't in the memo now. If it shows up there after * saving the tuple elements, the tuple must be recursive, in * which case we'll pop everything we put on the stack, and fetch * its value from the memo. */ - memo_key = PyLong_FromVoidPtr(obj); - if (memo_key == NULL) - return -1; - if (len <= 3 && self->proto >= 2) { /* Use TUPLE{1,2,3} opcodes. */ if (store_tuple_elements(self, obj, len) < 0) - goto error; + return -1; - if (PyDict_GetItem(self->memo, memo_key)) { + if (PyMemoTable_Get(self->memo, obj)) { /* pop the len elements */ for (i = 0; i < len; i++) - if (pickler_write(self, &pop_op, 1) < 0) - goto error; + if (_Pickler_Write(self, &pop_op, 1) < 0) + return -1; /* fetch from memo */ - if (memo_get(self, memo_key) < 0) - goto error; + if (memo_get(self, obj) < 0) + return -1; - Py_DECREF(memo_key); return 0; } else { /* Not recursive. */ - if (pickler_write(self, len2opcode + len, 1) < 0) - goto error; + if (_Pickler_Write(self, len2opcode + len, 1) < 0) + return -1; } goto memoize; } @@ -1373,49 +1942,42 @@ /* proto < 2 and len > 0, or proto >= 2 and len > 3. * Generate MARK e1 e2 ... TUPLE */ - if (pickler_write(self, &mark_op, 1) < 0) - goto error; + if (_Pickler_Write(self, &mark_op, 1) < 0) + return -1; if (store_tuple_elements(self, obj, len) < 0) - goto error; + return -1; - if (PyDict_GetItem(self->memo, memo_key)) { + if (PyMemoTable_Get(self->memo, obj)) { /* pop the stack stuff we pushed */ if (self->bin) { - if (pickler_write(self, &pop_mark_op, 1) < 0) - goto error; + if (_Pickler_Write(self, &pop_mark_op, 1) < 0) + return -1; } else { /* Note that we pop one more than len, to remove * the MARK too. */ for (i = 0; i <= len; i++) - if (pickler_write(self, &pop_op, 1) < 0) - goto error; + if (_Pickler_Write(self, &pop_op, 1) < 0) + return -1; } /* fetch from memo */ - if (memo_get(self, memo_key) < 0) - goto error; + if (memo_get(self, obj) < 0) + return -1; - Py_DECREF(memo_key); return 0; } else { /* Not recursive. */ - if (pickler_write(self, &tuple_op, 1) < 0) - goto error; + if (_Pickler_Write(self, &tuple_op, 1) < 0) + return -1; } memoize: if (memo_put(self, obj) < 0) - goto error; - - if (0) { - error: - status = -1; - } + return -1; - Py_DECREF(memo_key); - return status; + return 0; } /* iter is an iterator giving items, and we batch up chunks of @@ -1455,7 +2017,7 @@ Py_DECREF(obj); if (i < 0) return -1; - if (pickler_write(self, &append_op, 1) < 0) + if (_Pickler_Write(self, &append_op, 1) < 0) return -1; } return 0; @@ -1482,7 +2044,7 @@ /* Only one item to write */ if (save(self, firstitem, 0) < 0) goto error; - if (pickler_write(self, &append_op, 1) < 0) + if (_Pickler_Write(self, &append_op, 1) < 0) goto error; Py_CLEAR(firstitem); break; @@ -1491,7 +2053,7 @@ /* More than one item to write */ /* Pump out MARK, items, APPENDS. */ - if (pickler_write(self, &mark_op, 1) < 0) + if (_Pickler_Write(self, &mark_op, 1) < 0) goto error; if (save(self, firstitem, 0) < 0) @@ -1517,7 +2079,7 @@ } } - if (pickler_write(self, &appends_op, 1) < 0) + if (_Pickler_Write(self, &appends_op, 1) < 0) goto error; } while (n == BATCHSIZE); @@ -1529,10 +2091,65 @@ return -1; } +/* This is a variant of batch_list() above, specialized for lists (with no + * support for list subclasses). Like batch_list(), we batch up chunks of + * MARK item item ... item APPENDS + * opcode sequences. Calling code should have arranged to first create an + * empty list, or list-like object, for the APPENDS to operate on. + * Returns 0 on success, -1 on error. + * + * This version is considerably faster than batch_list(), if less general. + * + * Note that this only works for protocols > 0. + */ +static int +batch_list_exact(PicklerObject *self, PyObject *obj) +{ + PyObject *item = NULL; + int this_batch, total; + + const char append_op = APPEND; + const char appends_op = APPENDS; + const char mark_op = MARK; + + assert(obj != NULL); + assert(self->proto > 0); + assert(PyList_CheckExact(obj)); + + if (PyList_GET_SIZE(obj) == 1) { + item = PyList_GET_ITEM(obj, 0); + if (save(self, item, 0) < 0) + return -1; + if (_Pickler_Write(self, &append_op, 1) < 0) + return -1; + return 0; + } + + /* Write in batches of BATCHSIZE. */ + total = 0; + do { + this_batch = 0; + if (_Pickler_Write(self, &mark_op, 1) < 0) + return -1; + while (total < PyList_GET_SIZE(obj)) { + item = PyList_GET_ITEM(obj, total); + if (save(self, item, 0) < 0) + return -1; + total++; + if (++this_batch == BATCHSIZE) + break; + } + if (_Pickler_Write(self, &appends_op, 1) < 0) + return -1; + + } while (total < PyList_GET_SIZE(obj)); + + return 0; +} + static int save_list(PicklerObject *self, PyObject *obj) { - PyObject *iter; char header[3]; int len; int status = 0; @@ -1551,7 +2168,7 @@ len = 2; } - if (pickler_write(self, header, len) < 0) + if (_Pickler_Write(self, header, len) < 0) goto error; /* Get list length, and bow out early if empty. */ @@ -1562,14 +2179,24 @@ goto error; if (len != 0) { - /* Save the list elements. */ - iter = PyObject_GetIter(obj); - if (iter == NULL) - goto error; - status = batch_list(self, iter); - Py_DECREF(iter); - } + /* Materialize the list elements. */ + if (PyList_CheckExact(obj) && self->proto > 0) { + if (Py_EnterRecursiveCall(" while pickling an object") == 0) { + status = batch_list_exact(self, obj); + Py_LeaveRecursiveCall(); + } + } else { + PyObject *iter = PyObject_GetIter(obj); + if (iter == NULL) + goto error; + if (Py_EnterRecursiveCall(" while pickling an object") == 0) { + status = batch_list(self, iter); + Py_LeaveRecursiveCall(); + } + Py_DECREF(iter); + } + } if (0) { error: status = -1; @@ -1625,7 +2252,7 @@ Py_DECREF(obj); if (i < 0) return -1; - if (pickler_write(self, &setitem_op, 1) < 0) + if (_Pickler_Write(self, &setitem_op, 1) < 0) return -1; } return 0; @@ -1659,7 +2286,7 @@ goto error; if (save(self, PyTuple_GET_ITEM(firstitem, 1), 0) < 0) goto error; - if (pickler_write(self, &setitem_op, 1) < 0) + if (_Pickler_Write(self, &setitem_op, 1) < 0) goto error; Py_CLEAR(firstitem); break; @@ -1668,7 +2295,7 @@ /* More than one item to write */ /* Pump out MARK, items, SETITEMS. */ - if (pickler_write(self, &mark_op, 1) < 0) + if (_Pickler_Write(self, &mark_op, 1) < 0) goto error; if (save(self, PyTuple_GET_ITEM(firstitem, 0), 0) < 0) @@ -1684,7 +2311,7 @@ PyErr_SetString(PyExc_TypeError, "dict items " "iterator must return 2-tuples"); goto error; - } + } if (save(self, PyTuple_GET_ITEM(obj, 0), 0) < 0 || save(self, PyTuple_GET_ITEM(obj, 1), 0) < 0) goto error; @@ -1702,7 +2329,7 @@ } } - if (pickler_write(self, &setitems_op, 1) < 0) + if (_Pickler_Write(self, &setitems_op, 1) < 0) goto error; } while (n == BATCHSIZE); @@ -1746,7 +2373,7 @@ return -1; if (save(self, value, 0) < 0) return -1; - if (pickler_write(self, &setitem_op, 1) < 0) + if (_Pickler_Write(self, &setitem_op, 1) < 0) return -1; return 0; } @@ -1754,7 +2381,7 @@ /* Write in batches of BATCHSIZE. */ do { i = 0; - if (pickler_write(self, &mark_op, 1) < 0) + if (_Pickler_Write(self, &mark_op, 1) < 0) return -1; while (PyDict_Next(obj, &ppos, &key, &value)) { if (save(self, key, 0) < 0) @@ -1764,7 +2391,7 @@ if (++i == BATCHSIZE) break; } - if (pickler_write(self, &setitems_op, 1) < 0) + if (_Pickler_Write(self, &setitems_op, 1) < 0) return -1; if (PyDict_Size(obj) != dict_size) { PyErr_Format( @@ -1799,7 +2426,7 @@ len = 2; } - if (pickler_write(self, header, len) < 0) + if (_Pickler_Write(self, header, len) < 0) goto error; /* Get dict size, and bow out early if empty. */ @@ -1962,7 +2589,7 @@ n = 5; } - if (pickler_write(self, pdata, n) < 0) + if (_Pickler_Write(self, pdata, n) < 0) goto error; } else { @@ -1973,7 +2600,7 @@ PyObject *(*unicode_encoder)(PyObject *); gen_global: - if (pickler_write(self, &global_op, 1) < 0) + if (_Pickler_Write(self, &global_op, 1) < 0) goto error; /* Since Python 3.0 now supports non-ASCII identifiers, we encode both @@ -2053,13 +2680,13 @@ "pickle protocol %i", module_name, self->proto); goto error; } - if (pickler_write(self, PyBytes_AS_STRING(encoded), + if (_Pickler_Write(self, PyBytes_AS_STRING(encoded), PyBytes_GET_SIZE(encoded)) < 0) { Py_DECREF(encoded); goto error; } Py_DECREF(encoded); - if(pickler_write(self, "\n", 1) < 0) + if(_Pickler_Write(self, "\n", 1) < 0) goto error; /* Save the name of the module. */ @@ -2071,13 +2698,13 @@ "pickle protocol %i", global_name, self->proto); goto error; } - if (pickler_write(self, PyBytes_AS_STRING(encoded), + if (_Pickler_Write(self, PyBytes_AS_STRING(encoded), PyBytes_GET_SIZE(encoded)) < 0) { Py_DECREF(encoded); goto error; } Py_DECREF(encoded); - if(pickler_write(self, "\n", 1) < 0) + if(_Pickler_Write(self, "\n", 1) < 0) goto error; /* Memoize the object. */ @@ -2106,14 +2733,14 @@ const char binpersid_op = BINPERSID; Py_INCREF(obj); - pid = pickler_call(self, func, obj); + pid = _Pickler_FastCall(self, func, obj); if (pid == NULL) return -1; if (pid != Py_None) { if (self->bin) { if (save(self, pid, 1) < 0 || - pickler_write(self, &binpersid_op, 1) < 0) + _Pickler_Write(self, &binpersid_op, 1) < 0) goto error; } else { @@ -2133,9 +2760,9 @@ if (pid_ascii_bytes == NULL) goto error; - if (pickler_write(self, &persid_op, 1) < 0 || - pickler_write(self, pid_ascii_bytes, size) < 0 || - pickler_write(self, "\n", 1) < 0) + if (_Pickler_Write(self, &persid_op, 1) < 0 || + _Pickler_Write(self, pid_ascii_bytes, size) < 0 || + _Pickler_Write(self, "\n", 1) < 0) goto error; } status = 1; @@ -2220,6 +2847,8 @@ if (newobj_str == NULL) { newobj_str = PyUnicode_InternFromString("__newobj__"); + if (newobj_str == NULL) + return -1; } name_str = PyObject_GetAttrString(callable, "__name__"); @@ -2312,13 +2941,13 @@ return -1; /* Add NEWOBJ opcode. */ - if (pickler_write(self, &newobj_op, 1) < 0) + if (_Pickler_Write(self, &newobj_op, 1) < 0) return -1; } else { /* Not using NEWOBJ. */ if (save(self, callable, 0) < 0 || save(self, argtup, 0) < 0 || - pickler_write(self, &reduce_op, 1) < 0) + _Pickler_Write(self, &reduce_op, 1) < 0) return -1; } @@ -2337,7 +2966,7 @@ if (state) { if (save(self, state, 0) < 0 || - pickler_write(self, &build_op, 1) < 0) + _Pickler_Write(self, &build_op, 1) < 0) return -1; } @@ -2350,7 +2979,6 @@ PyTypeObject *type; PyObject *reduce_func = NULL; PyObject *reduce_value = NULL; - PyObject *memo_key = NULL; int status = 0; if (Py_EnterRecursiveCall(" while pickling an object") < 0) @@ -2370,11 +2998,10 @@ type = Py_TYPE(obj); - /* XXX: The old cPickle had an optimization that used switch-case - statement dispatching on the first letter of the type name. It was - probably not a bad idea after all. If benchmarks shows that particular - optimization had some real benefits, it would be nice to add it - back. */ + /* The old cPickle had an optimization that used switch-case statement + dispatching on the first letter of the type name. This has was removed + since benchmarks shown that this optimization was actually slowing + things down. */ /* Atom types; these aren't memoized, so don't check the memo. */ @@ -2398,11 +3025,8 @@ /* Check the memo to see if it has the object. If so, generate a GET (or BINGET) opcode, instead of pickling the object once again. */ - memo_key = PyLong_FromVoidPtr(obj); - if (memo_key == NULL) - goto error; - if (PyDict_GetItem(self->memo, memo_key)) { - if (memo_get(self, memo_key) < 0) + if (PyMemoTable_Get(self->memo, obj)) { + if (memo_get(self, obj) < 0) goto error; goto done; } @@ -2465,7 +3089,7 @@ */ Py_INCREF(reduce_func); Py_INCREF(obj); - reduce_value = pickler_call(self, reduce_func, obj); + reduce_value = _Pickler_FastCall(self, reduce_func, obj); } else { static PyObject *reduce_str = NULL; @@ -2495,7 +3119,7 @@ PyObject *proto; proto = PyLong_FromLong(self->proto); if (proto != NULL) { - reduce_value = pickler_call(self, reduce_func, proto); + reduce_value = _Pickler_FastCall(self, reduce_func, proto); } } else { @@ -2538,7 +3162,6 @@ } done: Py_LeaveRecursiveCall(); - Py_XDECREF(memo_key); Py_XDECREF(reduce_func); Py_XDECREF(reduce_value); @@ -2556,13 +3179,12 @@ header[0] = PROTO; assert(self->proto >= 0 && self->proto < 256); header[1] = (unsigned char)self->proto; - if (pickler_write(self, header, 2) < 0) + if (_Pickler_Write(self, header, 2) < 0) return -1; } if (save(self, obj, 0) < 0 || - pickler_write(self, &stop_op, 1) < 0 || - pickler_write(self, NULL, 0) < 0) + _Pickler_Write(self, &stop_op, 1) < 0) return -1; return 0; @@ -2580,7 +3202,7 @@ Pickler_clear_memo(PicklerObject *self) { if (self->memo) - PyDict_Clear(self->memo); + PyMemoTable_Clear(self->memo); Py_RETURN_NONE; } @@ -2606,9 +3228,15 @@ if (!PyArg_ParseTuple(args, "O:dump", &obj)) return NULL; + if (_Pickler_ClearBuffer(self) < 0) + return NULL; + if (dump(self, obj) < 0) return NULL; + if (_Pickler_FlushToFile(self) < 0) + return NULL; + Py_RETURN_NONE; } @@ -2625,13 +3253,13 @@ { PyObject_GC_UnTrack(self); + Py_XDECREF(self->output_buffer); Py_XDECREF(self->write); - Py_XDECREF(self->memo); Py_XDECREF(self->pers_func); Py_XDECREF(self->arg); Py_XDECREF(self->fast_memo); - PyMem_Free(self->write_buf); + PyMemoTable_Del(self->memo); Py_TYPE(self)->tp_free((PyObject *)self); } @@ -2640,7 +3268,6 @@ Pickler_traverse(PicklerObject *self, visitproc visit, void *arg) { Py_VISIT(self->write); - Py_VISIT(self->memo); Py_VISIT(self->pers_func); Py_VISIT(self->arg); Py_VISIT(self->fast_memo); @@ -2650,18 +3277,21 @@ static int Pickler_clear(PicklerObject *self) { + Py_CLEAR(self->output_buffer); Py_CLEAR(self->write); - Py_CLEAR(self->memo); Py_CLEAR(self->pers_func); Py_CLEAR(self->arg); Py_CLEAR(self->fast_memo); - PyMem_Free(self->write_buf); - self->write_buf = NULL; - + if (self->memo != NULL) { + PyMemoTable *memo = self->memo; + self->memo = NULL; + PyMemoTable_Del(memo); + } return 0; } + PyDoc_STRVAR(Pickler_doc, "Pickler(file, protocol=None)" "\n" @@ -2692,10 +3322,9 @@ static char *kwlist[] = {"file", "protocol", "fix_imports", 0}; PyObject *file; PyObject *proto_obj = NULL; - long proto = 0; - int fix_imports = 1; + PyObject *fix_imports = Py_True; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oi:Pickler", + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO:Pickler", kwlist, &file, &proto_obj, &fix_imports)) return -1; @@ -2703,44 +3332,31 @@ if (self->write != NULL) (void)Pickler_clear(self); - if (proto_obj == NULL || proto_obj == Py_None) - proto = DEFAULT_PROTOCOL; - else { - proto = PyLong_AsLong(proto_obj); - if (proto == -1 && PyErr_Occurred()) - return -1; - } + if (_Pickler_SetProtocol(self, proto_obj, fix_imports) < 0) + return -1; - if (proto < 0) - proto = HIGHEST_PROTOCOL; - if (proto > HIGHEST_PROTOCOL) { - PyErr_Format(PyExc_ValueError, "pickle protocol must be <= %d", - HIGHEST_PROTOCOL); + if (_Pickler_SetOutputStream(self, file) < 0) return -1; + + /* memo and output_buffer may have already been created in _Pickler_New */ + if (self->memo == NULL) { + self->memo = PyMemoTable_New(); + if (self->memo == NULL) + return -1; + } + self->output_len = 0; + if (self->output_buffer == NULL) { + self->max_output_len = WRITE_BUF_SIZE; + self->output_buffer = PyBytes_FromStringAndSize(NULL, + self->max_output_len); + if (self->output_buffer == NULL) + return -1; } - self->proto = proto; - self->bin = proto > 0; self->arg = NULL; self->fast = 0; self->fast_nesting = 0; self->fast_memo = NULL; - self->fix_imports = fix_imports && proto < 3; - - if (!PyObject_HasAttrString(file, "write")) { - PyErr_SetString(PyExc_TypeError, - "file must have a 'write' attribute"); - return -1; - } - self->write = PyObject_GetAttrString(file, "write"); - if (self->write == NULL) - return -1; - self->buf_size = 0; - self->write_buf = (char *)PyMem_Malloc(WRITE_BUF_SIZE); - if (self->write_buf == NULL) { - PyErr_NoMemory(); - return -1; - } self->pers_func = NULL; if (PyObject_HasAttrString((PyObject *)self, "persistent_id")) { self->pers_func = PyObject_GetAttrString((PyObject *)self, @@ -2748,44 +3364,248 @@ if (self->pers_func == NULL) return -1; } - self->memo = PyDict_New(); - if (self->memo == NULL) - return -1; + return 0; +} + +/* Define a proxy object for the Pickler's internal memo object. This is to + * avoid breaking code like: + * pickler.memo.clear() + * and + * pickler.memo = saved_memo + * Is this a good idea? Not really, but we don't want to break code that uses + * it. Note that we don't implement the entire mapping API here. This is + * intentional, as these should be treated as black-box implementation details. + */ + +typedef struct { + PyObject_HEAD + PicklerObject *pickler; /* Pickler whose memo table we're proxying. */ +} PicklerMemoProxyObject; + +PyDoc_STRVAR(pmp_clear_doc, +"memo.clear() -> None. Remove all items from memo."); + +static PyObject * +pmp_clear(PicklerMemoProxyObject *self) +{ + if (self->pickler->memo) + PyMemoTable_Clear(self->pickler->memo); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pmp_copy_doc, +"memo.copy() -> new_memo. Copy the memo to a new object."); + +static PyObject * +pmp_copy(PicklerMemoProxyObject *self) +{ + Py_ssize_t i; + PyMemoTable *memo; + PyObject *new_memo = PyDict_New(); + if (new_memo == NULL) + return NULL; + + memo = self->pickler->memo; + for (i = 0; i < memo->mt_allocated; ++i) { + PyMemoEntry entry = memo->mt_table[i]; + if (entry.me_key != NULL) { + int status; + PyObject *key, *value; + + key = PyLong_FromVoidPtr(entry.me_key); + value = Py_BuildValue("lO", entry.me_value, entry.me_key); + + if (key == NULL || value == NULL) { + Py_XDECREF(key); + Py_XDECREF(value); + goto error; + } + status = PyDict_SetItem(new_memo, key, value); + Py_DECREF(key); + Py_DECREF(value); + if (status < 0) + goto error; + } + } + return new_memo; + + error: + Py_XDECREF(new_memo); + return NULL; +} + +PyDoc_STRVAR(pmp_reduce_doc, +"memo.__reduce__(). Pickling support."); + +static PyObject * +pmp_reduce(PicklerMemoProxyObject *self, PyObject *args) +{ + PyObject *reduce_value, *dict_args; + PyObject *contents = pmp_copy(self); + if (contents == NULL) + return NULL; + + reduce_value = PyTuple_New(2); + if (reduce_value == NULL) { + Py_DECREF(contents); + return NULL; + } + dict_args = PyTuple_New(1); + if (dict_args == NULL) { + Py_DECREF(contents); + Py_DECREF(reduce_value); + return NULL; + } + PyTuple_SET_ITEM(dict_args, 0, contents); + Py_INCREF((PyObject *)&PyDict_Type); + PyTuple_SET_ITEM(reduce_value, 0, (PyObject *)&PyDict_Type); + PyTuple_SET_ITEM(reduce_value, 1, dict_args); + return reduce_value; +} + +static PyMethodDef picklerproxy_methods[] = { + {"clear", (PyCFunction)pmp_clear, METH_NOARGS, pmp_clear_doc}, + {"copy", (PyCFunction)pmp_copy, METH_NOARGS, pmp_copy_doc}, + {"__reduce__", (PyCFunction)pmp_reduce, METH_VARARGS, pmp_reduce_doc}, + {NULL, NULL} /* sentinel */ +}; + +static void +PicklerMemoProxy_dealloc(PicklerMemoProxyObject *self) +{ + PyObject_GC_UnTrack(self); + Py_XDECREF(self->pickler); + PyObject_GC_Del((PyObject *)self); +} +static int +PicklerMemoProxy_traverse(PicklerMemoProxyObject *self, + visitproc visit, void *arg) +{ + Py_VISIT(self->pickler); + return 0; +} + +static int +PicklerMemoProxy_clear(PicklerMemoProxyObject *self) +{ + Py_CLEAR(self->pickler); return 0; } +static PyTypeObject PicklerMemoProxyType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pickle.PicklerMemoProxy", /*tp_name*/ + sizeof(PicklerMemoProxyObject), /*tp_basicsize*/ + 0, + (destructor)PicklerMemoProxy_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)PyObject_HashNotImplemented, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + 0, /* tp_doc */ + (traverseproc)PicklerMemoProxy_traverse, /* tp_traverse */ + (inquiry)PicklerMemoProxy_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + picklerproxy_methods, /* tp_methods */ +}; + +static PyObject * +PicklerMemoProxy_New(PicklerObject *pickler) +{ + PicklerMemoProxyObject *self; + + self = PyObject_GC_New(PicklerMemoProxyObject, &PicklerMemoProxyType); + if (self == NULL) + return NULL; + Py_INCREF(pickler); + self->pickler = pickler; + PyObject_GC_Track(self); + return (PyObject *)self; +} + +/*****************************************************************************/ + static PyObject * Pickler_get_memo(PicklerObject *self) { - if (self->memo == NULL) - PyErr_SetString(PyExc_AttributeError, "memo"); - else - Py_INCREF(self->memo); - return self->memo; + return PicklerMemoProxy_New(self); } static int -Pickler_set_memo(PicklerObject *self, PyObject *value) +Pickler_set_memo(PicklerObject *self, PyObject *obj) { - PyObject *tmp; + PyMemoTable *new_memo = NULL; - if (value == NULL) { + if (obj == NULL) { PyErr_SetString(PyExc_TypeError, "attribute deletion is not supported"); return -1; } - if (!PyDict_Check(value)) { - PyErr_SetString(PyExc_TypeError, "memo must be a dictionary"); + + if (Py_TYPE(obj) == &PicklerMemoProxyType) { + PicklerObject *pickler = + ((PicklerMemoProxyObject *)obj)->pickler; + + new_memo = PyMemoTable_Copy(pickler->memo); + if (new_memo == NULL) + return -1; + } + else if (PyDict_Check(obj)) { + Py_ssize_t i = 0; + PyObject *key, *value; + + new_memo = PyMemoTable_New(); + if (new_memo == NULL) + return -1; + + while (PyDict_Next(obj, &i, &key, &value)) { + long memo_id; + PyObject *memo_obj; + + if (!PyTuple_Check(value) || Py_SIZE(value) != 2) { + PyErr_SetString(PyExc_TypeError, + "'memo' values must be 2-item tuples"); + goto error; + } + memo_id = PyLong_AsLong(PyTuple_GET_ITEM(value, 0)); + if (memo_id == -1 && PyErr_Occurred()) + goto error; + memo_obj = PyTuple_GET_ITEM(value, 1); + if (PyMemoTable_Set(new_memo, memo_obj, memo_id) < 0) + goto error; + } + } + else { + PyErr_Format(PyExc_TypeError, + "'memo' attribute must be an PicklerMemoProxy object" + "or dict, not %.200s", Py_TYPE(obj)->tp_name); return -1; } - tmp = self->memo; - Py_INCREF(value); - self->memo = value; - Py_XDECREF(tmp); + PyMemoTable_Del(self->memo); + self->memo = new_memo; return 0; + + error: + if (new_memo) + PyMemoTable_Del(new_memo); + return -1; } static PyObject * @@ -2926,21 +3746,22 @@ Py_ssize_t len; long x; - if ((len = unpickler_readline(self, &s)) < 0) + if ((len = _Unpickler_Readline(self, &s)) < 0) return -1; if (len < 2) return bad_readline(); errno = 0; - /* XXX: Should the base argument of strtol() be explicitly set to 10? */ + /* XXX: Should the base argument of strtol() be explicitly set to 10? + XXX(avassalotti): Should this uses PyOS_strtol()? */ x = strtol(s, &endptr, 0); - if (errno || (*endptr != '\n') || (endptr[1] != '\0')) { + if (errno || (*endptr != '\n' && *endptr != '\0')) { /* Hm, maybe we've got something long. Let's try reading * it as a Python long object. */ errno = 0; /* XXX: Same thing about the base here. */ - value = PyLong_FromString(s, NULL, 0); + value = PyLong_FromString(s, NULL, 0); if (value == NULL) { PyErr_SetString(PyExc_ValueError, "could not convert string to int"); @@ -3017,7 +3838,7 @@ { char *s; - if (unpickler_read(self, &s, 4) < 0) + if (_Unpickler_Read(self, &s, 4) < 0) return -1; return load_binintx(self, s, 4); @@ -3028,7 +3849,7 @@ { char *s; - if (unpickler_read(self, &s, 1) < 0) + if (_Unpickler_Read(self, &s, 1) < 0) return -1; return load_binintx(self, s, 1); @@ -3039,7 +3860,7 @@ { char *s; - if (unpickler_read(self, &s, 2) < 0) + if (_Unpickler_Read(self, &s, 2) < 0) return -1; return load_binintx(self, s, 2); @@ -3052,7 +3873,7 @@ char *s; Py_ssize_t len; - if ((len = unpickler_readline(self, &s)) < 0) + if ((len = _Unpickler_Readline(self, &s)) < 0) return -1; if (len < 2) return bad_readline(); @@ -3061,9 +3882,8 @@ the 'L' before calling PyLong_FromString. In order to maintain compatibility with Python 3.0.0, we don't actually *require* the 'L' to be present. */ - if (s[len-2] == 'L') { + if (s[len-2] == 'L') s[len-2] = '\0'; - } /* XXX: Should the base argument explicitly set to 10? */ value = PyLong_FromString(s, NULL, 0); if (value == NULL) @@ -3084,7 +3904,7 @@ char *pdata; assert(size == 1 || size == 4); - if (unpickler_read(self, &nbytes, size) < 0) + if (_Unpickler_Read(self, &nbytes, size) < 0) return -1; size = calc_binint(nbytes, size); @@ -3099,7 +3919,7 @@ value = PyLong_FromLong(0L); else { /* Read the raw little-endian bytes and convert. */ - if (unpickler_read(self, &pdata, size) < 0) + if (_Unpickler_Read(self, &pdata, size) < 0) return -1; value = _PyLong_FromByteArray((unsigned char *)pdata, (size_t)size, 1 /* little endian */ , 1 /* signed */ ); @@ -3118,7 +3938,7 @@ Py_ssize_t len; double d; - if ((len = unpickler_readline(self, &s)) < 0) + if ((len = _Unpickler_Readline(self, &s)) < 0) return -1; if (len < 2) return bad_readline(); @@ -3127,7 +3947,7 @@ d = PyOS_string_to_double(s, &endptr, PyExc_OverflowError); if (d == -1.0 && PyErr_Occurred()) return -1; - if ((endptr[0] != '\n') || (endptr[1] != '\0')) { + if ((endptr[0] != '\n') && (endptr[0] != '\0')) { PyErr_SetString(PyExc_ValueError, "could not convert string to float"); return -1; } @@ -3137,7 +3957,7 @@ PDATA_PUSH(self->stack, value, -1); return 0; - } +} static int load_binfloat(UnpicklerObject *self) @@ -3146,7 +3966,7 @@ double x; char *s; - if (unpickler_read(self, &s, 8) < 0) + if (_Unpickler_Read(self, &s, 8) < 0) return -1; x = _PyFloat_Unpack8((unsigned char *)s, 0); @@ -3168,7 +3988,7 @@ Py_ssize_t len; char *s, *p; - if ((len = unpickler_readline(self, &s)) < 0) + if ((len = _Unpickler_Readline(self, &s)) < 0) return -1; if (len < 3) return bad_readline(); @@ -3218,7 +4038,7 @@ long x; char *s; - if (unpickler_read(self, &s, 4) < 0) + if (_Unpickler_Read(self, &s, 4) < 0) return -1; x = calc_binint(s, 4); @@ -3228,7 +4048,7 @@ return -1; } - if (unpickler_read(self, &s, x) < 0) + if (_Unpickler_Read(self, &s, x) < 0) return -1; bytes = PyBytes_FromStringAndSize(s, x); if (bytes == NULL) @@ -3245,12 +4065,12 @@ unsigned char x; char *s; - if (unpickler_read(self, &s, 1) < 0) + if (_Unpickler_Read(self, &s, 1) < 0) return -1; x = (unsigned char)s[0]; - if (unpickler_read(self, &s, x) < 0) + if (_Unpickler_Read(self, &s, x) < 0) return -1; bytes = PyBytes_FromStringAndSize(s, x); @@ -3268,7 +4088,7 @@ long x; char *s; - if (unpickler_read(self, &s, 4) < 0) + if (_Unpickler_Read(self, &s, 4) < 0) return -1; x = calc_binint(s, 4); @@ -3278,7 +4098,7 @@ return -1; } - if (unpickler_read(self, &s, x) < 0) + if (_Unpickler_Read(self, &s, x) < 0) return -1; /* Convert Python 2.x strings to unicode. */ @@ -3297,12 +4117,12 @@ unsigned char x; char *s; - if (unpickler_read(self, &s, 1) < 0) + if (_Unpickler_Read(self, &s, 1) < 0) return -1; x = (unsigned char)s[0]; - if (unpickler_read(self, &s, x) < 0) + if (_Unpickler_Read(self, &s, x) < 0) return -1; /* Convert Python 2.x strings to unicode. */ @@ -3321,7 +4141,7 @@ Py_ssize_t len; char *s; - if ((len = unpickler_readline(self, &s)) < 0) + if ((len = _Unpickler_Readline(self, &s)) < 0) return -1; if (len < 1) return bad_readline(); @@ -3341,7 +4161,7 @@ long size; char *s; - if (unpickler_read(self, &s, 4) < 0) + if (_Unpickler_Read(self, &s, 4) < 0) return -1; size = calc_binint(s, 4); @@ -3351,7 +4171,7 @@ return -1; } - if (unpickler_read(self, &s, size) < 0) + if (_Unpickler_Read(self, &s, size) < 0) return -1; str = PyUnicode_DecodeUTF8(s, size, "surrogatepass"); @@ -3445,7 +4265,7 @@ if ((i = marker(self)) < 0) return -1; - j = self->stack->length; + j = Py_SIZE(self->stack); if ((dict = PyDict_New()) == NULL) return -1; @@ -3521,7 +4341,7 @@ if ((i = marker(self)) < 0) return -1; - if ((len = unpickler_readline(self, &s)) < 0) + if ((len = _Unpickler_Readline(self, &s)) < 0) return -1; if (len < 2) return bad_readline(); @@ -3533,7 +4353,7 @@ if (module_name == NULL) return -1; - if ((len = unpickler_readline(self, &s)) >= 0) { + if ((len = _Unpickler_Readline(self, &s)) >= 0) { if (len < 2) return bad_readline(); class_name = PyUnicode_DecodeASCII(s, len - 1, "strict"); @@ -3619,7 +4439,7 @@ Py_ssize_t len; char *s; - if ((len = unpickler_readline(self, &s)) < 0) + if ((len = _Unpickler_Readline(self, &s)) < 0) return -1; if (len < 2) return bad_readline(); @@ -3627,7 +4447,7 @@ if (!module_name) return -1; - if ((len = unpickler_readline(self, &s)) >= 0) { + if ((len = _Unpickler_Readline(self, &s)) >= 0) { if (len < 2) { Py_DECREF(module_name); return bad_readline(); @@ -3654,7 +4474,7 @@ char *s; if (self->pers_func) { - if ((len = unpickler_readline(self, &s)) < 0) + if ((len = _Unpickler_Readline(self, &s)) < 0) return -1; if (len < 2) return bad_readline(); @@ -3663,9 +4483,9 @@ if (pid == NULL) return -1; - /* Ugh... this does not leak since unpickler_call() steals the + /* Ugh... this does not leak since _Unpickler_FastCall() steals the reference to pid first. */ - pid = unpickler_call(self, self->pers_func, pid); + pid = _Unpickler_FastCall(self, self->pers_func, pid); if (pid == NULL) return -1; @@ -3690,9 +4510,9 @@ if (pid == NULL) return -1; - /* Ugh... this does not leak since unpickler_call() steals the + /* Ugh... this does not leak since _Unpickler_FastCall() steals the reference to pid first. */ - pid = unpickler_call(self, self->pers_func, pid); + pid = _Unpickler_FastCall(self, self->pers_func, pid); if (pid == NULL) return -1; @@ -3710,7 +4530,7 @@ static int load_pop(UnpicklerObject *self) { - int len = self->stack->length; + int len = Py_SIZE(self->stack); /* Note that we split the (pickle.py) stack into two stacks, * an object stack and a mark stack. We have to be clever and @@ -3724,7 +4544,7 @@ } else if (len > 0) { len--; Py_DECREF(self->stack->data[len]); - self->stack->length = len; + Py_SIZE(self->stack) = len; } else { return stack_underflow(); } @@ -3750,7 +4570,7 @@ PyObject *last; int len; - if ((len = self->stack->length) <= 0) + if ((len = Py_SIZE(self->stack)) <= 0) return stack_underflow(); last = self->stack->data[len - 1]; PDATA_APPEND(self->stack, last, -1); @@ -3761,10 +4581,11 @@ load_get(UnpicklerObject *self) { PyObject *key, *value; + Py_ssize_t idx; Py_ssize_t len; char *s; - if ((len = unpickler_readline(self, &s)) < 0) + if ((len = _Unpickler_Readline(self, &s)) < 0) return -1; if (len < 2) return bad_readline(); @@ -3772,8 +4593,13 @@ key = PyLong_FromString(s, NULL, 10); if (key == NULL) return -1; + idx = PyLong_AsSsize_t(key); + if (idx == -1 && PyErr_Occurred()) { + Py_DECREF(key); + return -1; + } - value = PyDict_GetItemWithError(self->memo, key); + value = _Unpickler_MemoGet(self, idx); if (value == NULL) { if (!PyErr_Occurred()) PyErr_SetObject(PyExc_KeyError, key); @@ -3789,25 +4615,23 @@ static int load_binget(UnpicklerObject *self) { - PyObject *key, *value; + PyObject *value; + Py_ssize_t idx; char *s; - if (unpickler_read(self, &s, 1) < 0) + if (_Unpickler_Read(self, &s, 1) < 0) return -1; - /* Here, the unsigned cast is necessary to avoid negative values. */ - key = PyLong_FromLong((long)(unsigned char)s[0]); - if (key == NULL) - return -1; + idx = Py_CHARMASK(s[0]); - value = PyDict_GetItemWithError(self->memo, key); + value = _Unpickler_MemoGet(self, idx); if (value == NULL) { + PyObject *key = PyLong_FromSsize_t(idx); if (!PyErr_Occurred()) PyErr_SetObject(PyExc_KeyError, key); Py_DECREF(key); return -1; } - Py_DECREF(key); PDATA_APPEND(self->stack, value, -1); return 0; @@ -3816,30 +4640,26 @@ static int load_long_binget(UnpicklerObject *self) { - PyObject *key, *value; + PyObject *value; + Py_ssize_t idx; char *s; - long k; - if (unpickler_read(self, &s, 4) < 0) + if (_Unpickler_Read(self, &s, 4) < 0) return -1; - k = (long)(unsigned char)s[0]; - k |= (long)(unsigned char)s[1] << 8; - k |= (long)(unsigned char)s[2] << 16; - k |= (long)(unsigned char)s[3] << 24; + idx = (long)Py_CHARMASK(s[0]); + idx |= (long)Py_CHARMASK(s[1]) << 8; + idx |= (long)Py_CHARMASK(s[2]) << 16; + idx |= (long)Py_CHARMASK(s[3]) << 24; - key = PyLong_FromLong(k); - if (key == NULL) - return -1; - - value = PyDict_GetItemWithError(self->memo, key); + value = _Unpickler_MemoGet(self, idx); if (value == NULL) { + PyObject *key = PyLong_FromSsize_t(idx); if (!PyErr_Occurred()) PyErr_SetObject(PyExc_KeyError, key); Py_DECREF(key); return -1; } - Py_DECREF(key); PDATA_APPEND(self->stack, value, -1); return 0; @@ -3859,7 +4679,7 @@ PyObject *module_name, *class_name; assert(nbytes == 1 || nbytes == 2 || nbytes == 4); - if (unpickler_read(self, &codebytes, nbytes) < 0) + if (_Unpickler_Read(self, &codebytes, nbytes) < 0) return -1; code = calc_binint(codebytes, nbytes); if (code <= 0) { /* note that 0 is forbidden */ @@ -3920,75 +4740,68 @@ load_put(UnpicklerObject *self) { PyObject *key, *value; + Py_ssize_t idx; Py_ssize_t len; char *s; - int x; - if ((len = unpickler_readline(self, &s)) < 0) + if ((len = _Unpickler_Readline(self, &s)) < 0) return -1; if (len < 2) return bad_readline(); - if ((x = self->stack->length) <= 0) + if (Py_SIZE(self->stack) <= 0) return stack_underflow(); + value = self->stack->data[Py_SIZE(self->stack) - 1]; key = PyLong_FromString(s, NULL, 10); if (key == NULL) return -1; - value = self->stack->data[x - 1]; - - x = PyDict_SetItem(self->memo, key, value); + idx = PyLong_AsSsize_t(key); Py_DECREF(key); - return x; + if (idx == -1 && PyErr_Occurred()) + return -1; + + return _Unpickler_MemoPut(self, idx, value); } static int load_binput(UnpicklerObject *self) { - PyObject *key, *value; + PyObject *value; + Py_ssize_t idx; char *s; - int x; - if (unpickler_read(self, &s, 1) < 0) + if (_Unpickler_Read(self, &s, 1) < 0) return -1; - if ((x = self->stack->length) <= 0) + + if (Py_SIZE(self->stack) <= 0) return stack_underflow(); + value = self->stack->data[Py_SIZE(self->stack) - 1]; - key = PyLong_FromLong((long)(unsigned char)s[0]); - if (key == NULL) - return -1; - value = self->stack->data[x - 1]; + idx = Py_CHARMASK(s[0]); - x = PyDict_SetItem(self->memo, key, value); - Py_DECREF(key); - return x; + return _Unpickler_MemoPut(self, idx, value); } static int load_long_binput(UnpicklerObject *self) { - PyObject *key, *value; - long k; + PyObject *value; + Py_ssize_t idx; char *s; - int x; - if (unpickler_read(self, &s, 4) < 0) + if (_Unpickler_Read(self, &s, 4) < 0) return -1; - if ((x = self->stack->length) <= 0) - return stack_underflow(); - k = (long)(unsigned char)s[0]; - k |= (long)(unsigned char)s[1] << 8; - k |= (long)(unsigned char)s[2] << 16; - k |= (long)(unsigned char)s[3] << 24; + if (Py_SIZE(self->stack) <= 0) + return stack_underflow(); + value = self->stack->data[Py_SIZE(self->stack) - 1]; - key = PyLong_FromLong(k); - if (key == NULL) - return -1; - value = self->stack->data[x - 1]; + idx = (long)Py_CHARMASK(s[0]); + idx |= (long)Py_CHARMASK(s[1]) << 8; + idx |= (long)Py_CHARMASK(s[2]) << 16; + idx |= (long)Py_CHARMASK(s[3]) << 24; - x = PyDict_SetItem(self->memo, key, value); - Py_DECREF(key); - return x; + return _Unpickler_MemoPut(self, idx, value); } static int @@ -3998,7 +4811,7 @@ PyObject *list; int len, i; - len = self->stack->length; + len = Py_SIZE(self->stack); if (x > len || x <= 0) return stack_underflow(); if (len == x) /* nothing to do */ @@ -4028,15 +4841,15 @@ PyObject *result; value = self->stack->data[i]; - result = unpickler_call(self, append_func, value); + result = _Unpickler_FastCall(self, append_func, value); if (result == NULL) { Pdata_clear(self->stack, i + 1); - self->stack->length = x; + Py_SIZE(self->stack) = x; return -1; } Py_DECREF(result); } - self->stack->length = x; + Py_SIZE(self->stack) = x; } return 0; @@ -4045,7 +4858,7 @@ static int load_append(UnpicklerObject *self) { - return do_append(self, self->stack->length - 1); + return do_append(self, Py_SIZE(self->stack) - 1); } static int @@ -4062,7 +4875,7 @@ int len, i; int status = 0; - len = self->stack->length; + len = Py_SIZE(self->stack); if (x > len || x <= 0) return stack_underflow(); if (len == x) /* nothing to do */ @@ -4093,7 +4906,7 @@ static int load_setitem(UnpicklerObject *self) { - return do_setitems(self, self->stack->length - 2); + return do_setitems(self, Py_SIZE(self->stack) - 2); } static int @@ -4112,14 +4925,14 @@ /* Stack is ... instance, state. We want to leave instance at * the stack top, possibly mutated via instance.__setstate__(state). */ - if (self->stack->length < 2) + if (Py_SIZE(self->stack) < 2) return stack_underflow(); PDATA_POP(self->stack, state); if (state == NULL) return -1; - inst = self->stack->data[self->stack->length - 1]; + inst = self->stack->data[Py_SIZE(self->stack) - 1]; setstate = PyObject_GetAttrString(inst, "__setstate__"); if (setstate == NULL) { @@ -4134,9 +4947,9 @@ PyObject *result; /* The explicit __setstate__ is responsible for everything. */ - /* Ugh... this does not leak since unpickler_call() steals the + /* Ugh... this does not leak since _Unpickler_FastCall() steals the reference to state first. */ - result = unpickler_call(self, setstate, state); + result = _Unpickler_FastCall(self, setstate, state); Py_DECREF(setstate); if (result == NULL) return -1; @@ -4249,7 +5062,7 @@ self->marks_size = (Py_ssize_t)alloc; } - self->marks[self->num_marks++] = self->stack->length; + self->marks[self->num_marks++] = Py_SIZE(self->stack); return 0; } @@ -4287,7 +5100,7 @@ char *s; int i; - if (unpickler_read(self, &s, 1) < 0) + if (_Unpickler_Read(self, &s, 1) < 0) return -1; i = (unsigned char)s[0]; @@ -4308,7 +5121,7 @@ char *s; self->num_marks = 0; - if (self->stack->length) + if (Py_SIZE(self->stack)) Pdata_clear(self->stack, 0); /* Convenient macros for the dispatch while-switch loop just below. */ @@ -4319,7 +5132,7 @@ case opcode: if (load_func(self, (arg)) < 0) break; continue; while (1) { - if (unpickler_read(self, &s, 1) < 0) + if (_Unpickler_Read(self, &s, 1) < 0) break; switch ((enum opcode)s[0]) { @@ -4543,13 +5356,17 @@ PyObject_GC_UnTrack((PyObject *)self); Py_XDECREF(self->readline); Py_XDECREF(self->read); - Py_XDECREF(self->memo); Py_XDECREF(self->stack); Py_XDECREF(self->pers_func); Py_XDECREF(self->arg); - Py_XDECREF(self->last_string); + if (self->buffer.buf != NULL) { + PyBuffer_Release(&self->buffer); + self->buffer.buf = NULL; + } + _Unpickler_MemoCleanup(self); PyMem_Free(self->marks); + PyMem_Free(self->input_line); free(self->encoding); free(self->errors); @@ -4561,11 +5378,9 @@ { Py_VISIT(self->readline); Py_VISIT(self->read); - Py_VISIT(self->memo); Py_VISIT(self->stack); Py_VISIT(self->pers_func); Py_VISIT(self->arg); - Py_VISIT(self->last_string); return 0; } @@ -4574,14 +5389,19 @@ { Py_CLEAR(self->readline); Py_CLEAR(self->read); - Py_CLEAR(self->memo); Py_CLEAR(self->stack); Py_CLEAR(self->pers_func); Py_CLEAR(self->arg); - Py_CLEAR(self->last_string); + if (self->buffer.buf != NULL) { + PyBuffer_Release(&self->buffer); + self->buffer.buf = NULL; + } + _Unpickler_MemoCleanup(self); PyMem_Free(self->marks); self->marks = NULL; + PyMem_Free(self->input_line); + self->input_line = NULL; free(self->encoding); self->encoding = NULL; free(self->errors); @@ -4618,7 +5438,7 @@ { static char *kwlist[] = {"file", "fix_imports", "encoding", "errors", 0}; PyObject *file; - int fix_imports = 1; + PyObject *fix_imports = Py_True; char *encoding = NULL; char *errors = NULL; @@ -4637,7 +5457,7 @@ extra careful in the other Unpickler methods, since a subclass could forget to call Unpickler.__init__() thus breaking our internal invariants. */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iss:Unpickler", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oss:Unpickler", kwlist, &file, &fix_imports, &encoding, &errors)) return -1; @@ -4645,22 +5465,15 @@ if (self->read != NULL) (void)Unpickler_clear(self); - self->read = PyObject_GetAttrString(file, "read"); - self->readline = PyObject_GetAttrString(file, "readline"); - if (self->readline == NULL || self->read == NULL) + if (_Unpickler_SetInputStream(self, file) < 0) return -1; - if (encoding == NULL) - encoding = "ASCII"; - if (errors == NULL) - errors = "strict"; + if (_Unpickler_SetInputEncoding(self, encoding, errors) < 0) + return -1; - self->encoding = strdup(encoding); - self->errors = strdup(errors); - if (self->encoding == NULL || self->errors == NULL) { - PyErr_NoMemory(); + self->fix_imports = PyObject_IsTrue(fix_imports); + if (self->fix_imports == -1) return -1; - } if (PyObject_HasAttrString((PyObject *)self, "persistent_load")) { self->pers_func = PyObject_GetAttrString((PyObject *)self, @@ -4676,49 +5489,269 @@ if (self->stack == NULL) return -1; - self->memo = PyDict_New(); + self->memo_size = 32; + self->memo = _Unpickler_NewMemo(self->memo_size); if (self->memo == NULL) return -1; - self->last_string = NULL; self->arg = NULL; self->proto = 0; - self->fix_imports = fix_imports; return 0; } +/* Define a proxy object for the Unpickler's internal memo object. This is to + * avoid breaking code like: + * unpickler.memo.clear() + * and + * unpickler.memo = saved_memo + * Is this a good idea? Not really, but we don't want to break code that uses + * it. Note that we don't implement the entire mapping API here. This is + * intentional, as these should be treated as black-box implementation details. + * + * We do, however, have to implement pickling/unpickling support because of + * real-world code like cvs2svn. + */ + +typedef struct { + PyObject_HEAD + UnpicklerObject *unpickler; +} UnpicklerMemoProxyObject; + +PyDoc_STRVAR(ump_clear_doc, +"memo.clear() -> None. Remove all items from memo."); + +static PyObject * +ump_clear(UnpicklerMemoProxyObject *self) +{ + _Unpickler_MemoCleanup(self->unpickler); + self->unpickler->memo = _Unpickler_NewMemo(self->unpickler->memo_size); + if (self->unpickler->memo == NULL) + return NULL; + Py_RETURN_NONE; +} + +PyDoc_STRVAR(ump_copy_doc, +"memo.copy() -> new_memo. Copy the memo to a new object."); + +static PyObject * +ump_copy(UnpicklerMemoProxyObject *self) +{ + Py_ssize_t i; + PyObject *new_memo = PyDict_New(); + if (new_memo == NULL) + return NULL; + + for (i = 0; i < self->unpickler->memo_size; i++) { + int status; + PyObject *key, *value; + + value = self->unpickler->memo[i]; + if (value == NULL) + continue; + + key = PyLong_FromSsize_t(i); + if (key == NULL) + goto error; + status = PyDict_SetItem(new_memo, key, value); + Py_DECREF(key); + if (status < 0) + goto error; + } + return new_memo; + +error: + Py_DECREF(new_memo); + return NULL; +} + +PyDoc_STRVAR(ump_reduce_doc, +"memo.__reduce__(). Pickling support."); + +static PyObject * +ump_reduce(UnpicklerMemoProxyObject *self, PyObject *args) +{ + PyObject *reduce_value; + PyObject *constructor_args; + PyObject *contents = ump_copy(self); + if (contents == NULL) + return NULL; + + reduce_value = PyTuple_New(2); + if (reduce_value == NULL) { + Py_DECREF(contents); + return NULL; + } + constructor_args = PyTuple_New(1); + if (constructor_args == NULL) { + Py_DECREF(contents); + Py_DECREF(reduce_value); + return NULL; + } + PyTuple_SET_ITEM(constructor_args, 0, contents); + Py_INCREF((PyObject *)&PyDict_Type); + PyTuple_SET_ITEM(reduce_value, 0, (PyObject *)&PyDict_Type); + PyTuple_SET_ITEM(reduce_value, 1, constructor_args); + return reduce_value; +} + +static PyMethodDef unpicklerproxy_methods[] = { + {"clear", (PyCFunction)ump_clear, METH_NOARGS, ump_clear_doc}, + {"copy", (PyCFunction)ump_copy, METH_NOARGS, ump_copy_doc}, + {"__reduce__", (PyCFunction)ump_reduce, METH_VARARGS, ump_reduce_doc}, + {NULL, NULL} /* sentinel */ +}; + +static void +UnpicklerMemoProxy_dealloc(UnpicklerMemoProxyObject *self) +{ + PyObject_GC_UnTrack(self); + Py_XDECREF(self->unpickler); + PyObject_GC_Del((PyObject *)self); +} + +static int +UnpicklerMemoProxy_traverse(UnpicklerMemoProxyObject *self, + visitproc visit, void *arg) +{ + Py_VISIT(self->unpickler); + return 0; +} + +static int +UnpicklerMemoProxy_clear(UnpicklerMemoProxyObject *self) +{ + Py_CLEAR(self->unpickler); + return 0; +} + +static PyTypeObject UnpicklerMemoProxyType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pickle.UnpicklerMemoProxy", /*tp_name*/ + sizeof(UnpicklerMemoProxyObject), /*tp_basicsize*/ + 0, + (destructor)UnpicklerMemoProxy_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)PyObject_HashNotImplemented, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + 0, /* tp_doc */ + (traverseproc)UnpicklerMemoProxy_traverse, /* tp_traverse */ + (inquiry)UnpicklerMemoProxy_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + unpicklerproxy_methods, /* tp_methods */ +}; + +static PyObject * +UnpicklerMemoProxy_New(UnpicklerObject *unpickler) +{ + UnpicklerMemoProxyObject *self; + + self = PyObject_GC_New(UnpicklerMemoProxyObject, + &UnpicklerMemoProxyType); + if (self == NULL) + return NULL; + Py_INCREF(unpickler); + self->unpickler = unpickler; + PyObject_GC_Track(self); + return (PyObject *)self; +} + +/*****************************************************************************/ + + static PyObject * Unpickler_get_memo(UnpicklerObject *self) { - if (self->memo == NULL) - PyErr_SetString(PyExc_AttributeError, "memo"); - else - Py_INCREF(self->memo); - return self->memo; + return UnpicklerMemoProxy_New(self); } static int -Unpickler_set_memo(UnpicklerObject *self, PyObject *value) +Unpickler_set_memo(UnpicklerObject *self, PyObject *obj) { - PyObject *tmp; + PyObject **new_memo; + Py_ssize_t new_memo_size = 0; + Py_ssize_t i; - if (value == NULL) { + if (obj == NULL) { PyErr_SetString(PyExc_TypeError, "attribute deletion is not supported"); return -1; } - if (!PyDict_Check(value)) { - PyErr_SetString(PyExc_TypeError, "memo must be a dictionary"); + + if (Py_TYPE(obj) == &UnpicklerMemoProxyType) { + UnpicklerObject *unpickler = + ((UnpicklerMemoProxyObject *)obj)->unpickler; + + new_memo_size = unpickler->memo_size; + new_memo = _Unpickler_NewMemo(new_memo_size); + if (new_memo == NULL) + return -1; + + for (i = 0; i < new_memo_size; i++) { + Py_XINCREF(unpickler->memo[i]); + new_memo[i] = unpickler->memo[i]; + } + } + else if (PyDict_Check(obj)) { + Py_ssize_t i = 0; + PyObject *key, *value; + + new_memo_size = PyDict_Size(obj); + new_memo = _Unpickler_NewMemo(new_memo_size); + if (new_memo == NULL) + return -1; + + while (PyDict_Next(obj, &i, &key, &value)) { + Py_ssize_t idx; + if (!PyLong_Check(key)) { + PyErr_SetString(PyExc_TypeError, + "memo key must be integers"); + goto error; + } + idx = PyLong_AsSsize_t(key); + if (idx == -1 && PyErr_Occurred()) + goto error; + if (_Unpickler_MemoPut(self, idx, value) < 0) + goto error; + } + } + else { + PyErr_Format(PyExc_TypeError, + "'memo' attribute must be an UnpicklerMemoProxy object" + "or dict, not %.200s", Py_TYPE(obj)->tp_name); return -1; } - tmp = self->memo; - Py_INCREF(value); - self->memo = value; - Py_XDECREF(tmp); + _Unpickler_MemoCleanup(self); + self->memo_size = new_memo_size; + self->memo = new_memo; return 0; + + error: + if (new_memo_size) { + i = new_memo_size; + while (--i >= 0) { + Py_XDECREF(new_memo[i]); + } + PyMem_FREE(new_memo); + } + return -1; } static PyObject * @@ -4771,7 +5804,7 @@ (destructor)Unpickler_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ - 0, /*tp_setattr*/ + 0, /*tp_setattr*/ 0, /*tp_reserved*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ @@ -4806,6 +5839,280 @@ 0, /*tp_is_gc*/ }; +PyDoc_STRVAR(pickle_dump_doc, +"dump(obj, file, protocol=None, *, fix_imports=True) -> None\n" +"\n" +"Write a pickled representation of obj to the open file object file. This\n" +"is equivalent to ``Pickler(file, protocol).dump(obj)``, but may be more\n" +"efficient.\n" +"\n" +"The optional protocol argument tells the pickler to use the given protocol;\n" +"supported protocols are 0, 1, 2, 3. The default protocol is 3; a\n" +"backward-incompatible protocol designed for Python 3.0.\n" +"\n" +"Specifying a negative protocol version selects the highest protocol version\n" +"supported. The higher the protocol used, the more recent the version of\n" +"Python needed to read the pickle produced.\n" +"\n" +"The file argument must have a write() method that accepts a single bytes\n" +"argument. It can thus be a file object opened for binary writing, a\n" +"io.BytesIO instance, or any other custom object that meets this interface.\n" +"\n" +"If fix_imports is True and protocol is less than 3, pickle will try to\n" +"map the new Python 3.x names to the old module names used in Python 2.x,\n" +"so that the pickle data stream is readable with Python 2.x.\n"); + +static PyObject * +pickle_dump(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"obj", "file", "protocol", "fix_imports", 0}; + PyObject *obj; + PyObject *file; + PyObject *proto = NULL; + PyObject *fix_imports = Py_True; + PicklerObject *pickler; + + /* fix_imports is a keyword-only argument. */ + if (Py_SIZE(args) > 3) { + PyErr_Format(PyExc_TypeError, + "pickle.dump() takes at most 3 positional " + "argument (%zd given)", Py_SIZE(args)); + return NULL; + } + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:dump", kwlist, + &obj, &file, &proto, &fix_imports)) + return NULL; + + pickler = _Pickler_New(); + if (pickler == NULL) + return NULL; + + if (_Pickler_SetProtocol(pickler, proto, fix_imports) < 0) + goto error; + + if (_Pickler_SetOutputStream(pickler, file) < 0) + goto error; + + if (dump(pickler, obj) < 0) + goto error; + + if (_Pickler_FlushToFile(pickler) < 0) + goto error; + + Py_DECREF(pickler); + Py_RETURN_NONE; + + error: + Py_XDECREF(pickler); + return NULL; +} + +PyDoc_STRVAR(pickle_dumps_doc, +"dumps(obj, protocol=None, *, fix_imports=True) -> bytes\n" +"\n" +"Return the pickled representation of the object as a bytes\n" +"object, instead of writing it to a file.\n" +"\n" +"The optional protocol argument tells the pickler to use the given protocol;\n" +"supported protocols are 0, 1, 2, 3. The default protocol is 3; a\n" +"backward-incompatible protocol designed for Python 3.0.\n" +"\n" +"Specifying a negative protocol version selects the highest protocol version\n" +"supported. The higher the protocol used, the more recent the version of\n" +"Python needed to read the pickle produced.\n" +"\n" +"If fix_imports is True and *protocol* is less than 3, pickle will try to\n" +"map the new Python 3.x names to the old module names used in Python 2.x,\n" +"so that the pickle data stream is readable with Python 2.x.\n"); + +static PyObject * +pickle_dumps(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"obj", "protocol", "fix_imports", 0}; + PyObject *obj; + PyObject *proto = NULL; + PyObject *result; + PyObject *fix_imports = Py_True; + PicklerObject *pickler; + + /* fix_imports is a keyword-only argument. */ + if (Py_SIZE(args) > 2) { + PyErr_Format(PyExc_TypeError, + "pickle.dumps() takes at most 2 positional " + "argument (%zd given)", Py_SIZE(args)); + return NULL; + } + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO:dumps", kwlist, + &obj, &proto, &fix_imports)) + return NULL; + + pickler = _Pickler_New(); + if (pickler == NULL) + return NULL; + + if (_Pickler_SetProtocol(pickler, proto, fix_imports) < 0) + goto error; + + if (dump(pickler, obj) < 0) + goto error; + + result = _Pickler_GetString(pickler); + Py_DECREF(pickler); + return result; + + error: + Py_XDECREF(pickler); + return NULL; +} + +PyDoc_STRVAR(pickle_load_doc, +"load(file, *, fix_imports=True, encoding='ASCII', errors='strict') -> object\n" +"\n" +"Read a pickled object representation from the open file object file and\n" +"return the reconstituted object hierarchy specified therein. This is\n" +"equivalent to ``Unpickler(file).load()``, but may be more efficient.\n" +"\n" +"The protocol version of the pickle is detected automatically, so no protocol\n" +"argument is needed. Bytes past the pickled object's representation are\n" +"ignored.\n" +"\n" +"The argument file must have two methods, a read() method that takes an\n" +"integer argument, and a readline() method that requires no arguments. Both\n" +"methods should return bytes. Thus *file* can be a binary file object opened\n" +"for reading, a BytesIO object, or any other custom object that meets this\n" +"interface.\n" +"\n" +"Optional keyword arguments are fix_imports, encoding and errors,\n" +"which are used to control compatiblity support for pickle stream generated\n" +"by Python 2.x. If fix_imports is True, pickle will try to map the old\n" +"Python 2.x names to the new names used in Python 3.x. The encoding and\n" +"errors tell pickle how to decode 8-bit string instances pickled by Python\n" +"2.x; these default to 'ASCII' and 'strict', respectively.\n"); + +static PyObject * +pickle_load(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"file", "fix_imports", "encoding", "errors", 0}; + PyObject *file; + PyObject *fix_imports = Py_True; + PyObject *result; + char *encoding = NULL; + char *errors = NULL; + UnpicklerObject *unpickler; + + /* fix_imports, encoding and errors are a keyword-only argument. */ + if (Py_SIZE(args) != 1) { + PyErr_Format(PyExc_TypeError, + "pickle.load() takes exactly one positional " + "argument (%zd given)", Py_SIZE(args)); + return NULL; + } + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oss:load", kwlist, + &file, &fix_imports, &encoding, &errors)) + return NULL; + + unpickler = _Unpickler_New(); + if (unpickler == NULL) + return NULL; + + if (_Unpickler_SetInputStream(unpickler, file) < 0) + goto error; + + if (_Unpickler_SetInputEncoding(unpickler, encoding, errors) < 0) + goto error; + + unpickler->fix_imports = PyObject_IsTrue(fix_imports); + if (unpickler->fix_imports == -1) + goto error; + + result = load(unpickler); + Py_DECREF(unpickler); + return result; + + error: + Py_XDECREF(unpickler); + return NULL; +} + +PyDoc_STRVAR(pickle_loads_doc, +"loads(input, *, fix_imports=True, encoding='ASCII', errors='strict') -> object\n" +"\n" +"Read a pickled object hierarchy from a bytes object and return the\n" +"reconstituted object hierarchy specified therein\n" +"\n" +"The protocol version of the pickle is detected automatically, so no protocol\n" +"argument is needed. Bytes past the pickled object's representation are\n" +"ignored.\n" +"\n" +"Optional keyword arguments are fix_imports, encoding and errors, which\n" +"are used to control compatiblity support for pickle stream generated\n" +"by Python 2.x. If fix_imports is True, pickle will try to map the old\n" +"Python 2.x names to the new names used in Python 3.x. The encoding and\n" +"errors tell pickle how to decode 8-bit string instances pickled by Python\n" +"2.x; these default to 'ASCII' and 'strict', respectively.\n"); + +static PyObject * +pickle_loads(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"input", "fix_imports", "encoding", "errors", 0}; + PyObject *input; + PyObject *fix_imports = Py_True; + PyObject *result; + char *encoding = NULL; + char *errors = NULL; + UnpicklerObject *unpickler; + + /* fix_imports, encoding and errors are a keyword-only argument. */ + if (Py_SIZE(args) != 1) { + PyErr_Format(PyExc_TypeError, + "pickle.loads() takes exactly one positional " + "argument (%zd given)", Py_SIZE(args)); + return NULL; + } + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oss:loads", kwlist, + &input, &fix_imports, &encoding, &errors)) + return NULL; + + unpickler = _Unpickler_New(); + if (unpickler == NULL) + return NULL; + + if (_Unpickler_SetStringInput(unpickler, input) < 0) + goto error; + + if (_Unpickler_SetInputEncoding(unpickler, encoding, errors) < 0) + goto error; + + unpickler->fix_imports = PyObject_IsTrue(fix_imports); + if (unpickler->fix_imports == -1) + goto error; + + result = load(unpickler); + Py_DECREF(unpickler); + return result; + + error: + Py_XDECREF(unpickler); + return NULL; +} + + +static struct PyMethodDef pickle_methods[] = { + {"dump", (PyCFunction)pickle_dump, METH_VARARGS|METH_KEYWORDS, + pickle_dump_doc}, + {"dumps", (PyCFunction)pickle_dumps, METH_VARARGS|METH_KEYWORDS, + pickle_dumps_doc}, + {"load", (PyCFunction)pickle_load, METH_VARARGS|METH_KEYWORDS, + pickle_load_doc}, + {"loads", (PyCFunction)pickle_loads, METH_VARARGS|METH_KEYWORDS, + pickle_loads_doc}, + {NULL, NULL} /* sentinel */ +}; + static int initmodule(void) { @@ -4915,7 +6222,7 @@ "_pickle", pickle_module_doc, -1, - NULL, + pickle_methods, NULL, NULL, NULL, @@ -4933,6 +6240,10 @@ return NULL; if (PyType_Ready(&Pdata_Type) < 0) return NULL; + if (PyType_Ready(&PicklerMemoProxyType) < 0) + return NULL; + if (PyType_Ready(&UnpicklerMemoProxyType) < 0) + return NULL; /* Create the module and add the functions. */ m = PyModule_Create(&_picklemodule); From python-checkins at python.org Thu Sep 9 21:57:17 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Thu, 9 Sep 2010 21:57:17 +0200 (CEST) Subject: [Python-checkins] r84654 - in python/branches/release31-maint: PCbuild/_multiprocessing.vcproj PCbuild/pythoncore.vcproj Message-ID: <20100909195717.4A5D7FAEF@mail.python.org> Author: hirokazu.yamamoto Date: Thu Sep 9 21:57:17 2010 New Revision: 84654 Log: Merged revisions 84645 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84645 | hirokazu.yamamoto | 2010-09-09 15:24:43 +0900 | 5 lines PCBuild cosmetic fixes. * pythoncore.vcproj: Fixed indentation * _multiprocessing.vcproj: Converted ProjectGUID to uppercase. Otherwise, VS8 _multiprocessing.vcproj created by vs9to8.py was modified every time loads it in VS8 IDE. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/PCbuild/_multiprocessing.vcproj python/branches/release31-maint/PCbuild/pythoncore.vcproj Modified: python/branches/release31-maint/PCbuild/_multiprocessing.vcproj ============================================================================== --- python/branches/release31-maint/PCbuild/_multiprocessing.vcproj (original) +++ python/branches/release31-maint/PCbuild/_multiprocessing.vcproj Thu Sep 9 21:57:17 2010 @@ -3,7 +3,7 @@ ProjectType="Visual C++" Version="9,00" Name="_multiprocessing" - ProjectGUID="{9e48b300-37d1-11dd-8c41-005056c00008}" + ProjectGUID="{9E48B300-37D1-11DD-8C41-005056C00008}" RootNamespace="_multiprocessing" Keyword="Win32Proj" TargetFrameworkVersion="196613" Modified: python/branches/release31-maint/PCbuild/pythoncore.vcproj ============================================================================== --- python/branches/release31-maint/PCbuild/pythoncore.vcproj (original) +++ python/branches/release31-maint/PCbuild/pythoncore.vcproj Thu Sep 9 21:57:17 2010 @@ -846,7 +846,10 @@ RelativePath="..\Include\pyarena.h" > - + + @@ -1375,10 +1378,10 @@ RelativePath="..\Objects\bytesobject.c" > - - + + From python-checkins at python.org Thu Sep 9 22:30:24 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 9 Sep 2010 22:30:24 +0200 (CEST) Subject: [Python-checkins] r84655 - in python/branches/py3k: Lib/test/test_builtin.py Lib/test/test_codeccallbacks.py Misc/NEWS Python/codecs.c Message-ID: <20100909203024.17989F538@mail.python.org> Author: antoine.pitrou Date: Thu Sep 9 22:30:23 2010 New Revision: 84655 Log: Issue #9804: ascii() now always represents unicode surrogate pairs as a single `\UXXXXXXXX`, regardless of whether the character is printable or not. Also, the "backslashreplace" error handler now joins surrogate pairs into a single character on UCS-2 builds. Modified: python/branches/py3k/Lib/test/test_builtin.py python/branches/py3k/Lib/test/test_codeccallbacks.py python/branches/py3k/Misc/NEWS python/branches/py3k/Python/codecs.c Modified: python/branches/py3k/Lib/test/test_builtin.py ============================================================================== --- python/branches/py3k/Lib/test/test_builtin.py (original) +++ python/branches/py3k/Lib/test/test_builtin.py Thu Sep 9 22:30:23 2010 @@ -179,6 +179,28 @@ a = {} a[0] = a self.assertEqual(ascii(a), '{0: {...}}') + # Advanced checks for unicode strings + def _check_uni(s): + self.assertEqual(ascii(s), repr(s)) + _check_uni("'") + _check_uni('"') + _check_uni('"\'') + _check_uni('\0') + _check_uni('\r\n\t .') + # Unprintable non-ASCII characters + _check_uni('\x85') + _check_uni('\u1fff') + _check_uni('\U00012fff') + # Lone surrogates + _check_uni('\ud800') + _check_uni('\udfff') + # Issue #9804: surrogates should be joined even for printable + # wide characters (UCS-2 builds). + self.assertEqual(ascii('\U0001d121'), "'\\U0001d121'") + # All together + s = "'\0\"\n\r\t abcd\x85?\U00012fff\uD800\U0001D121xxx." + self.assertEqual(ascii(s), + r"""'\'\x00"\n\r\t abcd\x85\xe9\U00012fff\ud800\U0001d121xxx.'""") def test_neg(self): x = -sys.maxsize-1 Modified: python/branches/py3k/Lib/test/test_codeccallbacks.py ============================================================================== --- python/branches/py3k/Lib/test/test_codeccallbacks.py (original) +++ python/branches/py3k/Lib/test/test_codeccallbacks.py Thu Sep 9 22:30:23 2010 @@ -577,17 +577,31 @@ UnicodeEncodeError("ascii", "\uffff", 0, 1, "ouch")), ("\\uffff", 1) ) - if sys.maxunicode>0xffff: - self.assertEquals( - codecs.backslashreplace_errors( - UnicodeEncodeError("ascii", "\U00010000", 0, 1, "ouch")), - ("\\U00010000", 1) - ) - self.assertEquals( - codecs.backslashreplace_errors( - UnicodeEncodeError("ascii", "\U0010ffff", 0, 1, "ouch")), - ("\\U0010ffff", 1) - ) + # 1 on UCS-4 builds, 2 on UCS-2 + len_wide = len("\U00010000") + self.assertEquals( + codecs.backslashreplace_errors( + UnicodeEncodeError("ascii", "\U00010000", + 0, len_wide, "ouch")), + ("\\U00010000", len_wide) + ) + self.assertEquals( + codecs.backslashreplace_errors( + UnicodeEncodeError("ascii", "\U0010ffff", + 0, len_wide, "ouch")), + ("\\U0010ffff", len_wide) + ) + # Lone surrogates (regardless of unicode width) + self.assertEquals( + codecs.backslashreplace_errors( + UnicodeEncodeError("ascii", "\ud800", 0, 1, "ouch")), + ("\\ud800", 1) + ) + self.assertEquals( + codecs.backslashreplace_errors( + UnicodeEncodeError("ascii", "\udfff", 0, 1, "ouch")), + ("\\udfff", 1) + ) def test_badhandlerresults(self): results = ( 42, "foo", (1,2,3), ("foo", 1, 3), ("foo", None), ("foo",), ("foo", 1, 3), ("foo", None), ("foo",) ) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Sep 9 22:30:23 2010 @@ -10,6 +10,11 @@ Core and Builtins ----------------- +- Issue #9804: ascii() now always represents unicode surrogate pairs as + a single ``\UXXXXXXXX``, regardless of whether the character is printable + or not. Also, the "backslashreplace" error handler now joins surrogate + pairs into a single character on UCS-2 builds. + - Issue #9757: memoryview objects get a release() method to release the underlying buffer (previously this was only done when deallocating the memoryview), and gain support for the context management protocol. Modified: python/branches/py3k/Python/codecs.c ============================================================================== --- python/branches/py3k/Python/codecs.c (original) +++ python/branches/py3k/Python/codecs.c Thu Sep 9 22:30:23 2010 @@ -678,6 +678,13 @@ PyObject *PyCodec_BackslashReplaceErrors(PyObject *exc) { +#ifndef Py_UNICODE_WIDE +#define IS_SURROGATE_PAIR(p, end) \ + (*p >= 0xD800 && *p <= 0xDBFF && (p + 1) < end && \ + *(p + 1) >= 0xDC00 && *(p + 1) <= 0xDFFF) +#else +#define IS_SURROGATE_PAIR(p, end) 0 +#endif if (PyObject_IsInstance(exc, PyExc_UnicodeEncodeError)) { PyObject *restuple; PyObject *object; @@ -702,7 +709,12 @@ else #endif if (*p >= 0x100) { - ressize += 1+1+4; + if (IS_SURROGATE_PAIR(p, startp+end)) { + ressize += 1+1+8; + ++p; + } + else + ressize += 1+1+4; } else ressize += 1+1+2; @@ -712,9 +724,12 @@ return NULL; for (p = startp+start, outp = PyUnicode_AS_UNICODE(res); p < startp+end; ++p) { - Py_UNICODE c = *p; + Py_UCS4 c = (Py_UCS4) *p; *outp++ = '\\'; -#ifdef Py_UNICODE_WIDE + if (IS_SURROGATE_PAIR(p, startp+end)) { + c = ((*p & 0x3FF) << 10) + (*(p + 1) & 0x3FF) + 0x10000; + ++p; + } if (c >= 0x00010000) { *outp++ = 'U'; *outp++ = hexdigits[(c>>28)&0xf]; @@ -724,9 +739,7 @@ *outp++ = hexdigits[(c>>12)&0xf]; *outp++ = hexdigits[(c>>8)&0xf]; } - else -#endif - if (c >= 0x100) { + else if (c >= 0x100) { *outp++ = 'u'; *outp++ = hexdigits[(c>>12)&0xf]; *outp++ = hexdigits[(c>>8)&0xf]; @@ -746,6 +759,7 @@ wrong_exception_type(exc); return NULL; } +#undef IS_SURROGATE_PAIR } /* This handler is declared static until someone demonstrates From python-checkins at python.org Thu Sep 9 22:33:43 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 9 Sep 2010 22:33:43 +0200 (CEST) Subject: [Python-checkins] r84656 - in python/branches/release31-maint: Lib/test/test_builtin.py Lib/test/test_codeccallbacks.py Misc/NEWS Python/codecs.c Message-ID: <20100909203343.99E0BF538@mail.python.org> Author: antoine.pitrou Date: Thu Sep 9 22:33:43 2010 New Revision: 84656 Log: Merged revisions 84655 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84655 | antoine.pitrou | 2010-09-09 22:30:23 +0200 (jeu., 09 sept. 2010) | 6 lines Issue #9804: ascii() now always represents unicode surrogate pairs as a single `\UXXXXXXXX`, regardless of whether the character is printable or not. Also, the "backslashreplace" error handler now joins surrogate pairs into a single character on UCS-2 builds. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_builtin.py python/branches/release31-maint/Lib/test/test_codeccallbacks.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Python/codecs.c Modified: python/branches/release31-maint/Lib/test/test_builtin.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_builtin.py (original) +++ python/branches/release31-maint/Lib/test/test_builtin.py Thu Sep 9 22:33:43 2010 @@ -174,6 +174,28 @@ a = {} a[0] = a self.assertEqual(ascii(a), '{0: {...}}') + # Advanced checks for unicode strings + def _check_uni(s): + self.assertEqual(ascii(s), repr(s)) + _check_uni("'") + _check_uni('"') + _check_uni('"\'') + _check_uni('\0') + _check_uni('\r\n\t .') + # Unprintable non-ASCII characters + _check_uni('\x85') + _check_uni('\u1fff') + _check_uni('\U00012fff') + # Lone surrogates + _check_uni('\ud800') + _check_uni('\udfff') + # Issue #9804: surrogates should be joined even for printable + # wide characters (UCS-2 builds). + self.assertEqual(ascii('\U0001d121'), "'\\U0001d121'") + # All together + s = "'\0\"\n\r\t abcd\x85?\U00012fff\uD800\U0001D121xxx." + self.assertEqual(ascii(s), + r"""'\'\x00"\n\r\t abcd\x85\xe9\U00012fff\ud800\U0001d121xxx.'""") def test_neg(self): x = -sys.maxsize-1 Modified: python/branches/release31-maint/Lib/test/test_codeccallbacks.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_codeccallbacks.py (original) +++ python/branches/release31-maint/Lib/test/test_codeccallbacks.py Thu Sep 9 22:33:43 2010 @@ -577,17 +577,31 @@ UnicodeEncodeError("ascii", "\uffff", 0, 1, "ouch")), ("\\uffff", 1) ) - if sys.maxunicode>0xffff: - self.assertEquals( - codecs.backslashreplace_errors( - UnicodeEncodeError("ascii", "\U00010000", 0, 1, "ouch")), - ("\\U00010000", 1) - ) - self.assertEquals( - codecs.backslashreplace_errors( - UnicodeEncodeError("ascii", "\U0010ffff", 0, 1, "ouch")), - ("\\U0010ffff", 1) - ) + # 1 on UCS-4 builds, 2 on UCS-2 + len_wide = len("\U00010000") + self.assertEquals( + codecs.backslashreplace_errors( + UnicodeEncodeError("ascii", "\U00010000", + 0, len_wide, "ouch")), + ("\\U00010000", len_wide) + ) + self.assertEquals( + codecs.backslashreplace_errors( + UnicodeEncodeError("ascii", "\U0010ffff", + 0, len_wide, "ouch")), + ("\\U0010ffff", len_wide) + ) + # Lone surrogates (regardless of unicode width) + self.assertEquals( + codecs.backslashreplace_errors( + UnicodeEncodeError("ascii", "\ud800", 0, 1, "ouch")), + ("\\ud800", 1) + ) + self.assertEquals( + codecs.backslashreplace_errors( + UnicodeEncodeError("ascii", "\udfff", 0, 1, "ouch")), + ("\\udfff", 1) + ) def test_badhandlerresults(self): results = ( 42, "foo", (1,2,3), ("foo", 1, 3), ("foo", None), ("foo",), ("foo", 1, 3), ("foo", None), ("foo",) ) Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Thu Sep 9 22:33:43 2010 @@ -12,6 +12,11 @@ Core and Builtins ----------------- +- Issue #9804: ascii() now always represents unicode surrogate pairs as + a single ``\UXXXXXXXX``, regardless of whether the character is printable + or not. Also, the "backslashreplace" error handler now joins surrogate + pairs into a single character on UCS-2 builds. + - Issue #9797: pystate.c wrongly assumed that zero couldn't be a valid thread-local storage key. Modified: python/branches/release31-maint/Python/codecs.c ============================================================================== --- python/branches/release31-maint/Python/codecs.c (original) +++ python/branches/release31-maint/Python/codecs.c Thu Sep 9 22:33:43 2010 @@ -678,6 +678,13 @@ PyObject *PyCodec_BackslashReplaceErrors(PyObject *exc) { +#ifndef Py_UNICODE_WIDE +#define IS_SURROGATE_PAIR(p, end) \ + (*p >= 0xD800 && *p <= 0xDBFF && (p + 1) < end && \ + *(p + 1) >= 0xDC00 && *(p + 1) <= 0xDFFF) +#else +#define IS_SURROGATE_PAIR(p, end) 0 +#endif if (PyObject_IsInstance(exc, PyExc_UnicodeEncodeError)) { PyObject *restuple; PyObject *object; @@ -702,7 +709,12 @@ else #endif if (*p >= 0x100) { - ressize += 1+1+4; + if (IS_SURROGATE_PAIR(p, startp+end)) { + ressize += 1+1+8; + ++p; + } + else + ressize += 1+1+4; } else ressize += 1+1+2; @@ -712,9 +724,12 @@ return NULL; for (p = startp+start, outp = PyUnicode_AS_UNICODE(res); p < startp+end; ++p) { - Py_UNICODE c = *p; + Py_UCS4 c = (Py_UCS4) *p; *outp++ = '\\'; -#ifdef Py_UNICODE_WIDE + if (IS_SURROGATE_PAIR(p, startp+end)) { + c = ((*p & 0x3FF) << 10) + (*(p + 1) & 0x3FF) + 0x10000; + ++p; + } if (c >= 0x00010000) { *outp++ = 'U'; *outp++ = hexdigits[(c>>28)&0xf]; @@ -724,9 +739,7 @@ *outp++ = hexdigits[(c>>12)&0xf]; *outp++ = hexdigits[(c>>8)&0xf]; } - else -#endif - if (c >= 0x100) { + else if (c >= 0x100) { *outp++ = 'u'; *outp++ = hexdigits[(c>>12)&0xf]; *outp++ = hexdigits[(c>>8)&0xf]; @@ -746,6 +759,7 @@ wrong_exception_type(exc); return NULL; } +#undef IS_SURROGATE_PAIR } /* This handler is declared static until someone demonstrates From python-checkins at python.org Thu Sep 9 22:40:29 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 9 Sep 2010 22:40:29 +0200 (CEST) Subject: [Python-checkins] r84657 - python/branches/py3k/Lib/test/test_gdb.py Message-ID: <20100909204029.0FE01F80D@mail.python.org> Author: antoine.pitrou Date: Thu Sep 9 22:40:28 2010 New Revision: 84657 Log: Remove workaround Modified: python/branches/py3k/Lib/test/test_gdb.py Modified: python/branches/py3k/Lib/test/test_gdb.py ============================================================================== --- python/branches/py3k/Lib/test/test_gdb.py (original) +++ python/branches/py3k/Lib/test/test_gdb.py Thu Sep 9 22:40:28 2010 @@ -234,9 +234,7 @@ text.encode(encoding) printable = True except UnicodeEncodeError: - # Workaround ascii() bug on UCS-2 builds: issue #9804 - asc = "'" + text.encode('unicode-escape').decode('ascii') + "'" - self.assertGdbRepr(text, asc) + self.assertGdbRepr(text, ascii(text)) else: self.assertGdbRepr(text) From python-checkins at python.org Thu Sep 9 23:17:58 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Thu, 9 Sep 2010 23:17:58 +0200 (CEST) Subject: [Python-checkins] r84658 - python/branches/py3k/Lib/test/test_socket.py Message-ID: <20100909211758.C2B66F882@mail.python.org> Author: daniel.stutzbach Date: Thu Sep 9 23:17:58 2010 New Revision: 84658 Log: Skip socket tests that require the network, if the network resource is not enabled Modified: python/branches/py3k/Lib/test/test_socket.py Modified: python/branches/py3k/Lib/test/test_socket.py ============================================================================== --- python/branches/py3k/Lib/test/test_socket.py (original) +++ python/branches/py3k/Lib/test/test_socket.py Thu Sep 9 23:17:58 2010 @@ -642,6 +642,8 @@ # only IP addresses are allowed self.assertRaises(socket.error, socket.getnameinfo, ('mail.python.org',0), 0) + @unittest.skipUnless(support.is_resource_enabled('network'), + 'network is not enabled') def test_idna(self): support.requires('network') # these should all be successful From python-checkins at python.org Thu Sep 9 23:18:04 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Thu, 9 Sep 2010 23:18:04 +0200 (CEST) Subject: [Python-checkins] r84659 - python/branches/py3k/Python/import.c Message-ID: <20100909211804.A5AE9FA9F@mail.python.org> Author: daniel.stutzbach Date: Thu Sep 9 23:18:04 2010 New Revision: 84659 Log: Fix Issue #9752: MSVC compiler warning due to undefined function (Patch by Jon Anglin) Modified: python/branches/py3k/Python/import.c Modified: python/branches/py3k/Python/import.c ============================================================================== --- python/branches/py3k/Python/import.c (original) +++ python/branches/py3k/Python/import.c Thu Sep 9 23:18:04 2010 @@ -25,6 +25,8 @@ #ifdef MS_WINDOWS /* for stat.st_mode */ typedef unsigned short mode_t; +/* for _mkdir */ +#include #endif @@ -1134,9 +1136,6 @@ time_t mtime = srcstat->st_mtime; #ifdef MS_WINDOWS /* since Windows uses different permissions */ mode_t mode = srcstat->st_mode & ~S_IEXEC; - mode_t dirmode = srcstat->st_mode | S_IEXEC; /* XXX Is this correct - for Windows? - 2010-04-07 BAW */ #else mode_t mode = srcstat->st_mode & ~S_IXUSR & ~S_IXGRP & ~S_IXOTH; mode_t dirmode = (srcstat->st_mode | @@ -1156,8 +1155,12 @@ } saved = *dirpath; *dirpath = '\0'; - /* XXX call os.mkdir() or maybe CreateDirectoryA() on Windows? */ + +#ifdef MS_WINDOWS + if (_mkdir(cpathname) < 0 && errno != EEXIST) { +#else if (mkdir(cpathname, dirmode) < 0 && errno != EEXIST) { +#endif *dirpath = saved; if (Py_VerboseFlag) PySys_WriteStderr( From python-checkins at python.org Fri Sep 10 04:59:56 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Fri, 10 Sep 2010 04:59:56 +0200 (CEST) Subject: [Python-checkins] r84660 - in python/branches/release31-maint: Misc/ACKS PC/VS7.1/pythoncore.vcproj Message-ID: <20100910025956.3C5C7EE987@mail.python.org> Author: hirokazu.yamamoto Date: Fri Sep 10 04:59:55 2010 New Revision: 84660 Log: Merged revisions 84644 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84644 | hirokazu.yamamoto | 2010-09-09 15:14:23 +0900 | 1 line Updated VS7.1 project file. (I cannot test this file because I don't have VS7.1) ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/ACKS python/branches/release31-maint/PC/VS7.1/pythoncore.vcproj Modified: python/branches/release31-maint/Misc/ACKS ============================================================================== --- python/branches/release31-maint/Misc/ACKS (original) +++ python/branches/release31-maint/Misc/ACKS Fri Sep 10 04:59:55 2010 @@ -857,6 +857,7 @@ Thomas Wouters Heiko Wundram Doug Wyatt +Hirokazu Yamamoto Ka-Ping Yee Bob Yodlowski Danny Yoo Modified: python/branches/release31-maint/PC/VS7.1/pythoncore.vcproj ============================================================================== --- python/branches/release31-maint/PC/VS7.1/pythoncore.vcproj (original) +++ python/branches/release31-maint/PC/VS7.1/pythoncore.vcproj Fri Sep 10 04:59:55 2010 @@ -466,6 +466,9 @@ RelativePath="..\..\Objects\boolobject.c"> + + - - py3k results for svn r84659 (hg cset aa138a707c1b) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogvcjP95', '-x'] From python-checkins at python.org Fri Sep 10 05:08:24 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Fri, 10 Sep 2010 05:08:24 +0200 (CEST) Subject: [Python-checkins] r84661 - in python/branches/release27-maint/PCbuild: _multiprocessing.vcproj pythoncore.vcproj Message-ID: <20100910030824.5ECD7EE9BD@mail.python.org> Author: hirokazu.yamamoto Date: Fri Sep 10 05:08:24 2010 New Revision: 84661 Log: PCBuild cosmetic fixes. * pythoncore.vcproj: Removed doubled entries, and sorted entries a bit. * _multiprocessing.vcproj: Converted ProjectGUID to uppercase. Otherwise, VS8 _multiprocessing.vcproj created by vs9to8.py was modified every time loads it in VS8 IDE. Modified: python/branches/release27-maint/PCbuild/_multiprocessing.vcproj python/branches/release27-maint/PCbuild/pythoncore.vcproj Modified: python/branches/release27-maint/PCbuild/_multiprocessing.vcproj ============================================================================== --- python/branches/release27-maint/PCbuild/_multiprocessing.vcproj (original) +++ python/branches/release27-maint/PCbuild/_multiprocessing.vcproj Fri Sep 10 05:08:24 2010 @@ -3,7 +3,7 @@ ProjectType="Visual C++" Version="9,00" Name="_multiprocessing" - ProjectGUID="{9e48b300-37d1-11dd-8c41-005056c00008}" + ProjectGUID="{9E48B300-37D1-11DD-8C41-005056C00008}" RootNamespace="_multiprocessing" Keyword="Win32Proj" TargetFrameworkVersion="196613" Modified: python/branches/release27-maint/PCbuild/pythoncore.vcproj ============================================================================== --- python/branches/release27-maint/PCbuild/pythoncore.vcproj (original) +++ python/branches/release27-maint/PCbuild/pythoncore.vcproj Fri Sep 10 05:08:24 2010 @@ -683,10 +683,6 @@ > - - @@ -719,6 +715,10 @@ > + + @@ -911,10 +911,6 @@ > - - @@ -1423,10 +1419,6 @@ > - - @@ -1443,10 +1435,6 @@ > - - @@ -1559,6 +1547,10 @@ > + + @@ -1719,6 +1711,10 @@ > + + @@ -1831,10 +1827,6 @@ > - - From python-checkins at python.org Fri Sep 10 05:09:38 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Fri, 10 Sep 2010 05:09:38 +0200 (CEST) Subject: [Python-checkins] r84662 - in python/branches/release27-maint/PC/VC6: readme.txt tcl852.patch Message-ID: <20100910030938.BDC7CEE98A@mail.python.org> Author: hirokazu.yamamoto Date: Fri Sep 10 05:09:38 2010 New Revision: 84662 Log: Updated VC6 files. * readme.txt: Added PSDK as requirement. * tcl852.patch: fixed patch. it was doubled. Modified: python/branches/release27-maint/PC/VC6/readme.txt python/branches/release27-maint/PC/VC6/tcl852.patch Modified: python/branches/release27-maint/PC/VC6/readme.txt ============================================================================== --- python/branches/release27-maint/PC/VC6/readme.txt (original) +++ python/branches/release27-maint/PC/VC6/readme.txt Fri Sep 10 05:09:38 2010 @@ -1,7 +1,8 @@ Building Python using VC++ 6.0 or 5.0 ------------------------------------- This directory is used to build Python for Win32 platforms, e.g. Windows -2000 and XP. It requires Microsoft Visual C++ 6.x or 5.x. +2000 and XP. It requires Microsoft Visual C++ 6.x or 5.x and Platform +SDK February 2003 Edition (Core SDK). (For other Windows platforms and compilers, see ../readme.txt.) All you need to do is open the workspace "pcbuild.dsw" in MSVC++, select @@ -39,7 +40,6 @@ pythonw.exe, a variant of python.exe that doesn't pop up a DOS box _msi _msi.c. You need to install Windows Installer SDK to build this module. - http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdk-full.htm _socket socketmodule.c _testcapi Modified: python/branches/release27-maint/PC/VC6/tcl852.patch ============================================================================== --- python/branches/release27-maint/PC/VC6/tcl852.patch (original) +++ python/branches/release27-maint/PC/VC6/tcl852.patch Fri Sep 10 05:09:38 2010 @@ -9,14 +9,3 @@ typedef struct _stati64 Tcl_StatBuf; # else typedef struct _stat64 Tcl_StatBuf; ---- tcl8.5.2\generic\tcl.h Fri Jun 13 03:35:39 2008 -+++ tcl8.5.2\generic\tcl.h Sun Jan 4 16:52:30 2009 -@@ -367,7 +367,7 @@ - typedef struct stati64 Tcl_StatBuf; - # define TCL_LL_MODIFIER "L" - # else /* __BORLANDC__ */ --# if _MSC_VER < 1400 && !defined(_M_IX86) -+# if _MSC_VER < 1400 /*&& !defined(_M_IX86)*/ - typedef struct _stati64 Tcl_StatBuf; - # else - typedef struct _stat64 Tcl_StatBuf; From python-checkins at python.org Fri Sep 10 05:12:10 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Fri, 10 Sep 2010 05:12:10 +0200 (CEST) Subject: [Python-checkins] r84663 - in python/branches/release27-maint/Lib: pydoc_data unittest unittest/test Message-ID: <20100910031210.E6862EE990@mail.python.org> Author: hirokazu.yamamoto Date: Fri Sep 10 05:12:10 2010 New Revision: 84663 Log: Added svn:ignore (*.pyc, *.pyo) Modified: python/branches/release27-maint/Lib/pydoc_data/ (props changed) python/branches/release27-maint/Lib/unittest/ (props changed) python/branches/release27-maint/Lib/unittest/test/ (props changed) From python-checkins at python.org Fri Sep 10 10:25:13 2010 From: python-checkins at python.org (vinay.sajip) Date: Fri, 10 Sep 2010 10:25:13 +0200 (CEST) Subject: [Python-checkins] r84664 - python/branches/py3k/Lib/logging/__init__.py Message-ID: <20100910082513.8C8D6F4F4@mail.python.org> Author: vinay.sajip Date: Fri Sep 10 10:25:13 2010 New Revision: 84664 Log: logging: Added threading interlock in basicConfig(). Modified: python/branches/py3k/Lib/logging/__init__.py Modified: python/branches/py3k/Lib/logging/__init__.py ============================================================================== --- python/branches/py3k/Lib/logging/__init__.py (original) +++ python/branches/py3k/Lib/logging/__init__.py Fri Sep 10 10:25:13 2010 @@ -1459,22 +1459,28 @@ using sys.stdout or sys.stderr), whereas FileHandler closes its stream when the handler is closed. """ - if len(root.handlers) == 0: - filename = kwargs.get("filename") - if filename: - mode = kwargs.get("filemode", 'a') - hdlr = FileHandler(filename, mode) - else: - stream = kwargs.get("stream") - hdlr = StreamHandler(stream) - fs = kwargs.get("format", BASIC_FORMAT) - dfs = kwargs.get("datefmt", None) - fmt = Formatter(fs, dfs) - hdlr.setFormatter(fmt) - root.addHandler(hdlr) - level = kwargs.get("level") - if level is not None: - root.setLevel(level) + # Add thread safety in case someone mistakenly calls + # basicConfig() from multiple threads + _acquireLock() + try: + if len(root.handlers) == 0: + filename = kwargs.get("filename") + if filename: + mode = kwargs.get("filemode", 'a') + hdlr = FileHandler(filename, mode) + else: + stream = kwargs.get("stream") + hdlr = StreamHandler(stream) + fs = kwargs.get("format", BASIC_FORMAT) + dfs = kwargs.get("datefmt", None) + fmt = Formatter(fs, dfs) + hdlr.setFormatter(fmt) + root.addHandler(hdlr) + level = kwargs.get("level") + if level is not None: + root.setLevel(level) + finally: + _releaseLock() #--------------------------------------------------------------------------- # Utility functions at module level. From python-checkins at python.org Fri Sep 10 12:47:25 2010 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 10 Sep 2010 12:47:25 +0200 (CEST) Subject: [Python-checkins] r84665 - in python/branches/release27-maint: Lib/random.py Lib/test/test_random.py Misc/NEWS Message-ID: <20100910104725.64E8CEE9F6@mail.python.org> Author: raymond.hettinger Date: Fri Sep 10 12:47:22 2010 New Revision: 84665 Log: Issue 9816: Random.jumpahead(n) didn't work well for small values of n. Modified: python/branches/release27-maint/Lib/random.py python/branches/release27-maint/Lib/test/test_random.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/random.py ============================================================================== --- python/branches/release27-maint/Lib/random.py (original) +++ python/branches/release27-maint/Lib/random.py Fri Sep 10 12:47:22 2010 @@ -46,6 +46,7 @@ from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin from os import urandom as _urandom from binascii import hexlify as _hexlify +import hashlib as _hashlib __all__ = ["Random","seed","random","uniform","randint","choice","sample", "randrange","shuffle","normalvariate","lognormvariate", @@ -141,6 +142,18 @@ "Random.setstate() of version %s" % (version, self.VERSION)) + def jumpahead(self, n): + """Change the internal state to one that is likely far away + from the current state. This method will not be in Py3.x, + so it is better to simply reseed. + """ + # The super.jumpahead() method uses shuffling to change state, + # so it needs a large and "interesting" n to work with. Here, + # we use hashing to create a large n for the shuffle. + s = repr(n) + repr(self.getstate()) + n = int(_hashlib.new('sha512', s).hexdigest(), 16) + super(Random, self).jumpahead(n) + ## ---- Methods below this point do not need to be overridden when ## ---- subclassing for the purpose of using a different core generator. Modified: python/branches/release27-maint/Lib/test/test_random.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_random.py (original) +++ python/branches/release27-maint/Lib/test/test_random.py Fri Sep 10 12:47:22 2010 @@ -55,8 +55,6 @@ with test_support.check_py3k_warnings(quiet=True): self.assertRaises(TypeError, self.gen.jumpahead) # needs an arg - self.assertRaises(TypeError, self.gen.jumpahead, "ick") # wrong type - self.assertRaises(TypeError, self.gen.jumpahead, 2.3) # wrong type self.assertRaises(TypeError, self.gen.jumpahead, 2, 3) # too many def test_sample(self): Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Fri Sep 10 12:47:22 2010 @@ -43,6 +43,10 @@ Library ------- +- Issue #9816: random.Random.jumpahead(n) did not produce a sufficiently + different internal state for small values of n. Fixed by salting the + value. + - Issue #9792: In case of connection failure, socket.create_connection() would swallow the exception and raise a new one, making it impossible to fetch the original errno, or to filter timeout errors. Now the From python-checkins at python.org Fri Sep 10 13:19:59 2010 From: python-checkins at python.org (victor.stinner) Date: Fri, 10 Sep 2010 13:19:59 +0200 (CEST) Subject: [Python-checkins] r84666 - python/branches/py3k/Lib/test/support.py Message-ID: <20100910111959.6C3AEFD5E@mail.python.org> Author: victor.stinner Date: Fri Sep 10 13:19:59 2010 New Revision: 84666 Log: Issue #9819: fix TESTFN_UNENCODABLE for japanese code page Modified: python/branches/py3k/Lib/test/support.py Modified: python/branches/py3k/Lib/test/support.py ============================================================================== --- python/branches/py3k/Lib/test/support.py (original) +++ python/branches/py3k/Lib/test/support.py Fri Sep 10 13:19:59 2010 @@ -394,8 +394,9 @@ if os.name in ('nt', 'ce'): # skip win32s (0) or Windows 9x/ME (1) if sys.getwindowsversion().platform >= 2: - # Japanese characters (I think - from bug 846133) - TESTFN_UNENCODABLE = TESTFN + "-\u5171\u6709\u3055\u308c\u308b" + # Different kinds of characters from various languages to minimize the + # probability that the whole name is encodable to MBCS (issue #9819) + TESTFN_UNENCODABLE = TESTFN + "-\u5171\u0141\u2661\u0363\uDC80" try: TESTFN_UNENCODABLE.encode(TESTFN_ENCODING) except UnicodeEncodeError: From python-checkins at python.org Fri Sep 10 13:24:10 2010 From: python-checkins at python.org (victor.stinner) Date: Fri, 10 Sep 2010 13:24:10 +0200 (CEST) Subject: [Python-checkins] r84667 - in python/branches/release27-maint: Lib/test/test_support.py Lib/test/test_unicode_file.py Message-ID: <20100910112410.71271F9BF@mail.python.org> Author: victor.stinner Date: Fri Sep 10 13:24:10 2010 New Revision: 84667 Log: Recorded merge of revisions 83987 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83987 | victor.stinner | 2010-08-14 00:23:24 +0200 (sam., 14 ao?t 2010) | 2 lines Fix a typo: TESTFN_UNENCODEABLE => TESTFN_UNENCODABLE ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_support.py python/branches/release27-maint/Lib/test/test_unicode_file.py Modified: python/branches/release27-maint/Lib/test/test_support.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_support.py (original) +++ python/branches/release27-maint/Lib/test/test_support.py Fri Sep 10 13:24:10 2010 @@ -361,30 +361,30 @@ # 2 latin characters. TESTFN_UNICODE = unicode("@test-\xe0\xf2", "latin-1") TESTFN_ENCODING = sys.getfilesystemencoding() - # TESTFN_UNICODE_UNENCODEABLE is a filename that should *not* be + # TESTFN_UNENCODABLE is a filename that should *not* be # able to be encoded by *either* the default or filesystem encoding. # This test really only makes sense on Windows NT platforms # which have special Unicode support in posixmodule. if (not hasattr(sys, "getwindowsversion") or sys.getwindowsversion()[3] < 2): # 0=win32s or 1=9x/ME - TESTFN_UNICODE_UNENCODEABLE = None + TESTFN_UNENCODABLE = None else: # Japanese characters (I think - from bug 846133) - TESTFN_UNICODE_UNENCODEABLE = eval('u"@test-\u5171\u6709\u3055\u308c\u308b"') + TESTFN_UNENCODABLE = eval('u"@test-\u5171\u6709\u3055\u308c\u308b"') try: # XXX - Note - should be using TESTFN_ENCODING here - but for # Windows, "mbcs" currently always operates as if in # errors=ignore' mode - hence we get '?' characters rather than # the exception. 'Latin1' operates as we expect - ie, fails. # See [ 850997 ] mbcs encoding ignores errors - TESTFN_UNICODE_UNENCODEABLE.encode("Latin1") + TESTFN_UNENCODABLE.encode("Latin1") except UnicodeEncodeError: pass else: print \ 'WARNING: The filename %r CAN be encoded by the filesystem. ' \ 'Unicode filename tests may not be effective' \ - % TESTFN_UNICODE_UNENCODEABLE + % TESTFN_UNENCODABLE # Disambiguate TESTFN for parallel testing, while letting it remain a valid Modified: python/branches/release27-maint/Lib/test/test_unicode_file.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_unicode_file.py (original) +++ python/branches/release27-maint/Lib/test/test_unicode_file.py Fri Sep 10 13:24:10 2010 @@ -6,7 +6,7 @@ import unittest from test.test_support import run_unittest, TESTFN_UNICODE -from test.test_support import TESTFN_ENCODING, TESTFN_UNICODE_UNENCODEABLE +from test.test_support import TESTFN_ENCODING, TESTFN_UNENCODABLE try: TESTFN_ENCODED = TESTFN_UNICODE.encode(TESTFN_ENCODING) except (UnicodeError, TypeError): @@ -171,8 +171,8 @@ def test_single_files(self): self._test_single(TESTFN_ENCODED) self._test_single(TESTFN_UNICODE) - if TESTFN_UNICODE_UNENCODEABLE is not None: - self._test_single(TESTFN_UNICODE_UNENCODEABLE) + if TESTFN_UNENCODABLE is not None: + self._test_single(TESTFN_UNENCODABLE) def test_equivalent_files(self): self._test_equivalent(TESTFN_ENCODED, TESTFN_UNICODE) @@ -188,9 +188,9 @@ self._do_directory(TESTFN_UNICODE+ext, TESTFN_ENCODED+ext, False) self._do_directory(TESTFN_UNICODE+ext, TESTFN_UNICODE+ext, False) # Our directory name that can't use a non-unicode name. - if TESTFN_UNICODE_UNENCODEABLE is not None: - self._do_directory(TESTFN_UNICODE_UNENCODEABLE+ext, - TESTFN_UNICODE_UNENCODEABLE+ext, + if TESTFN_UNENCODABLE is not None: + self._do_directory(TESTFN_UNENCODABLE+ext, + TESTFN_UNENCODABLE+ext, False) def test_main(): From python-checkins at python.org Fri Sep 10 14:19:38 2010 From: python-checkins at python.org (victor.stinner) Date: Fri, 10 Sep 2010 14:19:38 +0200 (CEST) Subject: [Python-checkins] r84668 - in python/branches/release31-maint: Lib/test/support.py Message-ID: <20100910121938.215A1EE990@mail.python.org> Author: victor.stinner Date: Fri Sep 10 14:19:37 2010 New Revision: 84668 Log: Merged revisions 84666 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84666 | victor.stinner | 2010-09-10 13:19:59 +0200 (ven., 10 sept. 2010) | 2 lines Issue #9819: fix TESTFN_UNENCODABLE for japanese code page ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/support.py Modified: python/branches/release31-maint/Lib/test/support.py ============================================================================== --- python/branches/release31-maint/Lib/test/support.py (original) +++ python/branches/release31-maint/Lib/test/support.py Fri Sep 10 14:19:37 2010 @@ -351,8 +351,9 @@ sys.getwindowsversion()[3] < 2): # 0=win32s or 1=9x/ME TESTFN_UNICODE_UNENCODEABLE = None else: - # Japanese characters (I think - from bug 846133) - TESTFN_UNICODE_UNENCODEABLE = "@test-\u5171\u6709\u3055\u308c\u308b" + # Different kinds of characters from various languages to minimize the + # probability that the whole name is encodable to MBCS (issue #9819) + TESTFN_UNENCODABLE = TESTFN + "-\u5171\u0141\u2661\u0363\uDC80" try: # XXX - Note - should be using TESTFN_ENCODING here - but for # Windows, "mbcs" currently always operates as if in From python-checkins at python.org Fri Sep 10 14:24:24 2010 From: python-checkins at python.org (nick.coghlan) Date: Fri, 10 Sep 2010 14:24:24 +0200 (CEST) Subject: [Python-checkins] r84669 - python/branches/py3k/Lib/dis.py Message-ID: <20100910122424.8DD97EE983@mail.python.org> Author: nick.coghlan Date: Fri Sep 10 14:24:24 2010 New Revision: 84669 Log: Fix dis.__all__ for new additions to module in 3.2(spotted by Eli Bendersky) Modified: python/branches/py3k/Lib/dis.py Modified: python/branches/py3k/Lib/dis.py ============================================================================== --- python/branches/py3k/Lib/dis.py (original) +++ python/branches/py3k/Lib/dis.py Fri Sep 10 14:24:24 2010 @@ -6,8 +6,8 @@ from opcode import * from opcode import __all__ as _opcodes_all -__all__ = ["dis", "disassemble", "distb", "disco", - "findlinestarts", "findlabels"] + _opcodes_all +__all__ = ["code_info", "dis", "disassemble", "distb", "disco", + "findlinestarts", "findlabels", "show_code"] + _opcodes_all del _opcodes_all _have_code = (types.MethodType, types.FunctionType, types.CodeType, type) From python-checkins at python.org Fri Sep 10 14:32:58 2010 From: python-checkins at python.org (victor.stinner) Date: Fri, 10 Sep 2010 14:32:58 +0200 (CEST) Subject: [Python-checkins] r84670 - in python/branches/release31-maint: Lib/test/support.py Lib/test/test_unicode_file.py Message-ID: <20100910123258.45B30EE98A@mail.python.org> Author: victor.stinner Date: Fri Sep 10 14:32:58 2010 New Revision: 84670 Log: Recorded merge of revisions 83987 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83987 | victor.stinner | 2010-08-14 00:23:24 +0200 (sam., 14 ao?t 2010) | 2 lines Fix a typo: TESTFN_UNENCODEABLE => TESTFN_UNENCODABLE ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/support.py python/branches/release31-maint/Lib/test/test_unicode_file.py Modified: python/branches/release31-maint/Lib/test/support.py ============================================================================== --- python/branches/release31-maint/Lib/test/support.py (original) +++ python/branches/release31-maint/Lib/test/support.py Fri Sep 10 14:32:58 2010 @@ -343,13 +343,13 @@ # file system encoding, but *not* with the default (ascii) encoding TESTFN_UNICODE = "@test-\xe0\xf2" TESTFN_ENCODING = sys.getfilesystemencoding() - # TESTFN_UNICODE_UNENCODEABLE is a filename that should *not* be + # TESTFN_UNENCODABLE is a filename that should *not* be # able to be encoded by *either* the default or filesystem encoding. # This test really only makes sense on Windows NT platforms # which have special Unicode support in posixmodule. if (not hasattr(sys, "getwindowsversion") or sys.getwindowsversion()[3] < 2): # 0=win32s or 1=9x/ME - TESTFN_UNICODE_UNENCODEABLE = None + TESTFN_UNENCODABLE = None else: # Different kinds of characters from various languages to minimize the # probability that the whole name is encodable to MBCS (issue #9819) @@ -360,13 +360,13 @@ # errors=ignore' mode - hence we get '?' characters rather than # the exception. 'Latin1' operates as we expect - ie, fails. # See [ 850997 ] mbcs encoding ignores errors - TESTFN_UNICODE_UNENCODEABLE.encode("Latin1") + TESTFN_UNENCODABLE.encode("Latin1") except UnicodeEncodeError: pass else: print('WARNING: The filename %r CAN be encoded by the filesystem. ' 'Unicode filename tests may not be effective' - % TESTFN_UNICODE_UNENCODEABLE) + % TESTFN_UNENCODABLE) if os.path.isdir(TESTFN): # a test failed (eg. test_os) without removing TESTFN directory Modified: python/branches/release31-maint/Lib/test/test_unicode_file.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_unicode_file.py (original) +++ python/branches/release31-maint/Lib/test/test_unicode_file.py Fri Sep 10 14:32:58 2010 @@ -6,7 +6,7 @@ import unittest from test.support import run_unittest, TESTFN_UNICODE, rmtree -from test.support import TESTFN_ENCODING, TESTFN_UNICODE_UNENCODEABLE +from test.support import TESTFN_ENCODING, TESTFN_UNENCODABLE try: TESTFN_UNICODE.encode(TESTFN_ENCODING) except (UnicodeError, TypeError): @@ -146,8 +146,8 @@ # _test functions with each of the filename combinations we wish to test def test_single_files(self): self._test_single(TESTFN_UNICODE) - if TESTFN_UNICODE_UNENCODEABLE is not None: - self._test_single(TESTFN_UNICODE_UNENCODEABLE) + if TESTFN_UNENCODABLE is not None: + self._test_single(TESTFN_UNENCODABLE) def test_directories(self): # For all 'equivalent' combinations: @@ -156,9 +156,9 @@ ext = ".dir" self._do_directory(TESTFN_UNICODE+ext, TESTFN_UNICODE+ext, False) # Our directory name that can't use a non-unicode name. - if TESTFN_UNICODE_UNENCODEABLE is not None: - self._do_directory(TESTFN_UNICODE_UNENCODEABLE+ext, - TESTFN_UNICODE_UNENCODEABLE+ext, + if TESTFN_UNENCODABLE is not None: + self._do_directory(TESTFN_UNENCODABLE+ext, + TESTFN_UNENCODABLE+ext, False) def test_main(): From python-checkins at python.org Fri Sep 10 14:32:58 2010 From: python-checkins at python.org (nick.coghlan) Date: Fri, 10 Sep 2010 14:32:58 +0200 (CEST) Subject: [Python-checkins] r84671 - python/branches/py3k/Lib/dis.py Message-ID: <20100910123258.DC5FFEE98A@mail.python.org> Author: nick.coghlan Date: Fri Sep 10 14:32:58 2010 New Revision: 84671 Log: Leave show_code out of __all__ and make it clear that its lack of documentation is deliberate Modified: python/branches/py3k/Lib/dis.py Modified: python/branches/py3k/Lib/dis.py ============================================================================== --- python/branches/py3k/Lib/dis.py (original) +++ python/branches/py3k/Lib/dis.py Fri Sep 10 14:32:58 2010 @@ -7,7 +7,7 @@ from opcode import __all__ as _opcodes_all __all__ = ["code_info", "dis", "disassemble", "distb", "disco", - "findlinestarts", "findlabels", "show_code"] + _opcodes_all + "findlinestarts", "findlabels"] + _opcodes_all del _opcodes_all _have_code = (types.MethodType, types.FunctionType, types.CodeType, type) @@ -140,6 +140,10 @@ lines.append("%4d: %s" % i_n) return "\n".join(lines) +# show_code is deliberately undocumented and left out of __all__, +# since it doesn't offer any real benefit over code_info() above +# It is only retained because it already existed and was not +# marked as private in previous versions of Python def show_code(co): """Show details about a code object.""" print(code_info(co)) From python-checkins at python.org Fri Sep 10 16:08:05 2010 From: python-checkins at python.org (nick.coghlan) Date: Fri, 10 Sep 2010 16:08:05 +0200 (CEST) Subject: [Python-checkins] r84672 - in python/branches/py3k: Doc/library/dis.rst Lib/dis.py Message-ID: <20100910140805.14A1AEF29@mail.python.org> Author: nick.coghlan Date: Fri Sep 10 16:08:04 2010 New Revision: 84672 Log: As per python-dev discussion with Eli, properly document and publish dis.show_code Modified: python/branches/py3k/Doc/library/dis.rst python/branches/py3k/Lib/dis.py Modified: python/branches/py3k/Doc/library/dis.rst ============================================================================== --- python/branches/py3k/Doc/library/dis.rst (original) +++ python/branches/py3k/Doc/library/dis.rst Fri Sep 10 16:08:04 2010 @@ -36,7 +36,7 @@ The :mod:`dis` module defines the following functions and constants: -.. function:: code_info(x=None) +.. function:: code_info(x) Return a formatted multi-line string with detailed code object information for the supplied function, method, source code string or code object. @@ -48,6 +48,16 @@ .. versionadded:: 3.2 +.. function:: show_code(x) + + Print detailed code object information for the supplied function, method, + source code string or code object to stdout. + + This is a convenient shorthand for ``print(code_info(x))``, intended for + interactive exploration at the interpreter prompt. + + .. versionadded:: 3.2 + .. function:: dis(x=None) Disassemble the *x* object. *x* can denote either a module, a class, a Modified: python/branches/py3k/Lib/dis.py ============================================================================== --- python/branches/py3k/Lib/dis.py (original) +++ python/branches/py3k/Lib/dis.py Fri Sep 10 16:08:04 2010 @@ -7,7 +7,7 @@ from opcode import __all__ as _opcodes_all __all__ = ["code_info", "dis", "disassemble", "distb", "disco", - "findlinestarts", "findlabels"] + _opcodes_all + "findlinestarts", "findlabels", "show_code"] + _opcodes_all del _opcodes_all _have_code = (types.MethodType, types.FunctionType, types.CodeType, type) @@ -140,12 +140,8 @@ lines.append("%4d: %s" % i_n) return "\n".join(lines) -# show_code is deliberately undocumented and left out of __all__, -# since it doesn't offer any real benefit over code_info() above -# It is only retained because it already existed and was not -# marked as private in previous versions of Python def show_code(co): - """Show details about a code object.""" + """Print details of methods, functions, or code to stdout.""" print(code_info(co)) def disassemble(co, lasti=-1): From python-checkins at python.org Fri Sep 10 20:11:45 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Fri, 10 Sep 2010 20:11:45 +0200 (CEST) Subject: [Python-checkins] r84673 - python/branches/py3k/Modules/posixmodule.c Message-ID: <20100910181145.75DE8EE9FE@mail.python.org> Author: amaury.forgeotdarc Date: Fri Sep 10 20:11:45 2010 New Revision: 84673 Log: Untabify file. Modified: python/branches/py3k/Modules/posixmodule.c Modified: python/branches/py3k/Modules/posixmodule.c ============================================================================== --- python/branches/py3k/Modules/posixmodule.c (original) +++ python/branches/py3k/Modules/posixmodule.c Fri Sep 10 20:11:45 2010 @@ -8256,12 +8256,12 @@ if (ins(d, "EX_NOTFOUND", (long)EX_NOTFOUND)) return -1; #endif /* EX_NOTFOUND */ - /* statvfs */ + /* statvfs */ #ifdef ST_RDONLY - if (ins(d, "ST_RDONLY", (long)ST_RDONLY)) return -1; + if (ins(d, "ST_RDONLY", (long)ST_RDONLY)) return -1; #endif /* ST_RDONLY */ #ifdef ST_NOSUID - if (ins(d, "ST_NOSUID", (long)ST_NOSUID)) return -1; + if (ins(d, "ST_NOSUID", (long)ST_NOSUID)) return -1; #endif /* ST_NOSUID */ #ifdef HAVE_SPAWNV From python-checkins at python.org Fri Sep 10 20:39:04 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 10 Sep 2010 20:39:04 +0200 (CEST) Subject: [Python-checkins] r84674 - in python/branches/py3k: configure configure.in Message-ID: <20100910183904.D1BE7EEA26@mail.python.org> Author: antoine.pitrou Date: Fri Sep 10 20:39:00 2010 New Revision: 84674 Log: Followup to #4026: better patch for flock detection. Modified: python/branches/py3k/configure python/branches/py3k/configure.in Modified: python/branches/py3k/configure ============================================================================== --- python/branches/py3k/configure (original) +++ python/branches/py3k/configure Fri Sep 10 20:39:00 2010 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 84512 . +# From configure.in Revision: 84584 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.65 for python 3.2. # @@ -9586,56 +9586,45 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock" >&5 -$as_echo_n "checking for flock... " >&6; } -have_flock=no -cat confdefs.h - <<_ACEOF >conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock declaration" >&5 +$as_echo_n "checking for flock declaration... " >&6; } +if test "${ac_cv_flock_decl+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - #include - int main () { void* p = flock + ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : - -$as_echo "#define HAVE_FLOCK 1" >>confdefs.h - - have_flock=yes + ac_cv_flock_decl=yes +else + ac_cv_flock_decl=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_flock" >&5 -$as_echo "$have_flock" >&6; } - -if test "$have_flock" = yes ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if flock requires additional libraries." >&5 -$as_echo_n "checking if flock requires additional libraries.... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - #include - -int -main () -{ -void *p = flock; flock(0, 0) - ; - return 0; -} +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_flock_decl" >&5 +$as_echo "$ac_cv_flock_decl" >&6; } +if test "x${ac_cv_flock_decl}" = xyes; then + for ac_func in flock +do : + ac_fn_c_check_func "$LINENO" "flock" "ac_cv_func_flock" +if test "x$ac_cv_func_flock" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_FLOCK 1 _ACEOF -if ac_fn_c_try_link "$LINENO"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 $as_echo_n "checking for flock in -lbsd... " >&6; } if test "${ac_cv_lib_bsd_flock+set}" = set; then : $as_echo_n "(cached) " >&6 @@ -9672,6 +9661,7 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_flock" >&5 $as_echo "$ac_cv_lib_bsd_flock" >&6; } if test "x$ac_cv_lib_bsd_flock" = x""yes; then : + $as_echo "#define HAVE_FLOCK 1" >>confdefs.h $as_echo "#define FLOCK_NEEDS_LIBBSD 1" >>confdefs.h @@ -9681,8 +9671,8 @@ fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext +done + fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for getpagesize" >&5 Modified: python/branches/py3k/configure.in ============================================================================== --- python/branches/py3k/configure.in (original) +++ python/branches/py3k/configure.in Fri Sep 10 20:39:00 2010 @@ -2641,27 +2641,23 @@ [AC_MSG_RESULT(no) ]) -AC_MSG_CHECKING(for flock) -have_flock=no -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -]], [[void* p = flock]])], - [AC_DEFINE(HAVE_FLOCK, 1, Define if you have the 'flock' function.) - have_flock=yes +AC_CACHE_CHECK([for flock declaration], [ac_cv_flock_decl], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include ], + [void* p = flock] + )], + [ac_cv_flock_decl=yes], + [ac_cv_flock_decl=no] + ) ]) -AC_MSG_RESULT($have_flock) - -if test "$have_flock" = yes ; then - AC_MSG_CHECKING(if flock requires additional libraries.) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include - ]], [[void *p = flock; flock(0, 0)]])], - [AC_MSG_RESULT(no)], - [AC_MSG_RESULT(yes) - AC_CHECK_LIB(bsd,flock, [ - AC_DEFINE(FLOCK_NEEDS_LIBBSD, 1, Define if flock needs to be linked with bsd library.) - ]) - ]) +if test "x${ac_cv_flock_decl}" = xyes; then + AC_CHECK_FUNCS(flock,, + AC_CHECK_LIB(bsd,flock, + [AC_DEFINE(HAVE_FLOCK) + AC_DEFINE(FLOCK_NEEDS_LIBBSD, 1, Define if flock needs to be linked with bsd library.) + ]) + ) fi AC_MSG_CHECKING(for getpagesize) From python-checkins at python.org Fri Sep 10 20:44:45 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 10 Sep 2010 20:44:45 +0200 (CEST) Subject: [Python-checkins] r84675 - in python/branches/release31-maint: configure configure.in Message-ID: <20100910184445.54555EEA29@mail.python.org> Author: antoine.pitrou Date: Fri Sep 10 20:44:45 2010 New Revision: 84675 Log: Merged revisions 84674 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84674 | antoine.pitrou | 2010-09-10 20:39:00 +0200 (ven., 10 sept. 2010) | 3 lines Followup to #4026: better patch for flock detection. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/configure python/branches/release31-maint/configure.in Modified: python/branches/release31-maint/configure ============================================================================== --- python/branches/release31-maint/configure (original) +++ python/branches/release31-maint/configure Fri Sep 10 20:44:45 2010 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 84367 . +# From configure.in Revision: 84586 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.65 for python 3.1. # @@ -9408,60 +9408,45 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock" >&5 -$as_echo_n "checking for flock... " >&6; } -have_flock=no -cat confdefs.h - <<_ACEOF >conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock declaration" >&5 +$as_echo_n "checking for flock declaration... " >&6; } +if test "${ac_cv_flock_decl+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -#include "confdefs.h" #include - int main () { void* p = flock + ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : - - -$as_echo "#define HAVE_FLOCK 1" >>confdefs.h - - have_flock=yes + ac_cv_flock_decl=yes +else + ac_cv_flock_decl=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_flock" >&5 -$as_echo "$have_flock" >&6; } -if test "$have_flock" = yes ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if flock requires additional libraries." >&5 -$as_echo_n "checking if flock requires additional libraries.... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #include "confdefs.h" - #include - -int -main () -{ -flock(0, 0) - ; - return 0; -} +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_flock_decl" >&5 +$as_echo "$ac_cv_flock_decl" >&6; } +if test "x${ac_cv_flock_decl}" = xyes; then + for ac_func in flock +do : + ac_fn_c_check_func "$LINENO" "flock" "ac_cv_func_flock" +if test "x$ac_cv_func_flock" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_FLOCK 1 _ACEOF -if ac_fn_c_try_link "$LINENO"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 $as_echo_n "checking for flock in -lbsd... " >&6; } if test "${ac_cv_lib_bsd_flock+set}" = set; then : $as_echo_n "(cached) " >&6 @@ -9498,6 +9483,7 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_flock" >&5 $as_echo "$ac_cv_lib_bsd_flock" >&6; } if test "x$ac_cv_lib_bsd_flock" = x""yes; then : + $as_echo "#define HAVE_FLOCK 1" >>confdefs.h $as_echo "#define FLOCK_NEEDS_LIBBSD 1" >>confdefs.h @@ -9507,8 +9493,8 @@ fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext +done + fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for getpagesize" >&5 Modified: python/branches/release31-maint/configure.in ============================================================================== --- python/branches/release31-maint/configure.in (original) +++ python/branches/release31-maint/configure.in Fri Sep 10 20:44:45 2010 @@ -2641,29 +2641,23 @@ AC_MSG_RESULT(no) ) -AC_MSG_CHECKING(for flock) -have_flock=no -AC_TRY_COMPILE([ -#include "confdefs.h" -#include -], void* p = flock, [ - AC_DEFINE(HAVE_FLOCK, 1, Define if you have the 'flock' function.) - have_flock=yes +AC_CACHE_CHECK([for flock declaration], [ac_cv_flock_decl], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include ], + [void* p = flock] + )], + [ac_cv_flock_decl=yes], + [ac_cv_flock_decl=no] + ) ]) -AC_MSG_RESULT($have_flock) - -if test "$have_flock" = yes ; then - AC_MSG_CHECKING(if flock requires additional libraries.) - AC_TRY_LINK([ - #include "confdefs.h" - #include - ], [flock(0, 0)], - AC_MSG_RESULT(no), [ - AC_MSG_RESULT(yes) - AC_CHECK_LIB(bsd,flock, [ - AC_DEFINE(FLOCK_NEEDS_LIBBSD, 1, Define if flock needs to be linked with bsd library.) - ]) - ]) +if test "x${ac_cv_flock_decl}" = xyes; then + AC_CHECK_FUNCS(flock,, + AC_CHECK_LIB(bsd,flock, + [AC_DEFINE(HAVE_FLOCK) + AC_DEFINE(FLOCK_NEEDS_LIBBSD, 1, Define if flock needs to be linked with bsd library.) + ]) + ) fi AC_MSG_CHECKING(for getpagesize) From python-checkins at python.org Fri Sep 10 20:47:36 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 10 Sep 2010 20:47:36 +0200 (CEST) Subject: [Python-checkins] r84676 - in python/branches/release27-maint: configure configure.in Message-ID: <20100910184736.6814FEEA32@mail.python.org> Author: antoine.pitrou Date: Fri Sep 10 20:47:36 2010 New Revision: 84676 Log: Merged revisions 84674 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84674 | antoine.pitrou | 2010-09-10 20:39:00 +0200 (ven., 10 sept. 2010) | 3 lines Followup to #4026: better patch for flock detection. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/configure python/branches/release27-maint/configure.in Modified: python/branches/release27-maint/configure ============================================================================== --- python/branches/release27-maint/configure (original) +++ python/branches/release27-maint/configure Fri Sep 10 20:47:36 2010 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 84368 . +# From configure.in Revision: 84585 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.65 for python 2.7. # @@ -9740,56 +9740,45 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock" >&5 -$as_echo_n "checking for flock... " >&6; } -have_flock=no -cat confdefs.h - <<_ACEOF >conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock declaration" >&5 +$as_echo_n "checking for flock declaration... " >&6; } +if test "${ac_cv_flock_decl+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - #include - int main () { void* p = flock + ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : - -$as_echo "#define HAVE_FLOCK 1" >>confdefs.h - - have_flock=yes + ac_cv_flock_decl=yes +else + ac_cv_flock_decl=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_flock" >&5 -$as_echo "$have_flock" >&6; } - -if test "$have_flock" = yes ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if flock requires additional libraries." >&5 -$as_echo_n "checking if flock requires additional libraries.... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - #include - -int -main () -{ -void *p = flock; flock(0, 0) - ; - return 0; -} +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_flock_decl" >&5 +$as_echo "$ac_cv_flock_decl" >&6; } +if test "x${ac_cv_flock_decl}" = xyes; then + for ac_func in flock +do : + ac_fn_c_check_func "$LINENO" "flock" "ac_cv_func_flock" +if test "x$ac_cv_func_flock" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_FLOCK 1 _ACEOF -if ac_fn_c_try_link "$LINENO"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 $as_echo_n "checking for flock in -lbsd... " >&6; } if test "${ac_cv_lib_bsd_flock+set}" = set; then : $as_echo_n "(cached) " >&6 @@ -9826,6 +9815,7 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_flock" >&5 $as_echo "$ac_cv_lib_bsd_flock" >&6; } if test "x$ac_cv_lib_bsd_flock" = x""yes; then : + $as_echo "#define HAVE_FLOCK 1" >>confdefs.h $as_echo "#define FLOCK_NEEDS_LIBBSD 1" >>confdefs.h @@ -9835,8 +9825,8 @@ fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext +done + fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for getpagesize" >&5 Modified: python/branches/release27-maint/configure.in ============================================================================== --- python/branches/release27-maint/configure.in (original) +++ python/branches/release27-maint/configure.in Fri Sep 10 20:47:36 2010 @@ -2774,27 +2774,23 @@ [AC_MSG_RESULT(no) ]) -AC_MSG_CHECKING(for flock) -have_flock=no -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -]], [[void* p = flock]])], - [AC_DEFINE(HAVE_FLOCK, 1, Define if you have the 'flock' function.) - have_flock=yes +AC_CACHE_CHECK([for flock declaration], [ac_cv_flock_decl], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include ], + [void* p = flock] + )], + [ac_cv_flock_decl=yes], + [ac_cv_flock_decl=no] + ) ]) -AC_MSG_RESULT($have_flock) - -if test "$have_flock" = yes ; then - AC_MSG_CHECKING(if flock requires additional libraries.) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include - ]], [[void *p = flock; flock(0, 0)]])], - [AC_MSG_RESULT(no)], - [AC_MSG_RESULT(yes) - AC_CHECK_LIB(bsd,flock, [ - AC_DEFINE(FLOCK_NEEDS_LIBBSD, 1, Define if flock needs to be linked with bsd library.) - ]) - ]) +if test "x${ac_cv_flock_decl}" = xyes; then + AC_CHECK_FUNCS(flock,, + AC_CHECK_LIB(bsd,flock, + [AC_DEFINE(HAVE_FLOCK) + AC_DEFINE(FLOCK_NEEDS_LIBBSD, 1, Define if flock needs to be linked with bsd library.) + ]) + ) fi AC_MSG_CHECKING(for getpagesize) From python-checkins at python.org Fri Sep 10 20:50:38 2010 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 10 Sep 2010 20:50:38 +0200 (CEST) Subject: [Python-checkins] r84677 - python/branches/py3k/Doc/library/fcntl.rst Message-ID: <20100910185038.4D427EE9F3@mail.python.org> Author: benjamin.peterson Date: Fri Sep 10 20:50:38 2010 New Revision: 84677 Log: add reference to file object Modified: python/branches/py3k/Doc/library/fcntl.rst Modified: python/branches/py3k/Doc/library/fcntl.rst ============================================================================== --- python/branches/py3k/Doc/library/fcntl.rst (original) +++ python/branches/py3k/Doc/library/fcntl.rst Fri Sep 10 20:50:38 2010 @@ -16,8 +16,8 @@ All functions in this module take a file descriptor *fd* as their first argument. This can be an integer file descriptor, such as returned by -``sys.stdin.fileno()``, or a file object, such as ``sys.stdin`` itself, which -provides a :meth:`fileno` which returns a genuine file descriptor. +``sys.stdin.fileno()``, or a :class:`io.IOBase` object, such as ``sys.stdin`` +itself, which provides a :meth:`fileno` that returns a genuine file descriptor. The module defines the following functions: From python-checkins at python.org Fri Sep 10 20:52:37 2010 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 10 Sep 2010 20:52:37 +0200 (CEST) Subject: [Python-checkins] r84678 - in python/branches/release31-maint: Doc/library/fcntl.rst Message-ID: <20100910185237.1987DEEA1D@mail.python.org> Author: benjamin.peterson Date: Fri Sep 10 20:52:36 2010 New Revision: 84678 Log: Merged revisions 84677 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84677 | benjamin.peterson | 2010-09-10 13:50:38 -0500 (Fri, 10 Sep 2010) | 1 line add reference to file object ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/fcntl.rst Modified: python/branches/release31-maint/Doc/library/fcntl.rst ============================================================================== --- python/branches/release31-maint/Doc/library/fcntl.rst (original) +++ python/branches/release31-maint/Doc/library/fcntl.rst Fri Sep 10 20:52:36 2010 @@ -16,8 +16,8 @@ All functions in this module take a file descriptor *fd* as their first argument. This can be an integer file descriptor, such as returned by -``sys.stdin.fileno()``, or a file object, such as ``sys.stdin`` itself, which -provides a :meth:`fileno` which returns a genuine file descriptor. +``sys.stdin.fileno()``, or a :class:`io.IOBase` object, such as ``sys.stdin`` +itself, which provides a :meth:`fileno` that returns a genuine file descriptor. The module defines the following functions: From python-checkins at python.org Fri Sep 10 21:40:53 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Fri, 10 Sep 2010 21:40:53 +0200 (CEST) Subject: [Python-checkins] r84679 - python/branches/py3k/Lib/test/test_scope.py Message-ID: <20100910194053.1F6D8EE990@mail.python.org> Author: amaury.forgeotdarc Date: Fri Sep 10 21:40:52 2010 New Revision: 84679 Log: Use the "if 1:" prefix so that quoted code appears nicely nested inside the test suite. def test_me(): exec("""if 1: ...code... """) No other change here. Modified: python/branches/py3k/Lib/test/test_scope.py Modified: python/branches/py3k/Lib/test/test_scope.py ============================================================================== --- python/branches/py3k/Lib/test/test_scope.py (original) +++ python/branches/py3k/Lib/test/test_scope.py Fri Sep 10 21:40:52 2010 @@ -192,44 +192,44 @@ def testUnoptimizedNamespaces(self): - check_syntax_error(self, """\ -def unoptimized_clash1(strip): - def f(s): - from sys import * - return getrefcount(s) # ambiguity: free or local - return f -""") - - check_syntax_error(self, """\ -def unoptimized_clash2(): - from sys import * - def f(s): - return getrefcount(s) # ambiguity: global or local - return f -""") - - check_syntax_error(self, """\ -def unoptimized_clash2(): - from sys import * - def g(): - def f(s): - return getrefcount(s) # ambiguity: global or local - return f -""") - - check_syntax_error(self, """\ -def f(x): - def g(): - return x - del x # can't del name -""") - - check_syntax_error(self, """\ -def f(): - def g(): - from sys import * - return getrefcount # global or local? -""") + check_syntax_error(self, """if 1: + def unoptimized_clash1(strip): + def f(s): + from sys import * + return getrefcount(s) # ambiguity: free or local + return f + """) + + check_syntax_error(self, """if 1: + def unoptimized_clash2(): + from sys import * + def f(s): + return getrefcount(s) # ambiguity: global or local + return f + """) + + check_syntax_error(self, """if 1: + def unoptimized_clash2(): + from sys import * + def g(): + def f(s): + return getrefcount(s) # ambiguity: global or local + return f + """) + + check_syntax_error(self, """if 1: + def f(x): + def g(): + return x + del x # can't del name + """) + + check_syntax_error(self, """if 1: + def f(): + def g(): + from sys import * + return getrefcount # global or local? + """) def testLambdas(self): @@ -273,17 +273,17 @@ self.assertRaises(NameError, errorInInner) # test for bug #1501934: incorrect LOAD/STORE_GLOBAL generation - exec(""" -global_x = 1 -def f(): - global_x += 1 -try: - f() -except UnboundLocalError: - pass -else: - fail('scope of global_x not correctly determined') -""", {'fail': self.fail}) + exec("""if 1: + global_x = 1 + def f(): + global_x += 1 + try: + f() + except UnboundLocalError: + pass + else: + fail('scope of global_x not correctly determined') + """, {'fail': self.fail}) def testComplexDefinitions(self): @@ -302,88 +302,88 @@ self.assertEqual(makeReturner2(a=11)()['a'], 11) def testScopeOfGlobalStmt(self): -# Examples posted by Samuele Pedroni to python-dev on 3/1/2001 + # Examples posted by Samuele Pedroni to python-dev on 3/1/2001 - exec("""\ -# I -x = 7 -def f(): - x = 1 - def g(): - global x - def i(): - def h(): - return x - return h() - return i() - return g() -self.assertEqual(f(), 7) -self.assertEqual(x, 7) - -# II -x = 7 -def f(): - x = 1 - def g(): - x = 2 - def i(): - def h(): - return x - return h() - return i() - return g() -self.assertEqual(f(), 2) -self.assertEqual(x, 7) - -# III -x = 7 -def f(): - x = 1 - def g(): - global x - x = 2 - def i(): - def h(): - return x - return h() - return i() - return g() -self.assertEqual(f(), 2) -self.assertEqual(x, 2) - -# IV -x = 7 -def f(): - x = 3 - def g(): - global x - x = 2 - def i(): - def h(): - return x - return h() - return i() - return g() -self.assertEqual(f(), 2) -self.assertEqual(x, 2) - -# XXX what about global statements in class blocks? -# do they affect methods? - -x = 12 -class Global: - global x - x = 13 - def set(self, val): - x = val - def get(self): - return x - -g = Global() -self.assertEqual(g.get(), 13) -g.set(15) -self.assertEqual(g.get(), 13) -""") + exec("""if 1: + # I + x = 7 + def f(): + x = 1 + def g(): + global x + def i(): + def h(): + return x + return h() + return i() + return g() + self.assertEqual(f(), 7) + self.assertEqual(x, 7) + + # II + x = 7 + def f(): + x = 1 + def g(): + x = 2 + def i(): + def h(): + return x + return h() + return i() + return g() + self.assertEqual(f(), 2) + self.assertEqual(x, 7) + + # III + x = 7 + def f(): + x = 1 + def g(): + global x + x = 2 + def i(): + def h(): + return x + return h() + return i() + return g() + self.assertEqual(f(), 2) + self.assertEqual(x, 2) + + # IV + x = 7 + def f(): + x = 3 + def g(): + global x + x = 2 + def i(): + def h(): + return x + return h() + return i() + return g() + self.assertEqual(f(), 2) + self.assertEqual(x, 2) + + # XXX what about global statements in class blocks? + # do they affect methods? + + x = 12 + class Global: + global x + x = 13 + def set(self, val): + x = val + def get(self): + return x + + g = Global() + self.assertEqual(g.get(), 13) + g.set(15) + self.assertEqual(g.get(), 13) + """) def testLeaks(self): @@ -409,28 +409,28 @@ def testClassAndGlobal(self): - exec("""\ -def test(x): - class Foo: - global x - def __call__(self, y): - return x + y - return Foo() - -x = 0 -self.assertEqual(test(6)(2), 8) -x = -1 -self.assertEqual(test(3)(2), 5) - -looked_up_by_load_name = False -class X: - # Implicit globals inside classes are be looked up by LOAD_NAME, not - # LOAD_GLOBAL. - locals()['looked_up_by_load_name'] = True - passed = looked_up_by_load_name + exec("""if 1: + def test(x): + class Foo: + global x + def __call__(self, y): + return x + y + return Foo() + + x = 0 + self.assertEqual(test(6)(2), 8) + x = -1 + self.assertEqual(test(3)(2), 5) + + looked_up_by_load_name = False + class X: + # Implicit globals inside classes are be looked up by LOAD_NAME, not + # LOAD_GLOBAL. + locals()['looked_up_by_load_name'] = True + passed = looked_up_by_load_name -self.assertTrue(X.passed) -""") + self.assertTrue(X.passed) + """) def testLocalsFunction(self): @@ -626,22 +626,22 @@ # function to other nested functions in the same block. # This test verifies that a global statement in the first # function does not affect the second function. - CODE = """def f(): - y = 1 - def g(): - global y - return y - def h(): - return y + 1 - return g, h -y = 9 -g, h = f() -result9 = g() -result2 = h() -""" local_ns = {} global_ns = {} - exec(CODE, local_ns, global_ns) + exec("""if 1: + def f(): + y = 1 + def g(): + global y + return y + def h(): + return y + 1 + return g, h + y = 9 + g, h = f() + result9 = g() + result2 = h() + """, local_ns, global_ns) self.assertEqual(2, global_ns["result2"]) self.assertEqual(9, global_ns["result9"]) From python-checkins at python.org Fri Sep 10 21:44:45 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 10 Sep 2010 21:44:45 +0200 (CEST) Subject: [Python-checkins] r84680 - in python/branches/py3k: Lib/distutils/command/build_ext.py Makefile.pre.in Misc/NEWS Modules/ld_so_aix Modules/ld_so_aix.in configure configure.in Message-ID: <20100910194445.1C71AEE995@mail.python.org> Author: antoine.pitrou Date: Fri Sep 10 21:44:44 2010 New Revision: 84680 Log: Issue #941346: Improve the build process under AIX and allow Python to be built as a shared library. Patch by S?bastien Sabl?. Added: python/branches/py3k/Modules/ld_so_aix.in Removed: python/branches/py3k/Modules/ld_so_aix Modified: python/branches/py3k/Lib/distutils/command/build_ext.py python/branches/py3k/Makefile.pre.in python/branches/py3k/Misc/NEWS python/branches/py3k/configure python/branches/py3k/configure.in Modified: python/branches/py3k/Lib/distutils/command/build_ext.py ============================================================================== --- python/branches/py3k/Lib/distutils/command/build_ext.py (original) +++ python/branches/py3k/Lib/distutils/command/build_ext.py Fri Sep 10 21:44:44 2010 @@ -748,6 +748,9 @@ elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries + elif sys.platform[:3] == 'aix': + # Don't use the default code below + return ext.libraries else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): Modified: python/branches/py3k/Makefile.pre.in ============================================================================== --- python/branches/py3k/Makefile.pre.in (original) +++ python/branches/py3k/Makefile.pre.in Fri Sep 10 21:44:44 2010 @@ -446,10 +446,10 @@ libpython$(VERSION).so: $(LIBRARY_OBJS) if test $(INSTSONAME) != $(LDLIBRARY); then \ - $(LDSHARED) $(PY_LDFLAGS) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ + $(BLDSHARED) $(PY_LDFLAGS) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ $(LN) -f $(INSTSONAME) $@; \ else \ - $(LDSHARED) $(PY_LDFLAGS) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ + $(BLDSHARED) $(PY_LDFLAGS) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ fi libpython$(VERSION).dylib: $(LIBRARY_OBJS) @@ -1220,7 +1220,7 @@ done -rm -f core Makefile Makefile.pre config.status \ Modules/Setup Modules/Setup.local Modules/Setup.config \ - Misc/python.pc + Modules/ld_so_aix Misc/python.pc -rm -f python*-gdb.py -rm -f pybuilddir.txt find $(srcdir) '(' -name '*.fdc' -o -name '*~' \ Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Sep 10 21:44:44 2010 @@ -102,6 +102,9 @@ Build ----- +- Issue #941346: Improve the build process under AIX and allow Python to + be built as a shared library. Patch by S?bastien Sabl?. + - Issue #4026: Make the fcntl extension build under AIX. Patch by S?bastien Sabl?. Deleted: python/branches/py3k/Modules/ld_so_aix ============================================================================== --- python/branches/py3k/Modules/ld_so_aix Fri Sep 10 21:44:44 2010 +++ (empty file) @@ -1,190 +0,0 @@ -#!/bin/sh -# -# ======================================================================== -# FILE: ld_so_aix -# TYPE: executable, uses makexp_aix -# SYSTEM: AIX -# -# DESCRIPTION: Creates a shareable .o from a set of pre-compiled -# (unshared) .o files -# -# USAGE: ld_so_aix [CC] [arguments] -# -# ARGUMENTS: Same as for "ld". The following arguments are processed -# or supplied by this script (those marked with an asterisk -# can be overriden from command line): -# -# Argument Default value -# (*) -o [OutputFileName] -o shr.o -# (*) -e [EntryPointLabel] -e init[OutputBaseName] -# (*) -bE:[ExportFile] -bE:[OutputBaseName].exp -# (*) -bI:[ImportFile] -bI:./python.exp -# -bM:[ModuleType] -bM:SRE -# -bhalt:[Number] -bhalt:4 -# -T[Number] -T512 -# -H[Number] -H512 -# -lm -# -# The compiler specific ("-lc" or "-lc_r", "-lpthreads",...) -# arguments will be automatically passed to "ld" according -# to the CC command provided as a first argument to this -# script. Usually, the same CC command was used to produce -# the pre-compiled .o file(s). -# -# NOTES: 1. Since "ld_so_aix" was originally written for building -# shared modules for the Python interpreter, the -e and -# -bI default values match Python's conventions. In -# Python, the entry point for a shared module is based -# on the module's name (e.g., the "mathmodule" will -# expect an entry point of "initmath"). -# 2. The script accepts multiple .o or .a input files and -# creates a single (shared) output file. The export list -# that is created is based on the output file's basename -# with the suffix ".exp". -# 3. The resulting shared object file is left in the -# current directory. -# 4. Uncommenting the "echo" lines gives detailed output -# about the commands executed in the script. -# -# -# HISTORY: Oct-1996 -- Support added for multiple .o files -- -# -- and optional arguments processing. -- -# Chris Myers (myers at tc.cornell.edu), Keith Kwok -# (kkwok at tc.cornell.edu) and Vladimir Marangozov -# -# Aug-6-1996 -- Take care of the compiler specific -- -# -- args by leaving CC to invoke "ld". -- -# Vladimir Marangozov -# -# Jul-1-1996 -- Make sure to use /usr/ccs/bin/ld -- -# -- Use makexp_aix for the export list. -- -# Vladimir Marangozov (Vladimir.Marangozov at imag.fr) -# -# Manus Hand (mhand at csn.net) -- Initial code -- 6/24/96 -# ======================================================================== -# - -usage="Usage: ld_so_aix [CC command] [ld arguments]" -if test ! -n "$*"; then - echo $usage; exit 2 -fi - -makexp=`dirname $0`/makexp_aix - -# Check for existence of compiler. -CC=$1; shift -whichcc=`which $CC` - -if test ! -x "$whichcc"; then - echo "ld_so_aix: Compiler '$CC' not found; exiting." - exit 2 -fi - -if test ! -n "$*"; then - echo $usage; exit 2 -fi - -# Default import file for Python -# Can be overriden by providing a -bI: argument. -impfile="./python.exp" - -# Parse arguments -while test -n "$1" -do - case "$1" in - -e | -Wl,-e) - if test -z "$2"; then - echo "ld_so_aix: The -e flag needs a parameter; exiting."; exit 2 - else - shift; entry=$1 - fi - ;; - -e* | -Wl,-e*) - entry=`echo $1 | sed -e "s/-Wl,//" -e "s/-e//"` - ;; - -o) - if test -z "$2"; then - echo "ld_so_aix: The -o flag needs a parameter; exiting."; exit 2 - else - shift; objfile=$1 - fi - ;; - -o*) - objfile=`echo $1 | sed "s/-o//"` - ;; - -bI:* | -Wl,-bI:*) - impfile=`echo $1 | sed -e "s/-Wl,//" -e "s/-bI://"` - ;; - -bE:* | -Wl,-bE:*) - expfile=`echo $1 | sed -e "s/-Wl,//" -e "s/-bE://"` - ;; - *.o | *.a) - objs="$objs $1" - args="$args $1" - ;; - -bM:* | -Wl,-bM:* | -H* | -Wl,-H* | -T* | -Wl,-T* | -lm) - ;; - *) - args="$args $1" - ;; - esac - shift -done - - -if test -z "$objs"; then - echo "ld_so_aix: No input files; exiting." - exit 2 -elif test ! -r "$impfile"; then - echo "ld_so_aix: Import file '$impfile' not found or not readable; exiting." - exit 2 -fi - -# If -o wasn't specified, assume "-o shr.o" -if test -z "$objfile"; then - objfile=shr.o -fi - -filename=`basename $objfile | sed "s/\.[^.]*$//"` - -# If -bE: wasn't specified, assume "-bE:$filename.exp" -if test -z "$expfile"; then - expfile="$filename.exp" -fi - -# Default entry symbol for Python modules = init[modulename] -# Can be overriden by providing a -e argument. -if test -z "$entry"; then - entry=init`echo $filename | sed "s/module.*//"` -fi - -#echo "ld_so_aix: Debug info section" -#echo " -> output file : $objfile" -#echo " -> import file : $impfile" -#echo " -> export file : $expfile" -#echo " -> entry point : $entry" -#echo " -> object files: $objs" -#echo " -> CC arguments: $args" - -CCOPT="-Wl,-e$entry -Wl,-bE:$expfile -Wl,-bI:$impfile -Wl,-bhalt:4" -CCOPT="$CCOPT -Wl,-bM:SRE -Wl,-T512 -Wl,-H512 -lm -o $objfile" -# Note: to use dynamic libraries like libtcl8.4.so and libtk8.4.so -# you may need to replace the second CCOPT line above with the following: -# CCOPT="$CCOPT -Wl,-bM:SRE -Wl,-T512 -Wl,-H512 -brtl -bnortllib -lm -o $objfile" - -CCARGS="$args" - -# Export list generation. -#echo $makexp $expfile "$objfile" $objs -$makexp $expfile "$objfile" $objs - -# Perform the link. -#echo $CC $CCOPT $CCARGS -$CC $CCOPT $CCARGS -retval=$? - -# Delete the module's export list file. -# Comment this line if you need it. -rm -f $expfile - -exit $retval Added: python/branches/py3k/Modules/ld_so_aix.in ============================================================================== --- (empty file) +++ python/branches/py3k/Modules/ld_so_aix.in Fri Sep 10 21:44:44 2010 @@ -0,0 +1,194 @@ +#!/bin/sh +# +# ======================================================================== +# FILE: ld_so_aix +# TYPE: executable, uses makexp_aix +# SYSTEM: AIX +# +# DESCRIPTION: Creates a shareable .o from a set of pre-compiled +# (unshared) .o files +# +# USAGE: ld_so_aix [CC] [arguments] +# +# ARGUMENTS: Same as for "ld". The following arguments are processed +# or supplied by this script (those marked with an asterisk +# can be overriden from command line): +# +# Argument Default value +# (*) -o [OutputFileName] -o shr.o +# (*) -e [EntryPointLabel] -e init[OutputBaseName] +# (*) -bE:[ExportFile] -bE:[OutputBaseName].exp +# (*) -bI:[ImportFile] -bI:./python.exp +# -bM:[ModuleType] -bM:SRE +# -bhalt:[Number] -bhalt:4 +# -T[Number] -T512 +# -H[Number] -H512 +# -lm +# +# The compiler specific ("-lc" or "-lc_r", "-lpthreads",...) +# arguments will be automatically passed to "ld" according +# to the CC command provided as a first argument to this +# script. Usually, the same CC command was used to produce +# the pre-compiled .o file(s). +# +# NOTES: 1. Since "ld_so_aix" was originally written for building +# shared modules for the Python interpreter, the -e and +# -bI default values match Python's conventions. In +# Python, the entry point for a shared module is based +# on the module's name (e.g., the "mathmodule" will +# expect an entry point of "initmath"). +# 2. The script accepts multiple .o or .a input files and +# creates a single (shared) output file. The export list +# that is created is based on the output file's basename +# with the suffix ".exp". +# 3. The resulting shared object file is left in the +# current directory. +# 4. Uncommenting the "echo" lines gives detailed output +# about the commands executed in the script. +# +# +# HISTORY: Oct-1996 -- Support added for multiple .o files -- +# -- and optional arguments processing. -- +# Chris Myers (myers at tc.cornell.edu), Keith Kwok +# (kkwok at tc.cornell.edu) and Vladimir Marangozov +# +# Aug-6-1996 -- Take care of the compiler specific -- +# -- args by leaving CC to invoke "ld". -- +# Vladimir Marangozov +# +# Jul-1-1996 -- Make sure to use /usr/ccs/bin/ld -- +# -- Use makexp_aix for the export list. -- +# Vladimir Marangozov (Vladimir.Marangozov at imag.fr) +# +# Manus Hand (mhand at csn.net) -- Initial code -- 6/24/96 +# ======================================================================== +# + +usage="Usage: ld_so_aix [CC command] [ld arguments]" +if test ! -n "$*"; then + echo $usage; exit 2 +fi + +makexp=`dirname $0`/makexp_aix + +# Check for existence of compiler. +CC=$1; shift +whichcc=`which $CC` + +if test ! -x "$whichcc"; then + echo "ld_so_aix: Compiler '$CC' not found; exiting." + exit 2 +fi + +if test ! -n "$*"; then + echo $usage; exit 2 +fi + +# Default import file for Python +# Can be overriden by providing a -bI: argument. +impfile="./python.exp" + +# Parse arguments +while test -n "$1" +do + case "$1" in + -e | -Wl,-e) + if test -z "$2"; then + echo "ld_so_aix: The -e flag needs a parameter; exiting."; exit 2 + else + shift; entry=$1 + fi + ;; + -e* | -Wl,-e*) + entry=`echo $1 | sed -e "s/-Wl,//" -e "s/-e//"` + ;; + -o) + if test -z "$2"; then + echo "ld_so_aix: The -o flag needs a parameter; exiting."; exit 2 + else + shift; objfile=$1 + fi + ;; + -o*) + objfile=`echo $1 | sed "s/-o//"` + ;; + -bI:* | -Wl,-bI:*) + impfile=`echo $1 | sed -e "s/-Wl,//" -e "s/-bI://"` + ;; + -bE:* | -Wl,-bE:*) + expfile=`echo $1 | sed -e "s/-Wl,//" -e "s/-bE://"` + ;; + *.o | *.a) + objs="$objs $1" + args="$args $1" + ;; + -bM:* | -Wl,-bM:* | -H* | -Wl,-H* | -T* | -Wl,-T* | -lm) + ;; + *) + args="$args $1" + ;; + esac + shift +done + +if test "$objfile" = "libpython at VERSION@.so"; then + ldsocoremode="true" +fi + +if test -z "$objs"; then + echo "ld_so_aix: No input files; exiting." + exit 2 +elif test ! -r "$impfile" -a -z "$ldsocoremode"; then + echo "ld_so_aix: Import file '$impfile' not found or not readable; exiting." + exit 2 +fi + +# If -o wasn't specified, assume "-o shr.o" +if test -z "$objfile"; then + objfile=shr.o +fi + +filename=`basename $objfile | sed "s/\.[^.]*$//"` + +# If -bE: wasn't specified, assume "-bE:$filename.exp" +if test -z "$expfile"; then + expfile="$filename.exp" +fi + +# Default entry symbol for Python modules = init[modulename] +# Can be overriden by providing a -e argument. +if test -z "$entry"; then + entry=PyInit_`echo $filename | sed "s/module.*//"` +fi + +#echo "ld_so_aix: Debug info section" +#echo " -> output file : $objfile" +#echo " -> import file : $impfile" +#echo " -> export file : $expfile" +#echo " -> entry point : $entry" +#echo " -> object files: $objs" +#echo " -> CC arguments: $args" + +if test -z "$ldsocoremode"; then + CCOPT="-Wl,-e$entry -Wl,-bE:$expfile -Wl,-bI:$impfile -Wl,-bhalt:4" +else + CCOPT="-Wl,-bnoentry -Wl,-bE:$expfile -Wl,-bhalt:4" +fi +CCOPT="$CCOPT -Wl,-bM:SRE -Wl,-T512 -Wl,-H512 -Wl,-brtl -Wl,-bnortllib -lm -o $objfile" + +CCARGS="$args" + +# Export list generation. +#echo $makexp $expfile "$objfile" $objs +$makexp $expfile "$objfile" $objs + +# Perform the link. +#echo $CC $CCOPT $CCARGS +$CC $CCOPT $CCARGS +retval=$? + +# Delete the module's export list file. +# Comment this line if you need it. +rm -f $expfile + +exit $retval Modified: python/branches/py3k/configure ============================================================================== --- python/branches/py3k/configure (original) +++ python/branches/py3k/configure Fri Sep 10 21:44:44 2010 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 84584 . +# From configure.in Revision: 84674 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.65 for python 3.2. # @@ -4929,6 +4929,10 @@ BLDLIBRARY='-L. -lpython$(VERSION)' RUNSHARED='DYLD_LIBRARY_PATH=`pwd`:${DYLD_LIBRARY_PATH}' ;; + AIX*) + LDLIBRARY='libpython$(VERSION).so' + RUNSHARED=LIBPATH=`pwd`:${LIBPATH} + ;; esac else # shared is disabled @@ -7484,7 +7488,7 @@ then case $ac_sys_system/$ac_sys_release in AIX*) - BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp" + BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp -L\$(srcdir)" LDSHARED="\$(BINLIBDEST)/config/ld_so_aix \$(CC) -bI:\$(BINLIBDEST)/config/python.exp" ;; IRIX/5*) LDSHARED="ld -shared";; @@ -13756,6 +13760,8 @@ # generate output files ac_config_files="$ac_config_files Makefile.pre Modules/Setup.config Misc/python.pc" +ac_config_files="$ac_config_files Modules/ld_so_aix" + cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure @@ -14448,6 +14454,7 @@ "Makefile.pre") CONFIG_FILES="$CONFIG_FILES Makefile.pre" ;; "Modules/Setup.config") CONFIG_FILES="$CONFIG_FILES Modules/Setup.config" ;; "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; + "Modules/ld_so_aix") CONFIG_FILES="$CONFIG_FILES Modules/ld_so_aix" ;; *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac @@ -14987,6 +14994,11 @@ esac + + case $ac_file$ac_mode in + "Modules/ld_so_aix":F) chmod +x Modules/ld_so_aix ;; + + esac done # for ac_tag Modified: python/branches/py3k/configure.in ============================================================================== --- python/branches/py3k/configure.in (original) +++ python/branches/py3k/configure.in Fri Sep 10 21:44:44 2010 @@ -766,6 +766,10 @@ BLDLIBRARY='-L. -lpython$(VERSION)' RUNSHARED='DYLD_LIBRARY_PATH=`pwd`:${DYLD_LIBRARY_PATH}' ;; + AIX*) + LDLIBRARY='libpython$(VERSION).so' + RUNSHARED=LIBPATH=`pwd`:${LIBPATH} + ;; esac else # shared is disabled @@ -1665,7 +1669,7 @@ then case $ac_sys_system/$ac_sys_release in AIX*) - BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp" + BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp -L\$(srcdir)" LDSHARED="\$(BINLIBDEST)/config/ld_so_aix \$(CC) -bI:\$(BINLIBDEST)/config/python.exp" ;; IRIX/5*) LDSHARED="ld -shared";; @@ -4247,6 +4251,7 @@ # generate output files AC_CONFIG_FILES(Makefile.pre Modules/Setup.config Misc/python.pc) +AC_CONFIG_FILES([Modules/ld_so_aix], [chmod +x Modules/ld_so_aix]) AC_OUTPUT echo "creating Modules/Setup" From python-checkins at python.org Fri Sep 10 21:47:43 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Fri, 10 Sep 2010 21:47:43 +0200 (CEST) Subject: [Python-checkins] r84681 - python/branches/py3k/Lib/test/test_syntax.py Message-ID: <20100910194743.58F90EE995@mail.python.org> Author: amaury.forgeotdarc Date: Fri Sep 10 21:47:43 2010 New Revision: 84681 Log: The "if 1": trick seems cleaner that the one with regular expressions. Use it here again. Modified: python/branches/py3k/Lib/test/test_syntax.py Modified: python/branches/py3k/Lib/test/test_syntax.py ============================================================================== --- python/branches/py3k/Lib/test/test_syntax.py (original) +++ python/branches/py3k/Lib/test/test_syntax.py Fri Sep 10 21:47:43 2010 @@ -550,13 +550,13 @@ def test_global_err_then_warn(self): # Bug tickler: The SyntaxError raised for one global statement # shouldn't be clobbered by a SyntaxWarning issued for a later one. - source = re.sub('(?m)^ *:', '', """\ - :def error(a): - : global a # SyntaxError - :def warning(): - : b = 1 - : global b # SyntaxWarning - :""") + source = """if 1: + def error(a): + global a # SyntaxError + def warning(): + b = 1 + global b # SyntaxWarning + """ warnings.filterwarnings(action='ignore', category=SyntaxWarning) self._check_error(source, "global") warnings.filters.pop(0) @@ -565,12 +565,12 @@ self._check_error("break", "outside loop") def test_delete_deref(self): - source = re.sub('(?m)^ *:', '', """\ - :def foo(x): - : def bar(): - : print(x) - : del x - :""") + source = """if 1: + def foo(x): + def bar(): + print(x) + del x + """ self._check_error(source, "nested scope") def test_unexpected_indent(self): From python-checkins at python.org Fri Sep 10 21:55:20 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 10 Sep 2010 21:55:20 +0200 (CEST) Subject: [Python-checkins] r84682 - in python/branches/release31-maint: Lib/distutils/command/build_ext.py Makefile.pre.in Misc/NEWS Modules/ld_so_aix Modules/ld_so_aix.in configure configure.in Message-ID: <20100910195520.3D354EE9A0@mail.python.org> Author: antoine.pitrou Date: Fri Sep 10 21:55:19 2010 New Revision: 84682 Log: Merged revisions 84680 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84680 | antoine.pitrou | 2010-09-10 21:44:44 +0200 (ven., 10 sept. 2010) | 4 lines Issue #941346: Improve the build process under AIX and allow Python to be built as a shared library. Patch by S?bastien Sabl?. ........ Added: python/branches/release31-maint/Modules/ld_so_aix.in - copied unchanged from r84680, /python/branches/py3k/Modules/ld_so_aix.in Removed: python/branches/release31-maint/Modules/ld_so_aix Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/distutils/command/build_ext.py python/branches/release31-maint/Makefile.pre.in python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/configure python/branches/release31-maint/configure.in Modified: python/branches/release31-maint/Lib/distutils/command/build_ext.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/command/build_ext.py (original) +++ python/branches/release31-maint/Lib/distutils/command/build_ext.py Fri Sep 10 21:55:19 2010 @@ -748,6 +748,9 @@ elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries + elif sys.platform[:3] == 'aix': + # Don't use the default code below + return ext.libraries else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): Modified: python/branches/release31-maint/Makefile.pre.in ============================================================================== --- python/branches/release31-maint/Makefile.pre.in (original) +++ python/branches/release31-maint/Makefile.pre.in Fri Sep 10 21:55:19 2010 @@ -435,10 +435,10 @@ libpython$(VERSION).so: $(LIBRARY_OBJS) if test $(INSTSONAME) != $(LDLIBRARY); then \ - $(LDSHARED) $(LDFLAGS) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ + $(BLDSHARED) $(LDFLAGS) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ $(LN) -f $(INSTSONAME) $@; \ else \ - $(LDSHARED) $(LDFLAGS) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ + $(BLDSHARED) $(LDFLAGS) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ fi libpython$(VERSION).dylib: $(LIBRARY_OBJS) @@ -1189,7 +1189,7 @@ distclean: clobber -rm -f core Makefile Makefile.pre config.status \ Modules/Setup Modules/Setup.local Modules/Setup.config \ - Misc/python.pc + Modules/ld_so_aix Misc/python.pc find $(srcdir) '(' -name '*.fdc' -o -name '*~' \ -o -name '[@,#]*' -o -name '*.old' \ -o -name '*.orig' -o -name '*.rej' \ Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Fri Sep 10 21:55:19 2010 @@ -552,6 +552,9 @@ Build ----- +- Issue #941346: Improve the build process under AIX and allow Python to + be built as a shared library. Patch by S?bastien Sabl?. + - Issue #4026: Make the fcntl extension build under AIX. Patch by S?bastien Sabl?. Deleted: python/branches/release31-maint/Modules/ld_so_aix ============================================================================== --- python/branches/release31-maint/Modules/ld_so_aix Fri Sep 10 21:55:19 2010 +++ (empty file) @@ -1,190 +0,0 @@ -#!/bin/sh -# -# ======================================================================== -# FILE: ld_so_aix -# TYPE: executable, uses makexp_aix -# SYSTEM: AIX -# -# DESCRIPTION: Creates a shareable .o from a set of pre-compiled -# (unshared) .o files -# -# USAGE: ld_so_aix [CC] [arguments] -# -# ARGUMENTS: Same as for "ld". The following arguments are processed -# or supplied by this script (those marked with an asterisk -# can be overriden from command line): -# -# Argument Default value -# (*) -o [OutputFileName] -o shr.o -# (*) -e [EntryPointLabel] -e init[OutputBaseName] -# (*) -bE:[ExportFile] -bE:[OutputBaseName].exp -# (*) -bI:[ImportFile] -bI:./python.exp -# -bM:[ModuleType] -bM:SRE -# -bhalt:[Number] -bhalt:4 -# -T[Number] -T512 -# -H[Number] -H512 -# -lm -# -# The compiler specific ("-lc" or "-lc_r", "-lpthreads",...) -# arguments will be automatically passed to "ld" according -# to the CC command provided as a first argument to this -# script. Usually, the same CC command was used to produce -# the pre-compiled .o file(s). -# -# NOTES: 1. Since "ld_so_aix" was originally written for building -# shared modules for the Python interpreter, the -e and -# -bI default values match Python's conventions. In -# Python, the entry point for a shared module is based -# on the module's name (e.g., the "mathmodule" will -# expect an entry point of "initmath"). -# 2. The script accepts multiple .o or .a input files and -# creates a single (shared) output file. The export list -# that is created is based on the output file's basename -# with the suffix ".exp". -# 3. The resulting shared object file is left in the -# current directory. -# 4. Uncommenting the "echo" lines gives detailed output -# about the commands executed in the script. -# -# -# HISTORY: Oct-1996 -- Support added for multiple .o files -- -# -- and optional arguments processing. -- -# Chris Myers (myers at tc.cornell.edu), Keith Kwok -# (kkwok at tc.cornell.edu) and Vladimir Marangozov -# -# Aug-6-1996 -- Take care of the compiler specific -- -# -- args by leaving CC to invoke "ld". -- -# Vladimir Marangozov -# -# Jul-1-1996 -- Make sure to use /usr/ccs/bin/ld -- -# -- Use makexp_aix for the export list. -- -# Vladimir Marangozov (Vladimir.Marangozov at imag.fr) -# -# Manus Hand (mhand at csn.net) -- Initial code -- 6/24/96 -# ======================================================================== -# - -usage="Usage: ld_so_aix [CC command] [ld arguments]" -if test ! -n "$*"; then - echo $usage; exit 2 -fi - -makexp=`dirname $0`/makexp_aix - -# Check for existence of compiler. -CC=$1; shift -whichcc=`which $CC` - -if test ! -x "$whichcc"; then - echo "ld_so_aix: Compiler '$CC' not found; exiting." - exit 2 -fi - -if test ! -n "$*"; then - echo $usage; exit 2 -fi - -# Default import file for Python -# Can be overriden by providing a -bI: argument. -impfile="./python.exp" - -# Parse arguments -while test -n "$1" -do - case "$1" in - -e | -Wl,-e) - if test -z "$2"; then - echo "ld_so_aix: The -e flag needs a parameter; exiting."; exit 2 - else - shift; entry=$1 - fi - ;; - -e* | -Wl,-e*) - entry=`echo $1 | sed -e "s/-Wl,//" -e "s/-e//"` - ;; - -o) - if test -z "$2"; then - echo "ld_so_aix: The -o flag needs a parameter; exiting."; exit 2 - else - shift; objfile=$1 - fi - ;; - -o*) - objfile=`echo $1 | sed "s/-o//"` - ;; - -bI:* | -Wl,-bI:*) - impfile=`echo $1 | sed -e "s/-Wl,//" -e "s/-bI://"` - ;; - -bE:* | -Wl,-bE:*) - expfile=`echo $1 | sed -e "s/-Wl,//" -e "s/-bE://"` - ;; - *.o | *.a) - objs="$objs $1" - args="$args $1" - ;; - -bM:* | -Wl,-bM:* | -H* | -Wl,-H* | -T* | -Wl,-T* | -lm) - ;; - *) - args="$args $1" - ;; - esac - shift -done - - -if test -z "$objs"; then - echo "ld_so_aix: No input files; exiting." - exit 2 -elif test ! -r "$impfile"; then - echo "ld_so_aix: Import file '$impfile' not found or not readable; exiting." - exit 2 -fi - -# If -o wasn't specified, assume "-o shr.o" -if test -z "$objfile"; then - objfile=shr.o -fi - -filename=`basename $objfile | sed "s/\.[^.]*$//"` - -# If -bE: wasn't specified, assume "-bE:$filename.exp" -if test -z "$expfile"; then - expfile="$filename.exp" -fi - -# Default entry symbol for Python modules = init[modulename] -# Can be overriden by providing a -e argument. -if test -z "$entry"; then - entry=init`echo $filename | sed "s/module.*//"` -fi - -#echo "ld_so_aix: Debug info section" -#echo " -> output file : $objfile" -#echo " -> import file : $impfile" -#echo " -> export file : $expfile" -#echo " -> entry point : $entry" -#echo " -> object files: $objs" -#echo " -> CC arguments: $args" - -CCOPT="-Wl,-e$entry -Wl,-bE:$expfile -Wl,-bI:$impfile -Wl,-bhalt:4" -CCOPT="$CCOPT -Wl,-bM:SRE -Wl,-T512 -Wl,-H512 -lm -o $objfile" -# Note: to use dynamic libraries like libtcl8.4.so and libtk8.4.so -# you may need to replace the second CCOPT line above with the following: -# CCOPT="$CCOPT -Wl,-bM:SRE -Wl,-T512 -Wl,-H512 -brtl -bnortllib -lm -o $objfile" - -CCARGS="$args" - -# Export list generation. -#echo $makexp $expfile "$objfile" $objs -$makexp $expfile "$objfile" $objs - -# Perform the link. -#echo $CC $CCOPT $CCARGS -$CC $CCOPT $CCARGS -retval=$? - -# Delete the module's export list file. -# Comment this line if you need it. -rm -f $expfile - -exit $retval Modified: python/branches/release31-maint/configure ============================================================================== --- python/branches/release31-maint/configure (original) +++ python/branches/release31-maint/configure Fri Sep 10 21:55:19 2010 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 84586 . +# From configure.in Revision: 84675 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.65 for python 3.1. # @@ -4870,6 +4870,10 @@ BLDLIBRARY='-L. -lpython$(VERSION)' RUNSHARED='DYLD_LIBRARY_PATH=`pwd`:${DYLD_LIBRARY_PATH}' ;; + AIX*) + LDLIBRARY='libpython$(VERSION).so' + RUNSHARED=LIBPATH=`pwd`:${LIBPATH} + ;; esac else # shared is disabled @@ -7472,7 +7476,7 @@ then case $ac_sys_system/$ac_sys_release in AIX*) - BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp" + BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp -L\$(srcdir)" LDSHARED="\$(BINLIBDEST)/config/ld_so_aix \$(CC) -bI:\$(BINLIBDEST)/config/python.exp" ;; IRIX/5*) LDSHARED="ld -shared";; @@ -13380,6 +13384,8 @@ # generate output files ac_config_files="$ac_config_files Makefile.pre Modules/Setup.config Misc/python.pc" +ac_config_files="$ac_config_files Modules/ld_so_aix" + cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure @@ -14072,6 +14078,7 @@ "Makefile.pre") CONFIG_FILES="$CONFIG_FILES Makefile.pre" ;; "Modules/Setup.config") CONFIG_FILES="$CONFIG_FILES Modules/Setup.config" ;; "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; + "Modules/ld_so_aix") CONFIG_FILES="$CONFIG_FILES Modules/ld_so_aix" ;; *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac @@ -14611,6 +14618,11 @@ esac + + case $ac_file$ac_mode in + "Modules/ld_so_aix":F) chmod +x Modules/ld_so_aix ;; + + esac done # for ac_tag Modified: python/branches/release31-maint/configure.in ============================================================================== --- python/branches/release31-maint/configure.in (original) +++ python/branches/release31-maint/configure.in Fri Sep 10 21:55:19 2010 @@ -748,6 +748,10 @@ BLDLIBRARY='-L. -lpython$(VERSION)' RUNSHARED='DYLD_LIBRARY_PATH=`pwd`:${DYLD_LIBRARY_PATH}' ;; + AIX*) + LDLIBRARY='libpython$(VERSION).so' + RUNSHARED=LIBPATH=`pwd`:${LIBPATH} + ;; esac else # shared is disabled @@ -1723,7 +1727,7 @@ then case $ac_sys_system/$ac_sys_release in AIX*) - BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp" + BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp -L\$(srcdir)" LDSHARED="\$(BINLIBDEST)/config/ld_so_aix \$(CC) -bI:\$(BINLIBDEST)/config/python.exp" ;; IRIX/5*) LDSHARED="ld -shared";; @@ -4106,6 +4110,7 @@ # generate output files AC_CONFIG_FILES(Makefile.pre Modules/Setup.config Misc/python.pc) +AC_CONFIG_FILES([Modules/ld_so_aix], [chmod +x Modules/ld_so_aix]) AC_OUTPUT echo "creating Modules/Setup" From python-checkins at python.org Fri Sep 10 22:03:17 2010 From: python-checkins at python.org (antoine.pitrou) Date: Fri, 10 Sep 2010 22:03:17 +0200 (CEST) Subject: [Python-checkins] r84683 - in python/branches/release27-maint: Lib/distutils/command/build_ext.py Makefile.pre.in Misc/NEWS Modules/ld_so_aix Modules/ld_so_aix.in configure configure.in Message-ID: <20100910200317.8FBAFF46E@mail.python.org> Author: antoine.pitrou Date: Fri Sep 10 22:03:17 2010 New Revision: 84683 Log: Merged revisions 84680 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84680 | antoine.pitrou | 2010-09-10 21:44:44 +0200 (ven., 10 sept. 2010) | 4 lines Issue #941346: Improve the build process under AIX and allow Python to be built as a shared library. Patch by S?bastien Sabl?. ........ Added: python/branches/release27-maint/Modules/ld_so_aix.in - copied unchanged from r84680, /python/branches/py3k/Modules/ld_so_aix.in Removed: python/branches/release27-maint/Modules/ld_so_aix Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/distutils/command/build_ext.py python/branches/release27-maint/Makefile.pre.in python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/configure python/branches/release27-maint/configure.in Modified: python/branches/release27-maint/Lib/distutils/command/build_ext.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/command/build_ext.py (original) +++ python/branches/release27-maint/Lib/distutils/command/build_ext.py Fri Sep 10 22:03:17 2010 @@ -753,7 +753,9 @@ elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries - + elif sys.platform[:3] == 'aix': + # Don't use the default code below + return ext.libraries else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): Modified: python/branches/release27-maint/Makefile.pre.in ============================================================================== --- python/branches/release27-maint/Makefile.pre.in (original) +++ python/branches/release27-maint/Makefile.pre.in Fri Sep 10 22:03:17 2010 @@ -422,10 +422,10 @@ libpython$(VERSION).so: $(LIBRARY_OBJS) if test $(INSTSONAME) != $(LDLIBRARY); then \ - $(LDSHARED) $(LDFLAGS) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ + $(BLDSHARED) $(LDFLAGS) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ $(LN) -f $(INSTSONAME) $@; \ else \ - $(LDSHARED) $(LDFLAGS) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ + $(BLDSHARED) $(LDFLAGS) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ fi libpython$(VERSION).dylib: $(LIBRARY_OBJS) @@ -1197,7 +1197,7 @@ done -rm -f core Makefile Makefile.pre config.status \ Modules/Setup Modules/Setup.local Modules/Setup.config \ - Misc/python.pc + Modules/ld_so_aix Misc/python.pc -rm -f python*-gdb.py find $(srcdir) '(' -name '*.fdc' -o -name '*~' \ -o -name '[@,#]*' -o -name '*.old' \ Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Fri Sep 10 22:03:17 2010 @@ -302,6 +302,9 @@ Build ----- +- Issue #941346: Improve the build process under AIX and allow Python to + be built as a shared library. Patch by S?bastien Sabl?. + - Issue #4026: Make the fcntl extension build under AIX. Patch by S?bastien Sabl?. Deleted: python/branches/release27-maint/Modules/ld_so_aix ============================================================================== --- python/branches/release27-maint/Modules/ld_so_aix Fri Sep 10 22:03:17 2010 +++ (empty file) @@ -1,190 +0,0 @@ -#!/bin/sh -# -# ======================================================================== -# FILE: ld_so_aix -# TYPE: executable, uses makexp_aix -# SYSTEM: AIX -# -# DESCRIPTION: Creates a shareable .o from a set of pre-compiled -# (unshared) .o files -# -# USAGE: ld_so_aix [CC] [arguments] -# -# ARGUMENTS: Same as for "ld". The following arguments are processed -# or supplied by this script (those marked with an asterisk -# can be overriden from command line): -# -# Argument Default value -# (*) -o [OutputFileName] -o shr.o -# (*) -e [EntryPointLabel] -e init[OutputBaseName] -# (*) -bE:[ExportFile] -bE:[OutputBaseName].exp -# (*) -bI:[ImportFile] -bI:./python.exp -# -bM:[ModuleType] -bM:SRE -# -bhalt:[Number] -bhalt:4 -# -T[Number] -T512 -# -H[Number] -H512 -# -lm -# -# The compiler specific ("-lc" or "-lc_r", "-lpthreads",...) -# arguments will be automatically passed to "ld" according -# to the CC command provided as a first argument to this -# script. Usually, the same CC command was used to produce -# the pre-compiled .o file(s). -# -# NOTES: 1. Since "ld_so_aix" was originally written for building -# shared modules for the Python interpreter, the -e and -# -bI default values match Python's conventions. In -# Python, the entry point for a shared module is based -# on the module's name (e.g., the "mathmodule" will -# expect an entry point of "initmath"). -# 2. The script accepts multiple .o or .a input files and -# creates a single (shared) output file. The export list -# that is created is based on the output file's basename -# with the suffix ".exp". -# 3. The resulting shared object file is left in the -# current directory. -# 4. Uncommenting the "echo" lines gives detailed output -# about the commands executed in the script. -# -# -# HISTORY: Oct-1996 -- Support added for multiple .o files -- -# -- and optional arguments processing. -- -# Chris Myers (myers at tc.cornell.edu), Keith Kwok -# (kkwok at tc.cornell.edu) and Vladimir Marangozov -# -# Aug-6-1996 -- Take care of the compiler specific -- -# -- args by leaving CC to invoke "ld". -- -# Vladimir Marangozov -# -# Jul-1-1996 -- Make sure to use /usr/ccs/bin/ld -- -# -- Use makexp_aix for the export list. -- -# Vladimir Marangozov (Vladimir.Marangozov at imag.fr) -# -# Manus Hand (mhand at csn.net) -- Initial code -- 6/24/96 -# ======================================================================== -# - -usage="Usage: ld_so_aix [CC command] [ld arguments]" -if test ! -n "$*"; then - echo $usage; exit 2 -fi - -makexp=`dirname $0`/makexp_aix - -# Check for existence of compiler. -CC=$1; shift -whichcc=`which $CC` - -if test ! -x "$whichcc"; then - echo "ld_so_aix: Compiler '$CC' not found; exiting." - exit 2 -fi - -if test ! -n "$*"; then - echo $usage; exit 2 -fi - -# Default import file for Python -# Can be overriden by providing a -bI: argument. -impfile="./python.exp" - -# Parse arguments -while test -n "$1" -do - case "$1" in - -e | -Wl,-e) - if test -z "$2"; then - echo "ld_so_aix: The -e flag needs a parameter; exiting."; exit 2 - else - shift; entry=$1 - fi - ;; - -e* | -Wl,-e*) - entry=`echo $1 | sed -e "s/-Wl,//" -e "s/-e//"` - ;; - -o) - if test -z "$2"; then - echo "ld_so_aix: The -o flag needs a parameter; exiting."; exit 2 - else - shift; objfile=$1 - fi - ;; - -o*) - objfile=`echo $1 | sed "s/-o//"` - ;; - -bI:* | -Wl,-bI:*) - impfile=`echo $1 | sed -e "s/-Wl,//" -e "s/-bI://"` - ;; - -bE:* | -Wl,-bE:*) - expfile=`echo $1 | sed -e "s/-Wl,//" -e "s/-bE://"` - ;; - *.o | *.a) - objs="$objs $1" - args="$args $1" - ;; - -bM:* | -Wl,-bM:* | -H* | -Wl,-H* | -T* | -Wl,-T* | -lm) - ;; - *) - args="$args $1" - ;; - esac - shift -done - - -if test -z "$objs"; then - echo "ld_so_aix: No input files; exiting." - exit 2 -elif test ! -r "$impfile"; then - echo "ld_so_aix: Import file '$impfile' not found or not readable; exiting." - exit 2 -fi - -# If -o wasn't specified, assume "-o shr.o" -if test -z "$objfile"; then - objfile=shr.o -fi - -filename=`basename $objfile | sed "s/\.[^.]*$//"` - -# If -bE: wasn't specified, assume "-bE:$filename.exp" -if test -z "$expfile"; then - expfile="$filename.exp" -fi - -# Default entry symbol for Python modules = init[modulename] -# Can be overriden by providing a -e argument. -if test -z "$entry"; then - entry=init`echo $filename | sed "s/module.*//"` -fi - -#echo "ld_so_aix: Debug info section" -#echo " -> output file : $objfile" -#echo " -> import file : $impfile" -#echo " -> export file : $expfile" -#echo " -> entry point : $entry" -#echo " -> object files: $objs" -#echo " -> CC arguments: $args" - -CCOPT="-Wl,-e$entry -Wl,-bE:$expfile -Wl,-bI:$impfile -Wl,-bhalt:4" -CCOPT="$CCOPT -Wl,-bM:SRE -Wl,-T512 -Wl,-H512 -lm -o $objfile" -# Note: to use dynamic libraries like libtcl8.4.so and libtk8.4.so -# you may need to replace the second CCOPT line above with the following: -# CCOPT="$CCOPT -Wl,-bM:SRE -Wl,-T512 -Wl,-H512 -brtl -bnortllib -lm -o $objfile" - -CCARGS="$args" - -# Export list generation. -#echo $makexp $expfile "$objfile" $objs -$makexp $expfile "$objfile" $objs - -# Perform the link. -#echo $CC $CCOPT $CCARGS -$CC $CCOPT $CCARGS -retval=$? - -# Delete the module's export list file. -# Comment this line if you need it. -rm -f $expfile - -exit $retval Modified: python/branches/release27-maint/configure ============================================================================== --- python/branches/release27-maint/configure (original) +++ python/branches/release27-maint/configure Fri Sep 10 22:03:17 2010 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 84585 . +# From configure.in Revision: 84676 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.65 for python 2.7. # @@ -4950,6 +4950,10 @@ BLDLIBRARY='-L. -lpython$(VERSION)' RUNSHARED='DYLD_LIBRARY_PATH=`pwd`:${DYLD_LIBRARY_PATH}' ;; + AIX*) + LDLIBRARY='libpython$(VERSION).so' + RUNSHARED=LIBPATH=`pwd`:${LIBPATH} + ;; esac else # shared is disabled @@ -7531,7 +7535,7 @@ then case $ac_sys_system/$ac_sys_release in AIX*) - BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp" + BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp -L\$(srcdir)" LDSHARED="\$(BINLIBDEST)/config/ld_so_aix \$(CC) -bI:\$(BINLIBDEST)/config/python.exp" ;; BeOS*) @@ -13753,6 +13757,8 @@ # generate output files ac_config_files="$ac_config_files Makefile.pre Modules/Setup.config Misc/python.pc" +ac_config_files="$ac_config_files Modules/ld_so_aix" + cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure @@ -14446,6 +14452,7 @@ "Makefile.pre") CONFIG_FILES="$CONFIG_FILES Makefile.pre" ;; "Modules/Setup.config") CONFIG_FILES="$CONFIG_FILES Modules/Setup.config" ;; "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; + "Modules/ld_so_aix") CONFIG_FILES="$CONFIG_FILES Modules/ld_so_aix" ;; *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac @@ -14985,6 +14992,11 @@ esac + + case $ac_file$ac_mode in + "Modules/ld_so_aix":F) chmod +x Modules/ld_so_aix ;; + + esac done # for ac_tag Modified: python/branches/release27-maint/configure.in ============================================================================== --- python/branches/release27-maint/configure.in (original) +++ python/branches/release27-maint/configure.in Fri Sep 10 22:03:17 2010 @@ -823,6 +823,10 @@ BLDLIBRARY='-L. -lpython$(VERSION)' RUNSHARED='DYLD_LIBRARY_PATH=`pwd`:${DYLD_LIBRARY_PATH}' ;; + AIX*) + LDLIBRARY='libpython$(VERSION).so' + RUNSHARED=LIBPATH=`pwd`:${LIBPATH} + ;; esac else # shared is disabled @@ -1757,7 +1761,7 @@ then case $ac_sys_system/$ac_sys_release in AIX*) - BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp" + BLDSHARED="\$(srcdir)/Modules/ld_so_aix \$(CC) -bI:Modules/python.exp -L\$(srcdir)" LDSHARED="\$(BINLIBDEST)/config/ld_so_aix \$(CC) -bI:\$(BINLIBDEST)/config/python.exp" ;; BeOS*) @@ -4273,6 +4277,7 @@ # generate output files AC_CONFIG_FILES(Makefile.pre Modules/Setup.config Misc/python.pc) +AC_CONFIG_FILES([Modules/ld_so_aix], [chmod +x Modules/ld_so_aix]) AC_OUTPUT echo "creating Modules/Setup" From python-checkins at python.org Fri Sep 10 22:43:53 2010 From: python-checkins at python.org (georg.brandl) Date: Fri, 10 Sep 2010 22:43:53 +0200 (CEST) Subject: [Python-checkins] r84684 - python/branches/py3k/Doc/library/stdtypes.rst Message-ID: <20100910204353.7E8E0DDAF@mail.python.org> Author: georg.brandl Date: Fri Sep 10 22:43:53 2010 New Revision: 84684 Log: release() is probably not the most important method Modified: python/branches/py3k/Doc/library/stdtypes.rst Modified: python/branches/py3k/Doc/library/stdtypes.rst ============================================================================== --- python/branches/py3k/Doc/library/stdtypes.rst (original) +++ python/branches/py3k/Doc/library/stdtypes.rst Fri Sep 10 22:43:53 2010 @@ -2313,6 +2313,24 @@ :class:`memoryview` has several methods: + .. method:: tobytes() + + Return the data in the buffer as a bytestring. This is equivalent to + calling the :class:`bytes` constructor on the memoryview. :: + + >>> m = memoryview(b"abc") + >>> m.tobytes() + b'abc' + >>> bytes(m) + b'abc' + + .. method:: tolist() + + Return the data in the buffer as a list of integers. :: + + >>> memoryview(b'abc').tolist() + [97, 98, 99] + .. method:: release() Release the underlying buffer exposed by the memoryview object. Many @@ -2346,24 +2364,6 @@ .. versionadded:: 3.2 - .. method:: tobytes() - - Return the data in the buffer as a bytestring. This is equivalent to - calling the :class:`bytes` constructor on the memoryview. :: - - >>> m = memoryview(b"abc") - >>> m.tobytes() - b'abc' - >>> bytes(m) - b'abc' - - .. method:: tolist() - - Return the data in the buffer as a list of integers. :: - - >>> memoryview(b'abc').tolist() - [97, 98, 99] - There are also several readonly attributes available: .. attribute:: format From python-checkins at python.org Fri Sep 10 23:39:54 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Fri, 10 Sep 2010 23:39:54 +0200 (CEST) Subject: [Python-checkins] r84685 - in python/branches/py3k: Doc/library/dis.rst Doc/reference/simple_stmts.rst Doc/whatsnew/3.2.rst Include/opcode.h Lib/opcode.py Lib/test/test_exceptions.py Lib/test/test_scope.py Lib/test/test_syntax.py Misc/NEWS Python/ceval.c Python/compile.c Python/opcode_targets.h Message-ID: <20100910213954.0BD48EEA38@mail.python.org> Author: amaury.forgeotdarc Date: Fri Sep 10 23:39:53 2010 New Revision: 84685 Log: #4617: Previously it was illegal to delete a name from the local namespace if it occurs as a free variable in a nested block. This limitation of the compiler has been lifted, and a new opcode introduced (DELETE_DEREF). This sample was valid in 2.6, but fails to compile in 3.x without this change:: >>> def f(): ... def print_error(): ... print(e) ... try: ... something ... except Exception as e: ... print_error() ... # implicit "del e" here This sample has always been invalid in Python, and now works:: >>> def outer(x): ... def inner(): ... return x ... inner() ... del x There is no need to bump the PYC magic number: the new opcode is used for code that did not compile before. Modified: python/branches/py3k/Doc/library/dis.rst python/branches/py3k/Doc/reference/simple_stmts.rst python/branches/py3k/Doc/whatsnew/3.2.rst python/branches/py3k/Include/opcode.h python/branches/py3k/Lib/opcode.py python/branches/py3k/Lib/test/test_exceptions.py python/branches/py3k/Lib/test/test_scope.py python/branches/py3k/Lib/test/test_syntax.py python/branches/py3k/Misc/NEWS python/branches/py3k/Python/ceval.c python/branches/py3k/Python/compile.c python/branches/py3k/Python/opcode_targets.h Modified: python/branches/py3k/Doc/library/dis.rst ============================================================================== --- python/branches/py3k/Doc/library/dis.rst (original) +++ python/branches/py3k/Doc/library/dis.rst Fri Sep 10 23:39:53 2010 @@ -723,6 +723,12 @@ storage. +.. opcode:: DELETE_DEREF (i) + + Empties the cell contained in slot *i* of the cell and free variable storage. + Used by the :keyword:`del` statement. + + .. opcode:: SET_LINENO (lineno) This opcode is obsolete. Modified: python/branches/py3k/Doc/reference/simple_stmts.rst ============================================================================== --- python/branches/py3k/Doc/reference/simple_stmts.rst (original) +++ python/branches/py3k/Doc/reference/simple_stmts.rst Fri Sep 10 23:39:53 2010 @@ -388,11 +388,6 @@ in the same code block. If the name is unbound, a :exc:`NameError` exception will be raised. -.. index:: pair: free; variable - -It is illegal to delete a name from the local namespace if it occurs as a free -variable in a nested block. - .. index:: pair: attribute; deletion Deletion of attribute references, subscriptions and slicings is passed to the @@ -400,6 +395,10 @@ assignment of an empty slice of the right type (but even this is determined by the sliced object). +.. versionchanged:: 3.2 + Previously it was illegal to delete a name from the local namespace if it + occurs as a free variable in a nested block. + .. _return: Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Fri Sep 10 23:39:53 2010 @@ -240,6 +240,31 @@ (See :issue:`8188`.) +* Previously it was illegal to delete a name from the local namespace if it + occurs as a free variable in a nested block:: + + >>> def outer(x): + ... def inner(): + ... return x + ... inner() + ... del x + + This is now allowed. Remember that the target of an :keyword:`except` clause + is cleared, so this code which used to work with Python 2.6, raised a + :exc:`SyntaxError` with Python 3.1 and now works again:: + + >>> def f(): + ... def print_error(): + ... print(e) + ... try: + ... something + ... except Exception as e: + ... print_error() + ... # implicit "del e" here + + (See :issue:`4617`.) + + New, Improved, and Deprecated Modules ===================================== Modified: python/branches/py3k/Include/opcode.h ============================================================================== --- python/branches/py3k/Include/opcode.h (original) +++ python/branches/py3k/Include/opcode.h Fri Sep 10 23:39:53 2010 @@ -123,6 +123,7 @@ #define LOAD_CLOSURE 135 /* Load free variable from closure */ #define LOAD_DEREF 136 /* Load and dereference from closure cell */ #define STORE_DEREF 137 /* Store into cell */ +#define DELETE_DEREF 138 /* Delete closure cell */ /* The next 3 opcodes must be contiguous and satisfy (CALL_FUNCTION_VAR - CALL_FUNCTION) & 3 == 1 */ Modified: python/branches/py3k/Lib/opcode.py ============================================================================== --- python/branches/py3k/Lib/opcode.py (original) +++ python/branches/py3k/Lib/opcode.py Fri Sep 10 23:39:53 2010 @@ -161,6 +161,8 @@ hasfree.append(136) def_op('STORE_DEREF', 137) hasfree.append(137) +def_op('DELETE_DEREF', 138) +hasfree.append(138) def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) Modified: python/branches/py3k/Lib/test/test_exceptions.py ============================================================================== --- python/branches/py3k/Lib/test/test_exceptions.py (original) +++ python/branches/py3k/Lib/test/test_exceptions.py Fri Sep 10 23:39:53 2010 @@ -526,6 +526,17 @@ obj = wr() self.assertTrue(obj is None, "%s" % obj) + def test_exception_target_in_nested_scope(self): + # issue 4617: This used to raise a SyntaxError + # "can not delete variable 'e' referenced in nested scope" + def print_error(): + e + try: + something + except Exception as e: + print_error() + # implicit "del e" here + def test_generator_leaking(self): # Test that generator exception state doesn't leak into the calling # frame Modified: python/branches/py3k/Lib/test/test_scope.py ============================================================================== --- python/branches/py3k/Lib/test/test_scope.py (original) +++ python/branches/py3k/Lib/test/test_scope.py Fri Sep 10 23:39:53 2010 @@ -218,13 +218,6 @@ """) check_syntax_error(self, """if 1: - def f(x): - def g(): - return x - del x # can't del name - """) - - check_syntax_error(self, """if 1: def f(): def g(): from sys import * @@ -272,6 +265,28 @@ self.assertRaises(UnboundLocalError, errorInOuter) self.assertRaises(NameError, errorInInner) + def testUnboundLocal_AfterDel(self): + # #4617: It is now legal to delete a cell variable. + # The following functions must obviously compile, + # and give the correct error when accessing the deleted name. + def errorInOuter(): + y = 1 + del y + print(y) + def inner(): + return y + + def errorInInner(): + def inner(): + return y + y = 1 + del y + inner() + + self.assertRaises(UnboundLocalError, errorInOuter) + self.assertRaises(NameError, errorInInner) + + def testUnboundLocal_AugAssign(self): # test for bug #1501934: incorrect LOAD/STORE_GLOBAL generation exec("""if 1: global_x = 1 Modified: python/branches/py3k/Lib/test/test_syntax.py ============================================================================== --- python/branches/py3k/Lib/test/test_syntax.py (original) +++ python/branches/py3k/Lib/test/test_syntax.py Fri Sep 10 23:39:53 2010 @@ -564,15 +564,6 @@ def test_break_outside_loop(self): self._check_error("break", "outside loop") - def test_delete_deref(self): - source = """if 1: - def foo(x): - def bar(): - print(x) - del x - """ - self._check_error(source, "nested scope") - def test_unexpected_indent(self): self._check_error("foo()\n bar()\n", "unexpected indent", subclass=IndentationError) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Sep 10 23:39:53 2010 @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #4617: Previously it was illegal to delete a name from the local + namespace if it occurs as a free variable in a nested block. This limitation + of the compiler has been lifted, and a new opcode introduced (DELETE_DEREF). + - Issue #9804: ascii() now always represents unicode surrogate pairs as a single ``\UXXXXXXXX``, regardless of whether the character is printable or not. Also, the "backslashreplace" error handler now joins surrogate Modified: python/branches/py3k/Python/ceval.c ============================================================================== --- python/branches/py3k/Python/ceval.c (original) +++ python/branches/py3k/Python/ceval.c Fri Sep 10 23:39:53 2010 @@ -135,6 +135,7 @@ static PyObject * import_from(PyObject *, PyObject *); static int import_all_from(PyObject *, PyObject *); static void format_exc_check_arg(PyObject *, const char *, PyObject *); +static void format_exc_unbound(PyCodeObject *co, int oparg); static PyObject * unicode_concatenate(PyObject *, PyObject *, PyFrameObject *, unsigned char *); static PyObject * special_lookup(PyObject *, char *, PyObject **); @@ -2143,6 +2144,16 @@ ); break; + TARGET(DELETE_DEREF) + x = freevars[oparg]; + if (PyCell_GET(x) != NULL) { + PyCell_Set(x, NULL); + continue; + } + err = -1; + format_exc_unbound(co, oparg); + break; + TARGET(LOAD_CLOSURE) x = freevars[oparg]; Py_INCREF(x); @@ -2158,22 +2169,7 @@ DISPATCH(); } err = -1; - /* Don't stomp existing exception */ - if (PyErr_Occurred()) - break; - if (oparg < PyTuple_GET_SIZE(co->co_cellvars)) { - v = PyTuple_GET_ITEM(co->co_cellvars, - oparg); - format_exc_check_arg( - PyExc_UnboundLocalError, - UNBOUNDLOCAL_ERROR_MSG, - v); - } else { - v = PyTuple_GET_ITEM(co->co_freevars, oparg - - PyTuple_GET_SIZE(co->co_cellvars)); - format_exc_check_arg(PyExc_NameError, - UNBOUNDFREE_ERROR_MSG, v); - } + format_exc_unbound(co, oparg); break; TARGET(STORE_DEREF) @@ -4352,6 +4348,28 @@ PyErr_Format(exc, format_str, obj_str); } +static void +format_exc_unbound(PyCodeObject *co, int oparg) +{ + PyObject *name; + /* Don't stomp existing exception */ + if (PyErr_Occurred()) + return; + if (oparg < PyTuple_GET_SIZE(co->co_cellvars)) { + name = PyTuple_GET_ITEM(co->co_cellvars, + oparg); + format_exc_check_arg( + PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + name); + } else { + name = PyTuple_GET_ITEM(co->co_freevars, oparg - + PyTuple_GET_SIZE(co->co_cellvars)); + format_exc_check_arg(PyExc_NameError, + UNBOUNDFREE_ERROR_MSG, name); + } +} + static PyObject * unicode_concatenate(PyObject *v, PyObject *w, PyFrameObject *f, unsigned char *next_instr) Modified: python/branches/py3k/Python/compile.c ============================================================================== --- python/branches/py3k/Python/compile.c (original) +++ python/branches/py3k/Python/compile.c Fri Sep 10 23:39:53 2010 @@ -857,6 +857,8 @@ return 1; case STORE_DEREF: return -1; + case DELETE_DEREF: + return 0; default: fprintf(stderr, "opcode = %d\n", opcode); Py_FatalError("opcode_stack_effect()"); @@ -2506,13 +2508,7 @@ case AugLoad: case AugStore: break; - case Del: - PyErr_Format(PyExc_SyntaxError, - "can not delete variable '%S' referenced " - "in nested scope", - name); - Py_DECREF(mangled); - return 0; + case Del: op = DELETE_DEREF; break; case Param: default: PyErr_SetString(PyExc_SystemError, Modified: python/branches/py3k/Python/opcode_targets.h ============================================================================== --- python/branches/py3k/Python/opcode_targets.h (original) +++ python/branches/py3k/Python/opcode_targets.h Fri Sep 10 23:39:53 2010 @@ -137,7 +137,7 @@ &&TARGET_LOAD_CLOSURE, &&TARGET_LOAD_DEREF, &&TARGET_STORE_DEREF, - &&_unknown_opcode, + &&TARGET_DELETE_DEREF, &&_unknown_opcode, &&TARGET_CALL_FUNCTION_VAR, &&TARGET_CALL_FUNCTION_KW, From python-checkins at python.org Fri Sep 10 23:51:44 2010 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 10 Sep 2010 23:51:44 +0200 (CEST) Subject: [Python-checkins] r84686 - python/branches/py3k/Python/import.c Message-ID: <20100910215144.ACBF7EE9F0@mail.python.org> Author: benjamin.peterson Date: Fri Sep 10 23:51:44 2010 New Revision: 84686 Log: bump magic number for DELETE_DEREF Modified: python/branches/py3k/Python/import.c Modified: python/branches/py3k/Python/import.c ============================================================================== --- python/branches/py3k/Python/import.c (original) +++ python/branches/py3k/Python/import.c Fri Sep 10 23:51:44 2010 @@ -105,12 +105,13 @@ tag: cpython-32 Python 3.2a1: 3170 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR) tag: cpython-32 + Python 3.2a2 3180 (add DELETE_DEREF) */ /* If you change MAGIC, you must change TAG and you must insert the old value into _PyMagicNumberTags below. */ -#define MAGIC (3170 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (3180 | ((long)'\r'<<16) | ((long)'\n'<<24)) #define TAG "cpython-32" #define CACHEDIR "__pycache__" /* Current magic word and string tag as globals. */ From python-checkins at python.org Fri Sep 10 23:58:00 2010 From: python-checkins at python.org (victor.stinner) Date: Fri, 10 Sep 2010 23:58:00 +0200 (CEST) Subject: [Python-checkins] r84687 - in python/branches/py3k: Doc/library/sys.rst Include/fileobject.h Lib/test/test_sys.py Misc/NEWS Python/bltinmodule.c Python/sysmodule.c Message-ID: <20100910215800.161F3EE9F0@mail.python.org> Author: victor.stinner Date: Fri Sep 10 23:57:59 2010 New Revision: 84687 Log: Issue #9632: Remove sys.setfilesystemencoding() function: use PYTHONFSENCODING environment variable to set the filesystem encoding at Python startup. sys.setfilesystemencoding() creates inconsistencies because it is unable to reencode all filenames in all objects. Modified: python/branches/py3k/Doc/library/sys.rst python/branches/py3k/Include/fileobject.h python/branches/py3k/Lib/test/test_sys.py python/branches/py3k/Misc/NEWS python/branches/py3k/Python/bltinmodule.c python/branches/py3k/Python/sysmodule.c Modified: python/branches/py3k/Doc/library/sys.rst ============================================================================== --- python/branches/py3k/Doc/library/sys.rst (original) +++ python/branches/py3k/Doc/library/sys.rst Fri Sep 10 23:57:59 2010 @@ -724,15 +724,6 @@ :file:`/usr/include/dlfcn.h` using the :program:`h2py` script. Availability: Unix. -.. function:: setfilesystemencoding(enc) - - Set the encoding used when converting Python strings to file names to *enc*. - By default, Python tries to determine the encoding it should use automatically - on Unix; on Windows, it avoids such conversion completely. This function can - be used when Python's determination of the encoding needs to be overwritten, - e.g. when not all file names on disk can be decoded using the encoding that - Python had chosen. - .. function:: setprofile(profilefunc) .. index:: Modified: python/branches/py3k/Include/fileobject.h ============================================================================== --- python/branches/py3k/Include/fileobject.h (original) +++ python/branches/py3k/Include/fileobject.h Fri Sep 10 23:57:59 2010 @@ -21,7 +21,6 @@ */ PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; PyAPI_DATA(int) Py_HasFileSystemDefaultEncoding; -PyAPI_FUNC(int) _Py_SetFileSystemEncoding(PyObject *); /* Internal API Modified: python/branches/py3k/Lib/test/test_sys.py ============================================================================== --- python/branches/py3k/Lib/test/test_sys.py (original) +++ python/branches/py3k/Lib/test/test_sys.py Fri Sep 10 23:57:59 2010 @@ -630,17 +630,6 @@ env['PYTHONFSENCODING'] = encoding self.check_fsencoding(get_fsencoding(env), encoding) - def test_setfilesystemencoding(self): - old = sys.getfilesystemencoding() - try: - sys.setfilesystemencoding("iso-8859-1") - self.assertEqual(sys.getfilesystemencoding(), "iso-8859-1") - finally: - sys.setfilesystemencoding(old) - try: - self.assertRaises(LookupError, sys.setfilesystemencoding, "xxx") - finally: - sys.setfilesystemencoding(old) class SizeofTest(unittest.TestCase): Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Sep 10 23:57:59 2010 @@ -29,6 +29,11 @@ Library ------- +- Issue #9632: Remove sys.setfilesystemencoding() function: use + PYTHONFSENCODING environment variable to set the filesystem encoding at + Python startup. sys.setfilesystemencoding() creates inconsistencies because + it is unable to reencode all filenames in all objects. + - Issue #9410: Various optimizations to the pickle module, leading to speedups up to 4x (depending on the benchmark). Mostly ported from Unladen Swallow; initial patch by Alexandre Vassalotti. Modified: python/branches/py3k/Python/bltinmodule.c ============================================================================== --- python/branches/py3k/Python/bltinmodule.c (original) +++ python/branches/py3k/Python/bltinmodule.c Fri Sep 10 23:57:59 2010 @@ -33,29 +33,6 @@ int Py_HasFileSystemDefaultEncoding = 1; #endif -int -_Py_SetFileSystemEncoding(PyObject *s) -{ - PyObject *defenc, *codec; - if (!PyUnicode_Check(s)) { - PyErr_BadInternalCall(); - return -1; - } - defenc = _PyUnicode_AsDefaultEncodedString(s, NULL); - if (!defenc) - return -1; - codec = _PyCodec_Lookup(PyBytes_AsString(defenc)); - if (codec == NULL) - return -1; - Py_DECREF(codec); - if (!Py_HasFileSystemDefaultEncoding && Py_FileSystemDefaultEncoding) - /* A file system encoding was set at run-time */ - free((char*)Py_FileSystemDefaultEncoding); - Py_FileSystemDefaultEncoding = strdup(PyBytes_AsString(defenc)); - Py_HasFileSystemDefaultEncoding = 0; - return 0; -} - static PyObject * builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds) { Modified: python/branches/py3k/Python/sysmodule.c ============================================================================== --- python/branches/py3k/Python/sysmodule.c (original) +++ python/branches/py3k/Python/sysmodule.c Fri Sep 10 23:57:59 2010 @@ -199,25 +199,6 @@ ); static PyObject * -sys_setfilesystemencoding(PyObject *self, PyObject *args) -{ - PyObject *new_encoding; - if (!PyArg_ParseTuple(args, "U:setfilesystemencoding", &new_encoding)) - return NULL; - if (_Py_SetFileSystemEncoding(new_encoding)) - return NULL; - Py_INCREF(Py_None); - return Py_None; -} - -PyDoc_STRVAR(setfilesystemencoding_doc, -"setfilesystemencoding(string) -> None\n\ -\n\ -Set the encoding used to convert Unicode filenames in\n\ -operating system filenames." -); - -static PyObject * sys_intern(PyObject *self, PyObject *args) { PyObject *s; @@ -1012,8 +993,6 @@ #ifdef USE_MALLOPT {"mdebug", sys_mdebug, METH_VARARGS}, #endif - {"setfilesystemencoding", sys_setfilesystemencoding, METH_VARARGS, - setfilesystemencoding_doc}, {"setcheckinterval", sys_setcheckinterval, METH_VARARGS, setcheckinterval_doc}, {"getcheckinterval", sys_getcheckinterval, METH_NOARGS, From python-checkins at python.org Fri Sep 10 23:59:21 2010 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 10 Sep 2010 23:59:21 +0200 (CEST) Subject: [Python-checkins] r84688 - python/branches/py3k/Doc/reference/simple_stmts.rst Message-ID: <20100910215921.D456BEE9F0@mail.python.org> Author: benjamin.peterson Date: Fri Sep 10 23:59:21 2010 New Revision: 84688 Log: add newline Modified: python/branches/py3k/Doc/reference/simple_stmts.rst Modified: python/branches/py3k/Doc/reference/simple_stmts.rst ============================================================================== --- python/branches/py3k/Doc/reference/simple_stmts.rst (original) +++ python/branches/py3k/Doc/reference/simple_stmts.rst Fri Sep 10 23:59:21 2010 @@ -396,6 +396,7 @@ the sliced object). .. versionchanged:: 3.2 + Previously it was illegal to delete a name from the local namespace if it occurs as a free variable in a nested block. From python-checkins at python.org Sat Sep 11 00:02:31 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 11 Sep 2010 00:02:31 +0200 (CEST) Subject: [Python-checkins] r84689 - python/branches/py3k/Python/ceval.c Message-ID: <20100910220231.4C17CFB1E@mail.python.org> Author: benjamin.peterson Date: Sat Sep 11 00:02:31 2010 New Revision: 84689 Log: use DISPATCH() instead of continue Modified: python/branches/py3k/Python/ceval.c Modified: python/branches/py3k/Python/ceval.c ============================================================================== --- python/branches/py3k/Python/ceval.c (original) +++ python/branches/py3k/Python/ceval.c Sat Sep 11 00:02:31 2010 @@ -2148,7 +2148,7 @@ x = freevars[oparg]; if (PyCell_GET(x) != NULL) { PyCell_Set(x, NULL); - continue; + DISPATCH(); } err = -1; format_exc_unbound(co, oparg); From python-checkins at python.org Sat Sep 11 00:18:17 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 11 Sep 2010 00:18:17 +0200 (CEST) Subject: [Python-checkins] r84690 - in python/branches/py3k/Lib: os.py test/test_os.py Message-ID: <20100910221817.1AA39EEA2C@mail.python.org> Author: victor.stinner Date: Sat Sep 11 00:18:16 2010 New Revision: 84690 Log: Issue #8603: Environ.data is now protected -> Environ._data os.environ.data was a str dict in Python 3.1. In Python 3.2 on UNIX/BSD, os.environ.data is now a bytes dict: mark it as protected to avoid confusion. Modified: python/branches/py3k/Lib/os.py python/branches/py3k/Lib/test/test_os.py Modified: python/branches/py3k/Lib/os.py ============================================================================== --- python/branches/py3k/Lib/os.py (original) +++ python/branches/py3k/Lib/os.py Sat Sep 11 00:18:16 2010 @@ -420,34 +420,34 @@ self.decodevalue = decodevalue self.putenv = putenv self.unsetenv = unsetenv - self.data = data + self._data = data def __getitem__(self, key): - value = self.data[self.encodekey(key)] + value = self._data[self.encodekey(key)] return self.decodevalue(value) def __setitem__(self, key, value): key = self.encodekey(key) value = self.encodevalue(value) self.putenv(key, value) - self.data[key] = value + self._data[key] = value def __delitem__(self, key): key = self.encodekey(key) self.unsetenv(key) - del self.data[key] + del self._data[key] def __iter__(self): - for key in self.data: + for key in self._data: yield self.decodekey(key) def __len__(self): - return len(self.data) + return len(self._data) def __repr__(self): return 'environ({{{}}})'.format(', '.join( ('{!r}: {!r}'.format(self.decodekey(key), self.decodevalue(value)) - for key, value in self.data.items()))) + for key, value in self._data.items()))) def copy(self): return dict(self) @@ -521,7 +521,7 @@ return value # bytes environ - environb = _Environ(environ.data, + environb = _Environ(environ._data, _check_bytes, bytes, _check_bytes, bytes, _putenv, _unsetenv) Modified: python/branches/py3k/Lib/test/test_os.py ============================================================================== --- python/branches/py3k/Lib/test/test_os.py (original) +++ python/branches/py3k/Lib/test/test_os.py Sat Sep 11 00:18:16 2010 @@ -422,7 +422,6 @@ def test___repr__(self): """Check that the repr() of os.environ looks like environ({...}).""" env = os.environ - self.assertTrue(isinstance(env.data, dict)) self.assertEqual(repr(env), 'environ({{{}}})'.format(', '.join( '{!r}: {!r}'.format(key, value) for key, value in env.items()))) From python-checkins at python.org Sat Sep 11 00:22:58 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 11 Sep 2010 00:22:58 +0200 (CEST) Subject: [Python-checkins] r84691 - python/branches/release27-maint/Modules/ld_so_aix.in Message-ID: <20100910222258.7910CE4D4@mail.python.org> Author: antoine.pitrou Date: Sat Sep 11 00:22:58 2010 New Revision: 84691 Log: Small fix to ld_so_aix.in for 2.x Modified: python/branches/release27-maint/Modules/ld_so_aix.in Modified: python/branches/release27-maint/Modules/ld_so_aix.in ============================================================================== --- python/branches/release27-maint/Modules/ld_so_aix.in (original) +++ python/branches/release27-maint/Modules/ld_so_aix.in Sat Sep 11 00:22:58 2010 @@ -158,7 +158,7 @@ # Default entry symbol for Python modules = init[modulename] # Can be overriden by providing a -e argument. if test -z "$entry"; then - entry=PyInit_`echo $filename | sed "s/module.*//"` + entry=init`echo $filename | sed "s/module.*//"` fi #echo "ld_so_aix: Debug info section" From python-checkins at python.org Sat Sep 11 00:25:19 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 11 Sep 2010 00:25:19 +0200 (CEST) Subject: [Python-checkins] r84692 - in python/branches/py3k: Lib/test/test_pyexpat.py Modules/pyexpat.c Message-ID: <20100910222519.62E21EEA33@mail.python.org> Author: victor.stinner Date: Sat Sep 11 00:25:19 2010 New Revision: 84692 Log: Issue #9402: pyexpat uses Py_DECREF() instead of PyObject_DEL() Fix a crash if Python is compiled in pydebug mode. Modified: python/branches/py3k/Lib/test/test_pyexpat.py python/branches/py3k/Modules/pyexpat.c Modified: python/branches/py3k/Lib/test/test_pyexpat.py ============================================================================== --- python/branches/py3k/Lib/test/test_pyexpat.py (original) +++ python/branches/py3k/Lib/test/test_pyexpat.py Sat Sep 11 00:25:19 2010 @@ -221,6 +221,25 @@ # L should have the same string repeated over and over. self.assertTrue(tag is entry) + def test_issue9402(self): + # create an ExternalEntityParserCreate with buffer text + class ExternalOutputter: + def __init__(self, parser): + self.parser = parser + self.parser_result = None + + def ExternalEntityRefHandler(self, context, base, sysId, pubId): + external_parser = self.parser.ExternalEntityParserCreate("") + self.parser_result = external_parser.Parse("", 1) + return 1 + + parser = expat.ParserCreate(namespace_separator='!') + parser.buffer_text = 1 + out = ExternalOutputter(parser) + parser.ExternalEntityRefHandler = out.ExternalEntityRefHandler + parser.Parse(data, 1) + self.assertEquals(out.parser_result, 1) + class BufferTextTest(unittest.TestCase): def setUp(self): Modified: python/branches/py3k/Modules/pyexpat.c ============================================================================== --- python/branches/py3k/Modules/pyexpat.c (original) +++ python/branches/py3k/Modules/pyexpat.c Sat Sep 11 00:25:19 2010 @@ -973,21 +973,7 @@ return NULL; new_parser->buffer_size = self->buffer_size; new_parser->buffer_used = 0; - if (self->buffer != NULL) { - new_parser->buffer = malloc(new_parser->buffer_size); - if (new_parser->buffer == NULL) { -#ifndef Py_TPFLAGS_HAVE_GC - /* Code for versions 2.0 and 2.1 */ - PyObject_Del(new_parser); -#else - /* Code for versions 2.2 and later. */ - PyObject_GC_Del(new_parser); -#endif - return PyErr_NoMemory(); - } - } - else - new_parser->buffer = NULL; + new_parser->buffer = NULL; new_parser->ordered_attributes = self->ordered_attributes; new_parser->specified_attributes = self->specified_attributes; new_parser->in_callback = 0; @@ -1003,6 +989,13 @@ PyObject_GC_Init(new_parser); #endif + if (self->buffer != NULL) { + new_parser->buffer = malloc(new_parser->buffer_size); + if (new_parser->buffer == NULL) { + Py_DECREF(new_parser); + return PyErr_NoMemory(); + } + } if (!new_parser->itself) { Py_DECREF(new_parser); return PyErr_NoMemory(); From python-checkins at python.org Sat Sep 11 00:47:02 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 11 Sep 2010 00:47:02 +0200 (CEST) Subject: [Python-checkins] r84693 - python/branches/py3k/Python/ceval.c Message-ID: <20100910224702.8A4D6EEA37@mail.python.org> Author: benjamin.peterson Date: Sat Sep 11 00:47:02 2010 New Revision: 84693 Log: remove gil_drop_request in --without-threads Modified: python/branches/py3k/Python/ceval.c Modified: python/branches/py3k/Python/ceval.c ============================================================================== --- python/branches/py3k/Python/ceval.c (original) +++ python/branches/py3k/Python/ceval.c Sat Sep 11 00:47:02 2010 @@ -217,16 +217,24 @@ #endif +#ifdef WITH_THREAD +#define GIL_REQUEST _Py_atomic_load_relaxed(&gil_drop_request) +#else +#define GIL_REQUEST 0 +#endif + /* This can set eval_breaker to 0 even though gil_drop_request became 1. We believe this is all right because the eval loop will release the GIL eventually anyway. */ #define COMPUTE_EVAL_BREAKER() \ _Py_atomic_store_relaxed( \ &eval_breaker, \ - _Py_atomic_load_relaxed(&gil_drop_request) | \ + GIL_REQUEST | \ _Py_atomic_load_relaxed(&pendingcalls_to_do) | \ pending_async_exc) +#ifdef WITH_THREAD + #define SET_GIL_DROP_REQUEST() \ do { \ _Py_atomic_store_relaxed(&gil_drop_request, 1); \ @@ -239,6 +247,8 @@ COMPUTE_EVAL_BREAKER(); \ } while (0) +#endif + /* Pending calls are only modified under pending_lock */ #define SIGNAL_PENDING_CALLS() \ do { \ @@ -387,7 +397,6 @@ #else static _Py_atomic_int eval_breaker = {0}; -static _Py_atomic_int gil_drop_request = {0}; static int pending_async_exc = 0; #endif /* WITH_THREAD */ @@ -1277,8 +1286,8 @@ goto on_error; } } - if (_Py_atomic_load_relaxed(&gil_drop_request)) { #ifdef WITH_THREAD + if (_Py_atomic_load_relaxed(&gil_drop_request)) { /* Give another thread a chance */ if (PyThreadState_Swap(NULL) != tstate) Py_FatalError("ceval: tstate mix-up"); @@ -1289,8 +1298,8 @@ take_gil(tstate); if (PyThreadState_Swap(tstate) != NULL) Py_FatalError("ceval: orphan tstate"); -#endif } +#endif /* Check for asynchronous exceptions. */ if (tstate->async_exc != NULL) { x = tstate->async_exc; From python-checkins at python.org Sat Sep 11 01:13:52 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 11 Sep 2010 01:13:52 +0200 (CEST) Subject: [Python-checkins] r84694 - python/branches/py3k/Modules/main.c Message-ID: <20100910231352.45E6EEE98A@mail.python.org> Author: victor.stinner Date: Sat Sep 11 01:13:52 2010 New Revision: 84694 Log: Issue #8589: surrogateescape error handler is not available at startup Py_Main() uses _Py_wchar2char() + PyUnicode_FromWideChar() instead of PyUnicode_DecodeFSDefault(), because the PyCodec machinery is not ready yet. Modified: python/branches/py3k/Modules/main.c Modified: python/branches/py3k/Modules/main.c ============================================================================== --- python/branches/py3k/Modules/main.c (original) +++ python/branches/py3k/Modules/main.c Sat Sep 11 01:13:52 2010 @@ -488,7 +488,8 @@ #else if ((p = Py_GETENV("PYTHONWARNINGS")) && *p != '\0') { char *buf, *oldloc; - PyObject *warning; + wchar_t *wchar; + PyObject *unicode; /* settle for strtok here as there's no one standard C89 wcstok */ @@ -500,11 +501,15 @@ oldloc = strdup(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, ""); for (p = strtok(buf, ","); p != NULL; p = strtok(NULL, ",")) { - warning = PyUnicode_DecodeFSDefault(p); - if (warning != NULL) { - PySys_AddWarnOptionUnicode(warning); - Py_DECREF(warning); - } + wchar = _Py_char2wchar(p); + if (wchar == NULL) + continue; + unicode = PyUnicode_FromWideChar(wchar, wcslen(wchar)); + PyMem_Free(wchar); + if (unicode == NULL) + continue; + PySys_AddWarnOptionUnicode(unicode); + Py_DECREF(unicode); } setlocale(LC_ALL, oldloc); free(oldloc); From python-checkins at python.org Sat Sep 11 01:35:52 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 11 Sep 2010 01:35:52 +0200 (CEST) Subject: [Python-checkins] r84695 - python/branches/py3k/Lib/functools.py Message-ID: <20100910233552.58F97EE9CA@mail.python.org> Author: benjamin.peterson Date: Sat Sep 11 01:35:52 2010 New Revision: 84695 Log: add reduce and partial to __all__ Modified: python/branches/py3k/Lib/functools.py Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Sat Sep 11 01:35:52 2010 @@ -9,7 +9,7 @@ # See C source code for _functools credits/copyright __all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', - 'total_ordering', 'cmp_to_key', 'lru_cache'] + 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial'] from _functools import partial, reduce from collections import OrderedDict From python-checkins at python.org Sat Sep 11 01:49:05 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 11 Sep 2010 01:49:05 +0200 (CEST) Subject: [Python-checkins] r84696 - in python/branches/py3k: Misc/NEWS Modules/posixmodule.c Message-ID: <20100910234905.00C70EEA0E@mail.python.org> Author: victor.stinner Date: Sat Sep 11 01:49:04 2010 New Revision: 84696 Log: Issue #9579, #9580: Fix os.confstr() for value longer than 255 bytes and encode the value with filesystem encoding and surrogateescape (instead of utf-8 in strict mode). Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/posixmodule.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 11 01:49:04 2010 @@ -29,6 +29,10 @@ Library ------- +- Issue #9579, #9580: Fix os.confstr() for value longer than 255 bytes and + encode the value with filesystem encoding and surrogateescape (instead of + utf-8 in strict mode). + - Issue #9632: Remove sys.setfilesystemencoding() function: use PYTHONFSENCODING environment variable to set the filesystem encoding at Python startup. sys.setfilesystemencoding() creates inconsistencies because Modified: python/branches/py3k/Modules/posixmodule.c ============================================================================== --- python/branches/py3k/Modules/posixmodule.c (original) +++ python/branches/py3k/Modules/posixmodule.c Sat Sep 11 01:49:04 2010 @@ -6721,32 +6721,34 @@ { PyObject *result = NULL; int name; - char buffer[256]; - - if (PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name)) { + char buffer[255]; int len; - errno = 0; - len = confstr(name, buffer, sizeof(buffer)); - if (len == 0) { - if (errno) { - posix_error(); - } - else { - result = Py_None; - Py_INCREF(Py_None); - } + if (!PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name)) + return NULL; + + errno = 0; + len = confstr(name, buffer, sizeof(buffer)); + if (len == 0) { + if (errno) { + posix_error(); + return NULL; } else { - if ((unsigned int)len >= sizeof(buffer)) { - result = PyUnicode_FromStringAndSize(NULL, len-1); - if (result != NULL) - confstr(name, _PyUnicode_AsString(result), len); - } - else - result = PyUnicode_FromStringAndSize(buffer, len-1); + Py_RETURN_NONE; } } + + if ((unsigned int)len >= sizeof(buffer)) { + char *buf = PyMem_Malloc(len); + if (buf == NULL) + return PyErr_NoMemory(); + confstr(name, buf, len); + result = PyUnicode_DecodeFSDefaultAndSize(buf, len-1); + PyMem_Free(buf); + } + else + result = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1); return result; } #endif From python-checkins at python.org Sat Sep 11 01:50:31 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 11 Sep 2010 01:50:31 +0200 (CEST) Subject: [Python-checkins] r84697 - python/branches/py3k/Misc/NEWS Message-ID: <20100910235031.8D601EE990@mail.python.org> Author: victor.stinner Date: Sat Sep 11 01:50:31 2010 New Revision: 84697 Log: Issue #9579, #9580: Oops, add the author of the patch Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 11 01:50:31 2010 @@ -31,7 +31,7 @@ - Issue #9579, #9580: Fix os.confstr() for value longer than 255 bytes and encode the value with filesystem encoding and surrogateescape (instead of - utf-8 in strict mode). + utf-8 in strict mode). Patch written by David Watson. - Issue #9632: Remove sys.setfilesystemencoding() function: use PYTHONFSENCODING environment variable to set the filesystem encoding at From python-checkins at python.org Sat Sep 11 01:52:42 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 11 Sep 2010 01:52:42 +0200 (CEST) Subject: [Python-checkins] r84698 - python/branches/py3k/Python/ceval.c Message-ID: <20100910235242.68E2AEE990@mail.python.org> Author: benjamin.peterson Date: Sat Sep 11 01:52:42 2010 New Revision: 84698 Log: use Py_REFCNT Modified: python/branches/py3k/Python/ceval.c Modified: python/branches/py3k/Python/ceval.c ============================================================================== --- python/branches/py3k/Python/ceval.c (original) +++ python/branches/py3k/Python/ceval.c Sat Sep 11 01:52:42 2010 @@ -4394,7 +4394,7 @@ return NULL; } - if (v->ob_refcnt == 2) { + if (Py_REFCNF(v) == 2) { /* In the common case, there are 2 references to the value * stored in 'variable' when the += is performed: one on the * value stack (in 'v') and one still stored in the @@ -4435,7 +4435,7 @@ } } - if (v->ob_refcnt == 1 && !PyUnicode_CHECK_INTERNED(v)) { + if (Py_REFCNF(v) == 1 && !PyUnicode_CHECK_INTERNED(v)) { /* Now we own the last reference to 'v', so we can resize it * in-place. */ From python-checkins at python.org Sat Sep 11 01:53:15 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 11 Sep 2010 01:53:15 +0200 (CEST) Subject: [Python-checkins] r84699 - python/branches/py3k/Python/ceval.c Message-ID: <20100910235315.12003EE990@mail.python.org> Author: benjamin.peterson Date: Sat Sep 11 01:53:14 2010 New Revision: 84699 Log: typo Modified: python/branches/py3k/Python/ceval.c Modified: python/branches/py3k/Python/ceval.c ============================================================================== --- python/branches/py3k/Python/ceval.c (original) +++ python/branches/py3k/Python/ceval.c Sat Sep 11 01:53:14 2010 @@ -4394,7 +4394,7 @@ return NULL; } - if (Py_REFCNF(v) == 2) { + if (Py_REFCNT(v) == 2) { /* In the common case, there are 2 references to the value * stored in 'variable' when the += is performed: one on the * value stack (in 'v') and one still stored in the @@ -4435,7 +4435,7 @@ } } - if (Py_REFCNF(v) == 1 && !PyUnicode_CHECK_INTERNED(v)) { + if (Py_REFCNT(v) == 1 && !PyUnicode_CHECK_INTERNED(v)) { /* Now we own the last reference to 'v', so we can resize it * in-place. */ From python-checkins at python.org Sat Sep 11 02:05:45 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 11 Sep 2010 02:05:45 +0200 (CEST) Subject: [Python-checkins] r84700 - in python/branches/py3k/Lib: collections.py functools.py Message-ID: <20100911000545.1D4A9EEA16@mail.python.org> Author: raymond.hettinger Date: Sat Sep 11 02:05:44 2010 New Revision: 84700 Log: Separate positional arguments from localized globals. Modified: python/branches/py3k/Lib/collections.py python/branches/py3k/Lib/functools.py Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Sat Sep 11 02:05:44 2010 @@ -47,7 +47,7 @@ self.__map = {} self.update(*args, **kwds) - def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): + def __setitem__(self, key, value, *, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): 'od.__setitem__(i, y) <==> od[i]=y' # Setting a new item creates a new link which goes at the end of the linked # list, and the inherited dictionary is updated with the new key/value pair. @@ -57,7 +57,7 @@ last[NEXT] = root[PREV] = self.__map[key] = [last, root, key] dict_setitem(self, key, value) - def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__): + def __delitem__(self, key, *, PREV=0, NEXT=1, dict_delitem=dict.__delitem__): 'od.__delitem__(y) <==> del od[y]' # Deleting an existing item uses self.__map to find the link which is # then removed by updating the links in the predecessor and successor nodes. @@ -68,7 +68,7 @@ link_prev[NEXT] = link_next link_next[PREV] = link_prev - def __iter__(self, NEXT=1, KEY=2): + def __iter__(self, *, NEXT=1, KEY=2): 'od.__iter__() <==> iter(od)' # Traverse the linked list in order. root = self.__root @@ -77,7 +77,7 @@ yield curr[KEY] curr = curr[NEXT] - def __reversed__(self, PREV=0, KEY=2): + def __reversed__(self, *, PREV=0, KEY=2): 'od.__reversed__() <==> reversed(od)' # Traverse the linked list in reverse order. root = self.__root @@ -108,7 +108,7 @@ pass dict.clear(self) - def popitem(self, last=True, PREV=0, NEXT=1, KEY=2, dict_pop=dict.pop): + def popitem(self, last=True, *, PREV=0, NEXT=1, KEY=2, dict_pop=dict.pop): '''od.popitem() -> (k, v), return and remove a (key, value) pair. Pairs are returned in LIFO order if last is true or FIFO order if false. @@ -173,7 +173,7 @@ def __del__(self): self.clear() # eliminate cyclical references - def move_to_end(self, key, last=True, PREV=0, NEXT=1): + def move_to_end(self, key, last=True, *, PREV=0, NEXT=1): '''Move an existing element to the end (or beginning if last==False). Raises KeyError if the element does not exist. Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Sat Sep 11 02:05:44 2010 @@ -123,8 +123,8 @@ http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used """ - def decorating_function(user_function, tuple=tuple, sorted=sorted, - len=len, KeyError=KeyError): + def decorating_function(user_function, + *, tuple=tuple, sorted=sorted, len=len, KeyError=KeyError): cache = OrderedDict() # ordered least recent to most recent cache_popitem = cache.popitem cache_renew = cache.move_to_end From python-checkins at python.org Sat Sep 11 02:22:12 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 11 Sep 2010 02:22:12 +0200 (CEST) Subject: [Python-checkins] r84701 - python/branches/py3k/Doc/library/os.path.rst Message-ID: <20100911002212.2E5EFEEA30@mail.python.org> Author: victor.stinner Date: Sat Sep 11 02:22:12 2010 New Revision: 84701 Log: Issue #767645: fix os.path.supports_unicode_filenames definition os.listdir(str) always returns unicode and it can return non-ascii filenames even if supports_unicode_filenames is False. Modified: python/branches/py3k/Doc/library/os.path.rst Modified: python/branches/py3k/Doc/library/os.path.rst ============================================================================== --- python/branches/py3k/Doc/library/os.path.rst (original) +++ python/branches/py3k/Doc/library/os.path.rst Sat Sep 11 02:22:12 2010 @@ -320,5 +320,4 @@ .. data:: supports_unicode_filenames True if arbitrary Unicode strings can be used as file names (within limitations - imposed by the file system), and if :func:`os.listdir` returns strings that - contain characters that cannot be represented by ASCII. + imposed by the file system). From python-checkins at python.org Sat Sep 11 02:23:53 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 11 Sep 2010 02:23:53 +0200 (CEST) Subject: [Python-checkins] r84702 - in python/branches/release31-maint: Doc/library/os.path.rst Message-ID: <20100911002353.47BD9EEA3A@mail.python.org> Author: victor.stinner Date: Sat Sep 11 02:23:53 2010 New Revision: 84702 Log: Merged revisions 84701 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84701 | victor.stinner | 2010-09-11 02:22:12 +0200 (sam., 11 sept. 2010) | 5 lines Issue #767645: fix os.path.supports_unicode_filenames definition os.listdir(str) always returns unicode and it can return non-ascii filenames even if supports_unicode_filenames is False. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/os.path.rst Modified: python/branches/release31-maint/Doc/library/os.path.rst ============================================================================== --- python/branches/release31-maint/Doc/library/os.path.rst (original) +++ python/branches/release31-maint/Doc/library/os.path.rst Sat Sep 11 02:23:53 2010 @@ -308,5 +308,4 @@ .. data:: supports_unicode_filenames True if arbitrary Unicode strings can be used as file names (within limitations - imposed by the file system), and if :func:`os.listdir` returns strings that - contain characters that cannot be represented by ASCII. + imposed by the file system). From ncoghlan at gmail.com Sat Sep 11 02:28:11 2010 From: ncoghlan at gmail.com (Nick Coghlan) Date: Sat, 11 Sep 2010 10:28:11 +1000 Subject: [Python-checkins] r84685 - in python/branches/py3k: Doc/library/dis.rst Doc/reference/simple_stmts.rst Doc/whatsnew/3.2.rst Include/opcode.h Lib/opcode.py Lib/test/test_exceptions.py Lib/test/test_scope.py Lib/test/test_syntax.py Misc/NEWS Python Message-ID: On Sat, Sep 11, 2010 at 7:39 AM, amaury.forgeotdarc wrote: > There is no need to bump the PYC magic number: the new opcode is used > for code that did not compile before. If the magic number doesn't change for 3.2, how will 3.1 know it can't run pyc and pyo files containing these opcodes? The magic number needs a bump or this change may cause SystemErrors when older versions attempt to run affected pyc files. Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From benjamin at python.org Sat Sep 11 02:33:10 2010 From: benjamin at python.org (Benjamin Peterson) Date: Fri, 10 Sep 2010 19:33:10 -0500 Subject: [Python-checkins] r84685 - in python/branches/py3k: Doc/library/dis.rst Doc/reference/simple_stmts.rst Doc/whatsnew/3.2.rst Include/opcode.h Lib/opcode.py Lib/test/test_exceptions.py Lib/test/test_scope.py Lib/test/test_syntax.py Misc/NEWS Python In-Reply-To: References: Message-ID: 2010/9/10 Nick Coghlan : > On Sat, Sep 11, 2010 at 7:39 AM, amaury.forgeotdarc > wrote: >> There is no need to bump the PYC magic number: the new opcode is used >> for code that did not compile before. > > If the magic number doesn't change for 3.2, how will 3.1 know it can't > run pyc and pyo files containing these opcodes? The magic number is already bumped since 3.1. However, it's true that the number should be bumped anyway for good measure. -- Regards, Benjamin From python-checkins at python.org Sat Sep 11 02:39:25 2010 From: python-checkins at python.org (nick.coghlan) Date: Sat, 11 Sep 2010 02:39:25 +0200 (CEST) Subject: [Python-checkins] r84703 - python/branches/py3k/Python/import.c Message-ID: <20100911003925.28CDAD2F3@mail.python.org> Author: nick.coghlan Date: Sat Sep 11 02:39:25 2010 New Revision: 84703 Log: Fix incorrect comment regarding MAGIC and TAG in import.c Modified: python/branches/py3k/Python/import.c Modified: python/branches/py3k/Python/import.c ============================================================================== --- python/branches/py3k/Python/import.c (original) +++ python/branches/py3k/Python/import.c Sat Sep 11 02:39:25 2010 @@ -108,8 +108,11 @@ Python 3.2a2 3180 (add DELETE_DEREF) */ -/* If you change MAGIC, you must change TAG and you must insert the old value - into _PyMagicNumberTags below. +/* MAGIC must change whenever the bytecode emitted by the compiler may no + longer be understood by older implementations of the eval loop (usually + due to the addition of new opcodes) + TAG must change for each major Python release. The magic number will take + care of any bytecode changes that occur during development. */ #define MAGIC (3180 | ((long)'\r'<<16) | ((long)'\n'<<24)) #define TAG "cpython-32" From ncoghlan at gmail.com Sat Sep 11 02:43:14 2010 From: ncoghlan at gmail.com (Nick Coghlan) Date: Sat, 11 Sep 2010 10:43:14 +1000 Subject: [Python-checkins] r84685 - in python/branches/py3k: Doc/library/dis.rst Doc/reference/simple_stmts.rst Doc/whatsnew/3.2.rst Include/opcode.h Lib/opcode.py Lib/test/test_exceptions.py Lib/test/test_scope.py Lib/test/test_syntax.py Misc/NEWS Python In-Reply-To: References: Message-ID: On Sat, Sep 11, 2010 at 10:33 AM, Benjamin Peterson wrote: > 2010/9/10 Nick Coghlan : >> On Sat, Sep 11, 2010 at 7:39 AM, amaury.forgeotdarc >> wrote: >>> There is no need to bump the PYC magic number: the new opcode is used >>> for code that did not compile before. >> >> If the magic number doesn't change for 3.2, how will 3.1 know it can't >> run pyc and pyo files containing these opcodes? > > The magic number is already bumped since 3.1. However, it's true that > the number should be bumped anyway for good measure. Yeah, I saw your subsequent checkin. I've updated the comment just above MAGIC and TAG to make it clearer when they should be changed. Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From python-checkins at python.org Sat Sep 11 02:54:47 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 11 Sep 2010 02:54:47 +0200 (CEST) Subject: [Python-checkins] r84704 - in python/branches/py3k: Doc/c-api/exceptions.rst Doc/c-api/unicode.rst Include/pyerrors.h Include/unicodeobject.h Lib/test/test_unicode.py Misc/NEWS Modules/_testcapimodule.c Objects/unicodeobject.c Message-ID: <20100911005447.60845EE9D7@mail.python.org> Author: victor.stinner Date: Sat Sep 11 02:54:47 2010 New Revision: 84704 Log: Issue #9738: PyUnicode_FromFormat() and PyErr_Format() raise an error on a non-ASCII byte in the format string. Document also the encoding. Modified: python/branches/py3k/Doc/c-api/exceptions.rst python/branches/py3k/Doc/c-api/unicode.rst python/branches/py3k/Include/pyerrors.h python/branches/py3k/Include/unicodeobject.h python/branches/py3k/Lib/test/test_unicode.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/_testcapimodule.c python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Doc/c-api/exceptions.rst ============================================================================== --- python/branches/py3k/Doc/c-api/exceptions.rst (original) +++ python/branches/py3k/Doc/c-api/exceptions.rst Sat Sep 11 02:54:47 2010 @@ -146,7 +146,7 @@ .. cfunction:: PyObject* PyErr_Format(PyObject *exception, const char *format, ...) This function sets the error indicator and returns *NULL*. *exception* should be - a Python exception (class, not an instance). *format* should be a string, + a Python exception (class, not an instance). *format* should be an ASCII-encoded string, containing format codes, similar to :cfunc:`printf`. The ``width.precision`` before a format code is parsed, but the width part is ignored. Modified: python/branches/py3k/Doc/c-api/unicode.rst ============================================================================== --- python/branches/py3k/Doc/c-api/unicode.rst (original) +++ python/branches/py3k/Doc/c-api/unicode.rst Sat Sep 11 02:54:47 2010 @@ -234,7 +234,7 @@ arguments, calculate the size of the resulting Python unicode string and return a string with the values formatted into it. The variable arguments must be C types and must correspond exactly to the format characters in the *format* - string. The following format characters are allowed: + ASCII-encoded string. The following format characters are allowed: .. % This should be exactly the same as the table in PyErr_Format. .. % The descriptions for %zd and %zu are wrong, but the truth is complicated Modified: python/branches/py3k/Include/pyerrors.h ============================================================================== --- python/branches/py3k/Include/pyerrors.h (original) +++ python/branches/py3k/Include/pyerrors.h Sat Sep 11 02:54:47 2010 @@ -183,7 +183,11 @@ PyObject *, const Py_UNICODE *); #endif /* MS_WINDOWS */ -PyAPI_FUNC(PyObject *) PyErr_Format(PyObject *, const char *, ...); +PyAPI_FUNC(PyObject *) PyErr_Format( + PyObject *exception, + const char *format, /* ASCII-encoded string */ + ... + ); #ifdef MS_WINDOWS PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilenameObject( Modified: python/branches/py3k/Include/unicodeobject.h ============================================================================== --- python/branches/py3k/Include/unicodeobject.h (original) +++ python/branches/py3k/Include/unicodeobject.h Sat Sep 11 02:54:47 2010 @@ -550,8 +550,14 @@ register PyObject *obj /* Object */ ); -PyAPI_FUNC(PyObject *) PyUnicode_FromFormatV(const char*, va_list); -PyAPI_FUNC(PyObject *) PyUnicode_FromFormat(const char*, ...); +PyAPI_FUNC(PyObject *) PyUnicode_FromFormatV( + const char *format, /* ASCII-encoded string */ + va_list vargs + ); +PyAPI_FUNC(PyObject *) PyUnicode_FromFormat( + const char *format, /* ASCII-encoded string */ + ... + ); /* Format the object based on the format_spec, as defined in PEP 3101 (Advanced String Formatting). */ Modified: python/branches/py3k/Lib/test/test_unicode.py ============================================================================== --- python/branches/py3k/Lib/test/test_unicode.py (original) +++ python/branches/py3k/Lib/test/test_unicode.py Sat Sep 11 02:54:47 2010 @@ -1385,6 +1385,20 @@ self.assertEquals("%s" % s, '__str__ overridden') self.assertEquals("{}".format(s), '__str__ overridden') + def test_from_format(self): + # Ensure that PyUnicode_FromFormat() raises an error for a non-ascii + # format string. + from _testcapi import format_unicode + + # ascii format, non-ascii argument + text = format_unicode(b'ascii\x7f=%U', 'unicode\xe9') + self.assertEqual(text, 'ascii\x7f=unicode\xe9') + + # non-ascii format, ascii argument + self.assertRaisesRegexp(ValueError, + '^PyUnicode_FromFormatV\(\) expects an ASCII-encoded format ' + 'string, got a non-ascii byte: 0xe9$', + format_unicode, b'unicode\xe9=%s', 'ascii') def test_main(): support.run_unittest(__name__) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 11 02:54:47 2010 @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #9738: PyUnicode_FromFormat() and PyErr_Format() raise an error on + a non-ASCII byte in the format string. + - Issue #4617: Previously it was illegal to delete a name from the local namespace if it occurs as a free variable in a nested block. This limitation of the compiler has been lifted, and a new opcode introduced (DELETE_DEREF). Modified: python/branches/py3k/Modules/_testcapimodule.c ============================================================================== --- python/branches/py3k/Modules/_testcapimodule.c (original) +++ python/branches/py3k/Modules/_testcapimodule.c Sat Sep 11 02:54:47 2010 @@ -2193,6 +2193,17 @@ return NULL; } +static PyObject * +format_unicode(PyObject *self, PyObject *args) +{ + const char *format; + PyObject *arg; + if (!PyArg_ParseTuple(args, "yU", &format, &arg)) + return NULL; + return PyUnicode_FromFormat(format, arg); + +} + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS}, @@ -2272,6 +2283,7 @@ {"make_exception_with_doc", (PyCFunction)make_exception_with_doc, METH_VARARGS | METH_KEYWORDS}, {"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS}, + {"format_unicode", format_unicode, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Sat Sep 11 02:54:47 2010 @@ -1102,7 +1102,15 @@ appendstring(p); goto end; } - } else + } + else if (128 <= (unsigned char)*f) { + PyErr_Format(PyExc_ValueError, + "PyUnicode_FromFormatV() expects an ASCII-encoded format " + "string, got a non-ascii byte: 0x%02x", + (unsigned char)*f); + goto fail; + } + else *s++ = *f; } From solipsis at pitrou.net Sat Sep 11 05:12:04 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 11 Sep 2010 05:12:04 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84704): sum=6 Message-ID: py3k results for svn r84704 (hg cset 4ffcca0d1896) -------------------------------------------------- test_unicode leaked [2, 2, 2] references, sum=6 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflog8l0wn3', '-x'] From python-checkins at python.org Sat Sep 11 08:39:58 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 11 Sep 2010 08:39:58 +0200 (CEST) Subject: [Python-checkins] r84705 - in python/branches/py3k: .bzrignore .hgignore Modules Message-ID: <20100911063958.6E9D1EEA04@mail.python.org> Author: georg.brandl Date: Sat Sep 11 08:39:58 2010 New Revision: 84705 Log: Add new generated file to ignoreds. Modified: python/branches/py3k/.bzrignore python/branches/py3k/.hgignore python/branches/py3k/Modules/ (props changed) Modified: python/branches/py3k/.bzrignore ============================================================================== --- python/branches/py3k/.bzrignore (original) +++ python/branches/py3k/.bzrignore Sat Sep 11 08:39:58 2010 @@ -30,6 +30,7 @@ Modules/Setup.config Modules/Setup.local Modules/config.c +Modules/ld_so_aix Parser/pgen Parser/pgen.stamp Lib/test/data/* Modified: python/branches/py3k/.hgignore ============================================================================== --- python/branches/py3k/.hgignore (original) +++ python/branches/py3k/.hgignore Sat Sep 11 08:39:58 2010 @@ -29,6 +29,7 @@ Modules/Setup.config Modules/Setup.local Modules/config.c +Modules/ld_so_aix$ Parser/pgen$ Parser/pgen.stamp$ ^core From python-checkins at python.org Sat Sep 11 08:41:30 2010 From: python-checkins at python.org (georg.brandl) Date: Sat, 11 Sep 2010 08:41:30 +0200 (CEST) Subject: [Python-checkins] r84706 - python/branches/py3k/Modules/pyexpat.c Message-ID: <20100911064130.967ACEE990@mail.python.org> Author: georg.brandl Date: Sat Sep 11 08:41:30 2010 New Revision: 84706 Log: Remove compatibility code for Python < 2.1, < 2.2 and < 2.4. Modified: python/branches/py3k/Modules/pyexpat.c Modified: python/branches/py3k/Modules/pyexpat.c ============================================================================== --- python/branches/py3k/Modules/pyexpat.c (original) +++ python/branches/py3k/Modules/pyexpat.c Sat Sep 11 08:41:30 2010 @@ -258,11 +258,7 @@ value = Py_None; Py_INCREF(value); } -#if PY_VERSION_HEX < 0x02040000 - arg = Py_BuildValue("(OOO)", type, value, traceback); -#else arg = PyTuple_Pack(3, type, value, traceback); -#endif if (arg == NULL) { PyErr_Restore(type, value, traceback); return 0; @@ -814,11 +810,7 @@ PyTuple_SET_ITEM(arg, 0, bytes); -#if PY_VERSION_HEX < 0x02020000 - str = PyObject_CallObject(meth, arg); -#else str = PyObject_Call(meth, arg, NULL); -#endif if (str == NULL) goto finally; @@ -961,14 +953,7 @@ return NULL; } -#ifndef Py_TPFLAGS_HAVE_GC - /* Python versions 2.0 and 2.1 */ - new_parser = PyObject_New(xmlparseobject, &Xmlparsetype); -#else - /* Python versions 2.2 and later */ new_parser = PyObject_GC_New(xmlparseobject, &Xmlparsetype); -#endif - if (new_parser == NULL) return NULL; new_parser->buffer_size = self->buffer_size; @@ -983,11 +968,7 @@ new_parser->handlers = 0; new_parser->intern = self->intern; Py_XINCREF(new_parser->intern); -#ifdef Py_TPFLAGS_HAVE_GC PyObject_GC_Track(new_parser); -#else - PyObject_GC_Init(new_parser); -#endif if (self->buffer != NULL) { new_parser->buffer = malloc(new_parser->buffer_size); @@ -1158,12 +1139,7 @@ int i; xmlparseobject *self; -#ifdef Py_TPFLAGS_HAVE_GC - /* Code for versions 2.2 and later */ self = PyObject_GC_New(xmlparseobject, &Xmlparsetype); -#else - self = PyObject_New(xmlparseobject, &Xmlparsetype); -#endif if (self == NULL) return NULL; @@ -1183,11 +1159,7 @@ } self->intern = intern; Py_XINCREF(self->intern); -#ifdef Py_TPFLAGS_HAVE_GC PyObject_GC_Track(self); -#else - PyObject_GC_Init(self); -#endif if (self->itself == NULL) { PyErr_SetString(PyExc_RuntimeError, "XML_ParserCreate failed"); @@ -1216,11 +1188,7 @@ xmlparse_dealloc(xmlparseobject *self) { int i; -#ifdef Py_TPFLAGS_HAVE_GC PyObject_GC_UnTrack(self); -#else - PyObject_GC_Fini(self); -#endif if (self->itself != NULL) XML_ParserFree(self->itself); self->itself = NULL; @@ -1240,13 +1208,7 @@ self->buffer = NULL; } Py_XDECREF(self->intern); -#ifndef Py_TPFLAGS_HAVE_GC - /* Code for versions 2.0 and 2.1 */ - PyObject_Del(self); -#else - /* Code for versions 2.2 and later. */ PyObject_GC_Del(self); -#endif } static int @@ -1571,11 +1533,7 @@ (getattrofunc)xmlparse_getattro, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ -#ifdef Py_TPFLAGS_HAVE_GC Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ -#else - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/ -#endif Xmlparsetype__doc__, /* tp_doc - Documentation string */ (traverseproc)xmlparse_traverse, /* tp_traverse */ (inquiry)xmlparse_clear, /* tp_clear */ From python-checkins at python.org Sat Sep 11 10:07:42 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 11 Sep 2010 10:07:42 +0200 (CEST) Subject: [Python-checkins] r84707 - in python/branches/py3k/Lib: collections.py functools.py Message-ID: <20100911080742.5B6FEEEA58@mail.python.org> Author: raymond.hettinger Date: Sat Sep 11 10:07:42 2010 New Revision: 84707 Log: Revert r84700 which had an unfortunate performance cost. Modified: python/branches/py3k/Lib/collections.py python/branches/py3k/Lib/functools.py Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Sat Sep 11 10:07:42 2010 @@ -47,7 +47,7 @@ self.__map = {} self.update(*args, **kwds) - def __setitem__(self, key, value, *, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): + def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): 'od.__setitem__(i, y) <==> od[i]=y' # Setting a new item creates a new link which goes at the end of the linked # list, and the inherited dictionary is updated with the new key/value pair. @@ -57,7 +57,7 @@ last[NEXT] = root[PREV] = self.__map[key] = [last, root, key] dict_setitem(self, key, value) - def __delitem__(self, key, *, PREV=0, NEXT=1, dict_delitem=dict.__delitem__): + def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__): 'od.__delitem__(y) <==> del od[y]' # Deleting an existing item uses self.__map to find the link which is # then removed by updating the links in the predecessor and successor nodes. @@ -68,7 +68,7 @@ link_prev[NEXT] = link_next link_next[PREV] = link_prev - def __iter__(self, *, NEXT=1, KEY=2): + def __iter__(self, NEXT=1, KEY=2): 'od.__iter__() <==> iter(od)' # Traverse the linked list in order. root = self.__root @@ -77,7 +77,7 @@ yield curr[KEY] curr = curr[NEXT] - def __reversed__(self, *, PREV=0, KEY=2): + def __reversed__(self, PREV=0, KEY=2): 'od.__reversed__() <==> reversed(od)' # Traverse the linked list in reverse order. root = self.__root @@ -108,7 +108,7 @@ pass dict.clear(self) - def popitem(self, last=True, *, PREV=0, NEXT=1, KEY=2, dict_pop=dict.pop): + def popitem(self, last=True, PREV=0, NEXT=1, KEY=2, dict_pop=dict.pop): '''od.popitem() -> (k, v), return and remove a (key, value) pair. Pairs are returned in LIFO order if last is true or FIFO order if false. @@ -173,7 +173,7 @@ def __del__(self): self.clear() # eliminate cyclical references - def move_to_end(self, key, last=True, *, PREV=0, NEXT=1): + def move_to_end(self, key, last=True, PREV=0, NEXT=1): '''Move an existing element to the end (or beginning if last==False). Raises KeyError if the element does not exist. Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Sat Sep 11 10:07:42 2010 @@ -123,8 +123,8 @@ http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used """ - def decorating_function(user_function, - *, tuple=tuple, sorted=sorted, len=len, KeyError=KeyError): + def decorating_function(user_function, tuple=tuple, sorted=sorted, + len=len, KeyError=KeyError): cache = OrderedDict() # ordered least recent to most recent cache_popitem = cache.popitem cache_renew = cache.move_to_end From python-checkins at python.org Sat Sep 11 11:06:21 2010 From: python-checkins at python.org (vinay.sajip) Date: Sat, 11 Sep 2010 11:06:21 +0200 (CEST) Subject: [Python-checkins] r84708 - python/branches/release27-maint/Lib/logging/__init__.py Message-ID: <20100911090621.7E173EEA4F@mail.python.org> Author: vinay.sajip Date: Sat Sep 11 11:06:21 2010 New Revision: 84708 Log: logging: Added threading interlock in basicConfig(). Modified: python/branches/release27-maint/Lib/logging/__init__.py Modified: python/branches/release27-maint/Lib/logging/__init__.py ============================================================================== --- python/branches/release27-maint/Lib/logging/__init__.py (original) +++ python/branches/release27-maint/Lib/logging/__init__.py Sat Sep 11 11:06:21 2010 @@ -1497,22 +1497,28 @@ using sys.stdout or sys.stderr), whereas FileHandler closes its stream when the handler is closed. """ - if len(root.handlers) == 0: - filename = kwargs.get("filename") - if filename: - mode = kwargs.get("filemode", 'a') - hdlr = FileHandler(filename, mode) - else: - stream = kwargs.get("stream") - hdlr = StreamHandler(stream) - fs = kwargs.get("format", BASIC_FORMAT) - dfs = kwargs.get("datefmt", None) - fmt = Formatter(fs, dfs) - hdlr.setFormatter(fmt) - root.addHandler(hdlr) - level = kwargs.get("level") - if level is not None: - root.setLevel(level) + # Add thread safety in case someone mistakenly calls + # basicConfig() from multiple threads + _acquireLock() + try: + if len(root.handlers) == 0: + filename = kwargs.get("filename") + if filename: + mode = kwargs.get("filemode", 'a') + hdlr = FileHandler(filename, mode) + else: + stream = kwargs.get("stream") + hdlr = StreamHandler(stream) + fs = kwargs.get("format", BASIC_FORMAT) + dfs = kwargs.get("datefmt", None) + fmt = Formatter(fs, dfs) + hdlr.setFormatter(fmt) + root.addHandler(hdlr) + level = kwargs.get("level") + if level is not None: + root.setLevel(level) + finally: + _releaseLock() #--------------------------------------------------------------------------- # Utility functions at module level. From python-checkins at python.org Sat Sep 11 12:25:28 2010 From: python-checkins at python.org (vinay.sajip) Date: Sat, 11 Sep 2010 12:25:28 +0200 (CEST) Subject: [Python-checkins] r84709 - in python/branches: py3k/Doc/library/logging.rst release27-maint/Doc/library/logging.rst Message-ID: <20100911102528.49ACBEE9FE@mail.python.org> Author: vinay.sajip Date: Sat Sep 11 12:25:28 2010 New Revision: 84709 Log: Issue #9827: clarified LogRecord documentation. Modified: python/branches/py3k/Doc/library/logging.rst python/branches/release27-maint/Doc/library/logging.rst Modified: python/branches/py3k/Doc/library/logging.rst ============================================================================== --- python/branches/py3k/Doc/library/logging.rst (original) +++ python/branches/py3k/Doc/library/logging.rst Sat Sep 11 12:25:28 2010 @@ -527,6 +527,7 @@ just "foo". .. versionadded:: 3.1 + The :class:`NullHandler` class was not present in previous versions, but is now included, so that it need not be defined in library code. @@ -736,7 +737,7 @@ d = {'clientip': '192.168.0.1', 'user': 'fbloggs'} logging.warning("Protocol problem: %s", "connection reset", extra=d) - would print something like :: + would print something like:: 2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset @@ -903,6 +904,7 @@ and 2.2.x, which do not include the :mod:`logging` package in the standard library. +.. _logger: Logger Objects -------------- @@ -1788,6 +1790,8 @@ the :meth:`makePickle` method and implementing your alternative there, as well as adapting the above script to use your alternative serialization. +.. _arbitrary-object-messages: + Using arbitrary objects as messages ----------------------------------- @@ -2705,7 +2709,6 @@ specified, ``'%(message)s'`` is used. If no *datefmt* is specified, the ISO8601 date format is used. - .. method:: format(record) The record's attribute dictionary is used as the operand to a string @@ -2781,32 +2784,70 @@ LogRecord Objects ----------------- -:class:`LogRecord` instances are created every time something is logged. They -contain all the information pertinent to the event being logged. The main -information passed in is in msg and args, which are combined using msg % args to -create the message field of the record. The record also includes information -such as when the record was created, the source line where the logging call was -made, and any exception information to be logged. +:class:`LogRecord` instances are created automatically by the :class:`Logger` +every time something is logged, and can be created manually via +:func:`makeLogRecord` (for example, from a pickled event received over the +wire). .. class:: LogRecord(name, lvl, pathname, lineno, msg, args, exc_info, func=None) - Returns an instance of :class:`LogRecord` initialized with interesting - information. The *name* is the logger name; *lvl* is the numeric level; - *pathname* is the absolute pathname of the source file in which the logging - call was made; *lineno* is the line number in that file where the logging - call is found; *msg* is the user-supplied message (a format string); *args* - is the tuple which, together with *msg*, makes up the user message; and - *exc_info* is the exception tuple obtained by calling :func:`sys.exc_info` - (or :const:`None`, if no exception information is available). The *func* is - the name of the function from which the logging call was made. If not - specified, it defaults to ``None``. + Contains all the information pertinent to the event being logged. + + The primary information is passed in :attr:`msg` and :attr:`args`, which + are combined using ``msg % args`` to create the :attr:`message` field of the + record. + + .. attribute:: args + + Tuple of arguments to be used in formatting :attr:`msg`. + + .. attribute:: exc_info + + Exception tuple (? la `sys.exc_info`) or `None` if no exception + information is availble. + + .. attribute:: func + + Name of the function of origin (i.e. in which the logging call was made). + + .. attribute:: lineno + Line number in the source file of origin. + + .. attribute:: lvl + + Numeric logging level. + + .. attribute:: message + + Bound to the result of :meth:`getMessage` when + :meth:`Formatter.format(record)` is invoked. + + .. attribute:: msg + + User-supplied :ref:`format string` or arbitrary object + (see :ref:`arbitrary-object-messages`) used in :meth:`getMessage`. + + .. attribute:: name + + Name of the logger that emitted the record. + + .. attribute:: pathname + + Absolute pathname of the source file of origin. .. method:: getMessage() Returns the message for this :class:`LogRecord` instance after merging any - user-supplied arguments with the message. + user-supplied arguments with the message. If the user-supplied message + argument to the logging call is not a string, :func:`str` is called on it to + convert it to a string. This allows use of user-defined classes as + messages, whose ``__str__`` method can return the actual format string to + be used. + + .. versionchanged:: 2.5 + *func* was added. .. _logger-adapter: Modified: python/branches/release27-maint/Doc/library/logging.rst ============================================================================== --- python/branches/release27-maint/Doc/library/logging.rst (original) +++ python/branches/release27-maint/Doc/library/logging.rst Sat Sep 11 12:25:28 2010 @@ -730,7 +730,7 @@ d = {'clientip': '192.168.0.1', 'user': 'fbloggs'} logging.warning("Protocol problem: %s", "connection reset", extra=d) - would print something like :: + would print something like:: 2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset @@ -1679,6 +1679,8 @@ the :meth:`makePickle` method and implementing your alternative there, as well as adapting the above script to use your alternative serialization. +.. _arbitrary-object-messages: + Using arbitrary objects as messages ----------------------------------- @@ -2562,6 +2564,8 @@ +-------------------------+-----------------------------------------------+ | ``%(process)d`` | Process ID (if available). | +-------------------------+-----------------------------------------------+ +| ``%(processName)s`` | Process name (if available). | ++-------------------------+-----------------------------------------------+ | ``%(message)s`` | The logged message, computed as ``msg % | | | args``. | +-------------------------+-----------------------------------------------+ @@ -2573,11 +2577,10 @@ .. class:: Formatter([fmt[, datefmt]]) Returns a new instance of the :class:`Formatter` class. The instance is - initialized with a format string for the message as a whole, as well as a format - string for the date/time portion of a message. If no *fmt* is specified, - ``'%(message)s'`` is used. If no *datefmt* is specified, the ISO8601 date format - is used. - + initialized with a format string for the message as a whole, as well as a + format string for the date/time portion of a message. If no *fmt* is + specified, ``'%(message)s'`` is used. If no *datefmt* is specified, the + ISO8601 date format is used. .. method:: format(record) @@ -2633,7 +2636,7 @@ Returns an instance of the :class:`Filter` class. If *name* is specified, it names a logger which, together with its children, will have its events allowed - through the filter. If no name is specified, allows every event. + through the filter. If *name* is the empty string, allows every event. .. method:: filter(record) @@ -2654,35 +2657,71 @@ LogRecord Objects ----------------- -:class:`LogRecord` instances are created every time something is logged. They -contain all the information pertinent to the event being logged. The main -information passed in is in msg and args, which are combined using msg % args to -create the message field of the record. The record also includes information -such as when the record was created, the source line where the logging call was -made, and any exception information to be logged. - - -.. class:: LogRecord(name, lvl, pathname, lineno, msg, args, exc_info [, func]) - - Returns an instance of :class:`LogRecord` initialized with interesting - information. The *name* is the logger name; *lvl* is the numeric level; - *pathname* is the absolute pathname of the source file in which the logging - call was made; *lineno* is the line number in that file where the logging - call is found; *msg* is the user-supplied message (a format string); *args* - is the tuple which, together with *msg*, makes up the user message; and - *exc_info* is the exception tuple obtained by calling :func:`sys.exc_info` - (or :const:`None`, if no exception information is available). The *func* is - the name of the function from which the logging call was made. If not - specified, it defaults to ``None``. +:class:`LogRecord` instances are created automatically by the :class:`Logger` +every time something is logged, and can be created manually via +:func:`makeLogRecord` (for example, from a pickled event received over the +wire). - .. versionchanged:: 2.5 - *func* was added. +.. class:: + LogRecord(name, lvl, pathname, lineno, msg, args, exc_info [, func=None]) + + Contains all the information pertinent to the event being logged. + + The primary information is passed in :attr:`msg` and :attr:`args`, which + are combined using ``msg % args`` to create the :attr:`message` field of the + record. + + .. attribute:: args + + Tuple of arguments to be used in formatting :attr:`msg`. + + .. attribute:: exc_info + + Exception tuple (? la `sys.exc_info`) or `None` if no exception + information is availble. + + .. attribute:: func + + Name of the function of origin (i.e. in which the logging call was made). + + .. attribute:: lineno + + Line number in the source file of origin. + + .. attribute:: lvl + + Numeric logging level. + + .. attribute:: message + + Bound to the result of :meth:`getMessage` when + :meth:`Formatter.format(record)` is invoked. + + .. attribute:: msg + + User-supplied :ref:`format string` or arbitrary object + (see :ref:`arbitrary-object-messages`) used in :meth:`getMessage`. + + .. attribute:: name + + Name of the logger that emitted the record. + + .. attribute:: pathname + + Absolute pathname of the source file of origin. .. method:: getMessage() Returns the message for this :class:`LogRecord` instance after merging any - user-supplied arguments with the message. + user-supplied arguments with the message. If the user-supplied message + argument to the logging call is not a string, :func:`str` is called on it to + convert it to a string. This allows use of user-defined classes as + messages, whose ``__str__`` method can return the actual format string to + be used. + + .. versionchanged:: 2.5 + *func* was added. .. _logger-adapter: From python-checkins at python.org Sat Sep 11 14:52:30 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 11 Sep 2010 14:52:30 +0200 (CEST) Subject: [Python-checkins] r84710 - python/branches/py3k/Lib/test/test_unicode_file.py Message-ID: <20100911125230.D3995EEA0E@mail.python.org> Author: victor.stinner Date: Sat Sep 11 14:52:30 2010 New Revision: 84710 Log: Issue #9819: don't try to encode TESTFN_UNICODE on Windows mbcs (Windows default filesystem encoding) is now strict by default, and depending on the code page, TESTFN_UNICODE may not be encodable to bytes. Remove also unused "encoded" argument from _do_directory() method. Modified: python/branches/py3k/Lib/test/test_unicode_file.py Modified: python/branches/py3k/Lib/test/test_unicode_file.py ============================================================================== --- python/branches/py3k/Lib/test/test_unicode_file.py (original) +++ python/branches/py3k/Lib/test/test_unicode_file.py Sat Sep 11 14:52:30 2010 @@ -8,12 +8,13 @@ from test.support import (run_unittest, rmtree, TESTFN_ENCODING, TESTFN_UNICODE, TESTFN_UNENCODABLE) -try: - TESTFN_UNICODE.encode(TESTFN_ENCODING) -except (UnicodeError, TypeError): - # Either the file system encoding is None, or the file name - # cannot be encoded in the file system encoding. - raise unittest.SkipTest("No Unicode filesystem semantics on this platform.") +if not os.path.supports_unicode_filenames: + try: + TESTFN_UNICODE.encode(TESTFN_ENCODING) + except (UnicodeError, TypeError): + # Either the file system encoding is None, or the file name + # cannot be encoded in the file system encoding. + raise unittest.SkipTest("No Unicode filesystem semantics on this platform.") def remove_if_exists(filename): if os.path.exists(filename): @@ -90,7 +91,7 @@ shutil.copy2(filename1, filename2 + ".new") os.unlink(filename1 + ".new") - def _do_directory(self, make_name, chdir_name, encoded): + def _do_directory(self, make_name, chdir_name): cwd = os.getcwdb() if os.path.isdir(make_name): rmtree(make_name) @@ -98,12 +99,8 @@ try: os.chdir(chdir_name) try: - if not encoded: - cwd_result = os.getcwd() - name_result = make_name - else: - cwd_result = os.getcwdb().decode(TESTFN_ENCODING) - name_result = make_name.decode(TESTFN_ENCODING) + cwd_result = os.getcwd() + name_result = make_name cwd_result = unicodedata.normalize("NFD", cwd_result) name_result = unicodedata.normalize("NFD", name_result) @@ -155,12 +152,11 @@ # Make dir with encoded, chdir with unicode, checkdir with encoded # (or unicode/encoded/unicode, etc ext = ".dir" - self._do_directory(TESTFN_UNICODE+ext, TESTFN_UNICODE+ext, False) + self._do_directory(TESTFN_UNICODE+ext, TESTFN_UNICODE+ext) # Our directory name that can't use a non-unicode name. if TESTFN_UNENCODABLE is not None: self._do_directory(TESTFN_UNENCODABLE+ext, - TESTFN_UNENCODABLE+ext, - False) + TESTFN_UNENCODABLE+ext) def test_main(): run_unittest(__name__) From python-checkins at python.org Sat Sep 11 17:28:56 2010 From: python-checkins at python.org (eric.araujo) Date: Sat, 11 Sep 2010 17:28:56 +0200 (CEST) Subject: [Python-checkins] r84711 - python/branches/py3k/Lib/distutils/command/sdist.py Message-ID: <20100911152856.266D1EE981@mail.python.org> Author: eric.araujo Date: Sat Sep 11 17:28:56 2010 New Revision: 84711 Log: Fix typo in option name Modified: python/branches/py3k/Lib/distutils/command/sdist.py Modified: python/branches/py3k/Lib/distutils/command/sdist.py ============================================================================== --- python/branches/py3k/Lib/distutils/command/sdist.py (original) +++ python/branches/py3k/Lib/distutils/command/sdist.py Sat Sep 11 17:28:56 2010 @@ -73,7 +73,7 @@ ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), - ('medata-check', None, + ('metadata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), ] From python-checkins at python.org Sat Sep 11 17:30:19 2010 From: python-checkins at python.org (eric.araujo) Date: Sat, 11 Sep 2010 17:30:19 +0200 (CEST) Subject: [Python-checkins] r84712 - in python/branches/release31-maint: Lib/distutils/command/sdist.py Message-ID: <20100911153019.BD167EE981@mail.python.org> Author: eric.araujo Date: Sat Sep 11 17:30:19 2010 New Revision: 84712 Log: Merged revisions 84711 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84711 | eric.araujo | 2010-09-11 17:28:56 +0200 (sam., 11 sept. 2010) | 2 lines Fix typo in option name ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/distutils/command/sdist.py Modified: python/branches/release31-maint/Lib/distutils/command/sdist.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/command/sdist.py (original) +++ python/branches/release31-maint/Lib/distutils/command/sdist.py Sat Sep 11 17:30:19 2010 @@ -73,7 +73,7 @@ ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), - ('medata-check', None, + ('metadata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), ] From python-checkins at python.org Sat Sep 11 17:31:13 2010 From: python-checkins at python.org (eric.araujo) Date: Sat, 11 Sep 2010 17:31:13 +0200 (CEST) Subject: [Python-checkins] r84713 - in python/branches/release27-maint: Lib/distutils/command/sdist.py Message-ID: <20100911153113.8F169EE990@mail.python.org> Author: eric.araujo Date: Sat Sep 11 17:31:13 2010 New Revision: 84713 Log: Merged revisions 84711 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84711 | eric.araujo | 2010-09-11 17:28:56 +0200 (sam., 11 sept. 2010) | 2 lines Fix typo in option name ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/distutils/command/sdist.py Modified: python/branches/release27-maint/Lib/distutils/command/sdist.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/command/sdist.py (original) +++ python/branches/release27-maint/Lib/distutils/command/sdist.py Sat Sep 11 17:31:13 2010 @@ -73,7 +73,7 @@ ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), - ('medata-check', None, + ('metadata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), ('owner=', 'u', From python-checkins at python.org Sat Sep 11 18:02:06 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 11 Sep 2010 18:02:06 +0200 (CEST) Subject: [Python-checkins] r84714 - in python/branches/py3k: Misc/NEWS Objects/abstract.c Message-ID: <20100911160206.5F45AEEA6A@mail.python.org> Author: benjamin.peterson Date: Sat Sep 11 18:02:03 2010 New Revision: 84714 Log: check for NULL tp_as_mapping in PySequence_(Get/Set/Del)Slice #9834 Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/abstract.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 11 18:02:03 2010 @@ -108,6 +108,13 @@ guaranteed to exist in all Python implementations and the names of hash algorithms available in the current process. +C-API +----- + +- Issue #9834: Don't segfault in PySequence_GetSlice, PySequence_SetSlice, or + PySequence_DelSlice when the object doesn't have any mapping operations + defined. + Tools/Demos ----------- Modified: python/branches/py3k/Objects/abstract.c ============================================================================== --- python/branches/py3k/Objects/abstract.c (original) +++ python/branches/py3k/Objects/abstract.c Sat Sep 11 18:02:03 2010 @@ -1612,7 +1612,7 @@ if (!s) return null_error(); mp = s->ob_type->tp_as_mapping; - if (mp->mp_subscript) { + if (mp && mp->mp_subscript) { PyObject *res; PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) @@ -1690,7 +1690,7 @@ } mp = s->ob_type->tp_as_mapping; - if (mp->mp_ass_subscript) { + if (mp && mp->mp_ass_subscript) { int res; PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) @@ -1715,7 +1715,7 @@ } mp = s->ob_type->tp_as_mapping; - if (mp->mp_ass_subscript) { + if (mp && mp->mp_ass_subscript) { int res; PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) From python-checkins at python.org Sat Sep 11 18:03:34 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 11 Sep 2010 18:03:34 +0200 (CEST) Subject: [Python-checkins] r84715 - in python/branches/release31-maint: Misc/NEWS Objects/abstract.c Message-ID: <20100911160334.21157EEA5F@mail.python.org> Author: benjamin.peterson Date: Sat Sep 11 18:03:33 2010 New Revision: 84715 Log: Merged revisions 84714 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84714 | benjamin.peterson | 2010-09-11 11:02:03 -0500 (Sat, 11 Sep 2010) | 1 line check for NULL tp_as_mapping in PySequence_(Get/Set/Del)Slice #9834 ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Objects/abstract.c Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sat Sep 11 18:03:33 2010 @@ -105,6 +105,10 @@ C-API ----- +- Issue #9834: Don't segfault in PySequence_GetSlice, PySequence_SetSlice, or + PySequence_DelSlice when the object doesn't have any mapping operations + defined. + - Issue #5753: A new C API function, :cfunc:`PySys_SetArgvEx`, allows embedders of the interpreter to set sys.argv without also modifying sys.path. This helps fix `CVE-2008-5983 Modified: python/branches/release31-maint/Objects/abstract.c ============================================================================== --- python/branches/release31-maint/Objects/abstract.c (original) +++ python/branches/release31-maint/Objects/abstract.c Sat Sep 11 18:03:33 2010 @@ -1623,7 +1623,7 @@ if (!s) return null_error(); mp = s->ob_type->tp_as_mapping; - if (mp->mp_subscript) { + if (mp && mp->mp_subscript) { PyObject *res; PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) @@ -1701,7 +1701,7 @@ } mp = s->ob_type->tp_as_mapping; - if (mp->mp_ass_subscript) { + if (mp && mp->mp_ass_subscript) { int res; PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) @@ -1726,7 +1726,7 @@ } mp = s->ob_type->tp_as_mapping; - if (mp->mp_ass_subscript) { + if (mp && mp->mp_ass_subscript) { int res; PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) From python-checkins at python.org Sat Sep 11 18:23:57 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 11 Sep 2010 18:23:57 +0200 (CEST) Subject: [Python-checkins] r84716 - in python/branches/release31-maint: .bzrignore .hgignore Modules Message-ID: <20100911162357.62697EEA4B@mail.python.org> Author: benjamin.peterson Date: Sat Sep 11 18:23:57 2010 New Revision: 84716 Log: Merged revisions 84705 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84705 | georg.brandl | 2010-09-11 01:39:58 -0500 (Sat, 11 Sep 2010) | 1 line Add new generated file to ignoreds. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/.bzrignore python/branches/release31-maint/.hgignore python/branches/release31-maint/Modules/ (props changed) Modified: python/branches/release31-maint/.bzrignore ============================================================================== --- python/branches/release31-maint/.bzrignore (original) +++ python/branches/release31-maint/.bzrignore Sat Sep 11 18:23:57 2010 @@ -45,6 +45,7 @@ Modules/Setup.config Modules/Setup.local Modules/config.c +Modules/ld_so_aix Parser/pgen Lib/lib2to3/Grammar*.pickle Lib/lib2to3/PatternGrammar*.pickle Modified: python/branches/release31-maint/.hgignore ============================================================================== --- python/branches/release31-maint/.hgignore (original) +++ python/branches/release31-maint/.hgignore Sat Sep 11 18:23:57 2010 @@ -48,7 +48,7 @@ Modules/config.c Parser/pgen core - +Modules/ld_so_aix$ syntax: glob libpython*.a *.o From python-checkins at python.org Sat Sep 11 18:39:57 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 11 Sep 2010 18:39:57 +0200 (CEST) Subject: [Python-checkins] r84717 - python/branches/py3k/Objects/object.c Message-ID: <20100911163957.8526DDF12@mail.python.org> Author: benjamin.peterson Date: Sat Sep 11 18:39:57 2010 New Revision: 84717 Log: fix formatting Modified: python/branches/py3k/Objects/object.c Modified: python/branches/py3k/Objects/object.c ============================================================================== --- python/branches/py3k/Objects/object.c (original) +++ python/branches/py3k/Objects/object.c Sat Sep 11 18:39:57 2010 @@ -479,13 +479,13 @@ result = PyObject_CallFunctionObjArgs(func, NULL); Py_DECREF(func); if (result == NULL) - return NULL; + return NULL; if (!PyBytes_Check(result)) { - PyErr_Format(PyExc_TypeError, - "__bytes__ returned non-bytes (type %.200s)", - Py_TYPE(result)->tp_name); - Py_DECREF(result); - return NULL; + PyErr_Format(PyExc_TypeError, + "__bytes__ returned non-bytes (type %.200s)", + Py_TYPE(result)->tp_name); + Py_DECREF(result); + return NULL; } return result; } From python-checkins at python.org Sat Sep 11 18:40:47 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 11 Sep 2010 18:40:47 +0200 (CEST) Subject: [Python-checkins] r84718 - in python/branches/release31-maint: Objects/object.c Message-ID: <20100911164047.60155EE9A1@mail.python.org> Author: benjamin.peterson Date: Sat Sep 11 18:40:47 2010 New Revision: 84718 Log: Merged revisions 84717 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84717 | benjamin.peterson | 2010-09-11 11:39:57 -0500 (Sat, 11 Sep 2010) | 1 line fix formatting ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Objects/object.c Modified: python/branches/release31-maint/Objects/object.c ============================================================================== --- python/branches/release31-maint/Objects/object.c (original) +++ python/branches/release31-maint/Objects/object.c Sat Sep 11 18:40:47 2010 @@ -479,13 +479,13 @@ result = PyObject_CallFunctionObjArgs(func, NULL); Py_DECREF(func); if (result == NULL) - return NULL; + return NULL; if (!PyBytes_Check(result)) { - PyErr_Format(PyExc_TypeError, - "__bytes__ returned non-bytes (type %.200s)", - Py_TYPE(result)->tp_name); - Py_DECREF(result); - return NULL; + PyErr_Format(PyExc_TypeError, + "__bytes__ returned non-bytes (type %.200s)", + Py_TYPE(result)->tp_name); + Py_DECREF(result); + return NULL; } return result; } From python-checkins at python.org Sat Sep 11 20:12:25 2010 From: python-checkins at python.org (r.david.murray) Date: Sat, 11 Sep 2010 20:12:25 +0200 (CEST) Subject: [Python-checkins] r84719 - python/branches/py3k/Doc/howto/doanddont.rst Message-ID: <20100911181225.F0DD7EEA2E@mail.python.org> Author: r.david.murray Date: Sat Sep 11 20:12:25 2010 New Revision: 84719 Log: #9608, #8512 : clarify and improve discussion of exceptions in howto. Modified: python/branches/py3k/Doc/howto/doanddont.rst Modified: python/branches/py3k/Doc/howto/doanddont.rst ============================================================================== --- python/branches/py3k/Doc/howto/doanddont.rst (original) +++ python/branches/py3k/Doc/howto/doanddont.rst Sat Sep 11 20:12:25 2010 @@ -111,30 +111,40 @@ ------- Python has the ``except:`` clause, which catches all exceptions. Since *every* -error in Python raises an exception, this makes many programming errors look -like runtime problems, and hinders the debugging process. +error in Python raises an exception, using ``except:`` can make many +programming errors look like runtime problems, which hinders the debugging +process. -The following code shows a great example:: +The following code shows a great example of why this is bad:: try: foo = opne("file") # misspelled "open" except: sys.exit("could not open file!") -The second line triggers a :exc:`NameError` which is caught by the except -clause. The program will exit, and you will have no idea that this has nothing -to do with the readability of ``"file"``. +The second line triggers a :exc:`NameError`, which is caught by the except +clause. The program will exit, and the error message the program prints will +make you think the problem is the readability of ``"file"`` when in fact +the real error has nothing to do with ``"file"``. -The example above is better written :: +A better way to write the above is :: try: - foo = opne("file") # will be changed to "open" as soon as we run it + foo = opne("file") except IOError: sys.exit("could not open file") -There are some situations in which the ``except:`` clause is useful: for -example, in a framework when running callbacks, it is good not to let any -callback disturb the framework. +When this is run, Python will produce a traceback showing the :exc:`NameError`, +and it will be immediately apparent what needs to be fixed. + +.. index:: bare except, except; bare + +Because ``except:`` catches *all* exceptions, including :exc:`SystemExit`, +:exc:`KeyboardInterrupt`, and :exc:`GeneratorExit` (which is not an error and +should not normally be caught by user code), using a bare ``except:`` is almost +never a good idea. In situations where you need to catch all "normal" errors, +such as in a framework that runs callbacks, you can catch the base class for +all normal exceptions, :exc:`Exception`. Exceptions @@ -152,51 +162,60 @@ sys.exit(1) return open(file).readline() -Consider the case the file gets deleted between the time the call to -:func:`os.path.exists` is made and the time :func:`open` is called. That means -the last line will raise an :exc:`IOError`. The same would happen if *file* -exists but has no read permission. Since testing this on a normal machine on -existing and non-existing files make it seem bugless, that means in testing the -results will seem fine, and the code will get shipped. Then an unhandled -:exc:`IOError` escapes to the user, who has to watch the ugly traceback. +Consider the case where the file gets deleted between the time the call to +:func:`os.path.exists` is made and the time :func:`open` is called. In that +case the last line will raise an :exc:`IOError`. The same thing would happen +if *file* exists but has no read permission. Since testing this on a normal +machine on existent and non-existent files makes it seem bugless, the test +results will seem fine, and the code will get shipped. Later an unhandled +:exc:`IOError` (or perhaps some other :exc:`EnvironmentError`) escapes to the +user, who gets to watch the ugly traceback. -Here is a better way to do it. :: +Here is a somewhat better way to do it. :: def get_status(file): try: return open(file).readline() - except (IOError, OSError): - print("file not found") + except EnvironmentError as err: + print("Unable to open file: {}".format(err)) sys.exit(1) -In this version, \*either\* the file gets opened and the line is read (so it -works even on flaky NFS or SMB connections), or the message is printed and the -application aborted. - -Still, :func:`get_status` makes too many assumptions --- that it will only be -used in a short running script, and not, say, in a long running server. Sure, -the caller could do something like :: +In this version, *either* the file gets opened and the line is read (so it +works even on flaky NFS or SMB connections), or an error message is printed +that provides all the available information on why the open failed, and the +application is aborted. + +However, even this version of :func:`get_status` makes too many assumptions --- +that it will only be used in a short running script, and not, say, in a long +running server. Sure, the caller could do something like :: try: status = get_status(log) except SystemExit: status = None -So, try to make as few ``except`` clauses in your code --- those will usually be -a catch-all in the :func:`main`, or inside calls which should always succeed. +But there is a better way. You should try to use as few ``except`` clauses in +your code as you can --- the ones you do use will usually be inside calls which +should always succeed, or a catch-all in a main function. -So, the best version is probably :: +So, an even better version of :func:`get_status()` is probably :: def get_status(file): return open(file).readline() -The caller can deal with the exception if it wants (for example, if it tries +The caller can deal with the exception if it wants (for example, if it tries several files in a loop), or just let the exception filter upwards to *its* caller. -The last version is not very good either --- due to implementation details, the -file would not be closed when an exception is raised until the handler finishes, -and perhaps not at all in non-C implementations (e.g., Jython). :: +But the last version still has a serious problem --- due to implementation +details in CPython, the file would not be closed when an exception is raised +until the exception handler finishes; and, worse, in other implementations +(e.g., Jython) it might not be closed at all regardless of whether or not +an exception is raised. + +The best version of this function uses the ``open()`` call as a context +manager, which will ensure that the file gets closed as soon as the +function returns:: def get_status(file): with open(file) as fp: From python-checkins at python.org Sat Sep 11 20:20:06 2010 From: python-checkins at python.org (r.david.murray) Date: Sat, 11 Sep 2010 20:20:06 +0200 (CEST) Subject: [Python-checkins] r84720 - in python/branches/release31-maint: Doc/howto/doanddont.rst Message-ID: <20100911182006.AF849EEA62@mail.python.org> Author: r.david.murray Date: Sat Sep 11 20:20:06 2010 New Revision: 84720 Log: Merged revisions 84719 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84719 | r.david.murray | 2010-09-11 14:12:25 -0400 (Sat, 11 Sep 2010) | 2 lines #9608, #8512 : clarify and improve discussion of exceptions in howto. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/howto/doanddont.rst Modified: python/branches/release31-maint/Doc/howto/doanddont.rst ============================================================================== --- python/branches/release31-maint/Doc/howto/doanddont.rst (original) +++ python/branches/release31-maint/Doc/howto/doanddont.rst Sat Sep 11 20:20:06 2010 @@ -111,30 +111,40 @@ ------- Python has the ``except:`` clause, which catches all exceptions. Since *every* -error in Python raises an exception, this makes many programming errors look -like runtime problems, and hinders the debugging process. +error in Python raises an exception, using ``except:`` can make many +programming errors look like runtime problems, which hinders the debugging +process. -The following code shows a great example:: +The following code shows a great example of why this is bad:: try: foo = opne("file") # misspelled "open" except: sys.exit("could not open file!") -The second line triggers a :exc:`NameError` which is caught by the except -clause. The program will exit, and you will have no idea that this has nothing -to do with the readability of ``"file"``. +The second line triggers a :exc:`NameError`, which is caught by the except +clause. The program will exit, and the error message the program prints will +make you think the problem is the readability of ``"file"`` when in fact +the real error has nothing to do with ``"file"``. -The example above is better written :: +A better way to write the above is :: try: - foo = opne("file") # will be changed to "open" as soon as we run it + foo = opne("file") except IOError: sys.exit("could not open file") -There are some situations in which the ``except:`` clause is useful: for -example, in a framework when running callbacks, it is good not to let any -callback disturb the framework. +When this is run, Python will produce a traceback showing the :exc:`NameError`, +and it will be immediately apparent what needs to be fixed. + +.. index:: bare except, except; bare + +Because ``except:`` catches *all* exceptions, including :exc:`SystemExit`, +:exc:`KeyboardInterrupt`, and :exc:`GeneratorExit` (which is not an error and +should not normally be caught by user code), using a bare ``except:`` is almost +never a good idea. In situations where you need to catch all "normal" errors, +such as in a framework that runs callbacks, you can catch the base class for +all normal exceptions, :exc:`Exception`. Exceptions @@ -152,51 +162,60 @@ sys.exit(1) return open(file).readline() -Consider the case the file gets deleted between the time the call to -:func:`os.path.exists` is made and the time :func:`open` is called. That means -the last line will throw an :exc:`IOError`. The same would happen if *file* -exists but has no read permission. Since testing this on a normal machine on -existing and non-existing files make it seem bugless, that means in testing the -results will seem fine, and the code will get shipped. Then an unhandled -:exc:`IOError` escapes to the user, who has to watch the ugly traceback. +Consider the case where the file gets deleted between the time the call to +:func:`os.path.exists` is made and the time :func:`open` is called. In that +case the last line will raise an :exc:`IOError`. The same thing would happen +if *file* exists but has no read permission. Since testing this on a normal +machine on existent and non-existent files makes it seem bugless, the test +results will seem fine, and the code will get shipped. Later an unhandled +:exc:`IOError` (or perhaps some other :exc:`EnvironmentError`) escapes to the +user, who gets to watch the ugly traceback. -Here is a better way to do it. :: +Here is a somewhat better way to do it. :: def get_status(file): try: return open(file).readline() - except (IOError, OSError): - print("file not found") + except EnvironmentError as err: + print("Unable to open file: {}".format(err)) sys.exit(1) -In this version, \*either\* the file gets opened and the line is read (so it -works even on flaky NFS or SMB connections), or the message is printed and the -application aborted. - -Still, :func:`get_status` makes too many assumptions --- that it will only be -used in a short running script, and not, say, in a long running server. Sure, -the caller could do something like :: +In this version, *either* the file gets opened and the line is read (so it +works even on flaky NFS or SMB connections), or an error message is printed +that provides all the available information on why the open failed, and the +application is aborted. + +However, even this version of :func:`get_status` makes too many assumptions --- +that it will only be used in a short running script, and not, say, in a long +running server. Sure, the caller could do something like :: try: status = get_status(log) except SystemExit: status = None -So, try to make as few ``except`` clauses in your code --- those will usually be -a catch-all in the :func:`main`, or inside calls which should always succeed. +But there is a better way. You should try to use as few ``except`` clauses in +your code as you can --- the ones you do use will usually be inside calls which +should always succeed, or a catch-all in a main function. -So, the best version is probably :: +So, an even better version of :func:`get_status()` is probably :: def get_status(file): return open(file).readline() -The caller can deal with the exception if it wants (for example, if it tries +The caller can deal with the exception if it wants (for example, if it tries several files in a loop), or just let the exception filter upwards to *its* caller. -The last version is not very good either --- due to implementation details, the -file would not be closed when an exception is raised until the handler finishes, -and perhaps not at all in non-C implementations (e.g., Jython). :: +But the last version still has a serious problem --- due to implementation +details in CPython, the file would not be closed when an exception is raised +until the exception handler finishes; and, worse, in other implementations +(e.g., Jython) it might not be closed at all regardless of whether or not +an exception is raised. + +The best version of this function uses the ``open()`` call as a context +manager, which will ensure that the file gets closed as soon as the +function returns:: def get_status(file): with open(file) as fp: From python-checkins at python.org Sat Sep 11 20:53:33 2010 From: python-checkins at python.org (r.david.murray) Date: Sat, 11 Sep 2010 20:53:33 +0200 (CEST) Subject: [Python-checkins] r84719 - svn:log Message-ID: <20100911185333.9FB61EEA71@mail.python.org> Author: r.david.murray Revision: 84719 Property Name: svn:log Action: modified Property diff: --- old property value +++ new property value @@ -1 +1 @@ -#9608, #8512 : clarify and improve discussion of exceptions in howto. +#9608, #8518 : clarify and improve discussion of exceptions in howto. From python-checkins at python.org Sat Sep 11 20:54:27 2010 From: python-checkins at python.org (r.david.murray) Date: Sat, 11 Sep 2010 20:54:27 +0200 (CEST) Subject: [Python-checkins] r84720 - svn:log Message-ID: <20100911185427.36E75FA9F@mail.python.org> Author: r.david.murray Revision: 84720 Property Name: svn:log Action: modified Property diff: --- old property value +++ new property value @@ -4,5 +4,5 @@ ........ r84719 | r.david.murray | 2010-09-11 14:12:25 -0400 (Sat, 11 Sep 2010) | 2 lines - #9608, #8512 : clarify and improve discussion of exceptions in howto. + #9608, #8518 : clarify and improve discussion of exceptions in howto. ........ From python-checkins at python.org Sat Sep 11 20:57:20 2010 From: python-checkins at python.org (r.david.murray) Date: Sat, 11 Sep 2010 20:57:20 +0200 (CEST) Subject: [Python-checkins] r84721 - in python/branches/release27-maint: Doc/howto/doanddont.rst Message-ID: <20100911185720.58FACEE9CA@mail.python.org> Author: r.david.murray Date: Sat Sep 11 20:57:20 2010 New Revision: 84721 Log: Merged revisions 84719 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84719 | r.david.murray | 2010-09-11 14:12:25 -0400 (Sat, 11 Sep 2010) | 2 lines #9608, #8518 : clarify and improve discussion of exceptions in howto. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/howto/doanddont.rst Modified: python/branches/release27-maint/Doc/howto/doanddont.rst ============================================================================== --- python/branches/release27-maint/Doc/howto/doanddont.rst (original) +++ python/branches/release27-maint/Doc/howto/doanddont.rst Sat Sep 11 20:57:20 2010 @@ -144,30 +144,44 @@ ------- Python has the ``except:`` clause, which catches all exceptions. Since *every* -error in Python raises an exception, this makes many programming errors look -like runtime problems, and hinders the debugging process. +error in Python raises an exception, using ``except:`` can make many +programming errors look like runtime problems, which hinders the debugging +process. -The following code shows a great example:: +The following code shows a great example of why this is bad:: try: foo = opne("file") # misspelled "open" except: sys.exit("could not open file!") -The second line triggers a :exc:`NameError` which is caught by the except -clause. The program will exit, and you will have no idea that this has nothing -to do with the readability of ``"file"``. +The second line triggers a :exc:`NameError`, which is caught by the except +clause. The program will exit, and the error message the program prints will +make you think the problem is the readability of ``"file"`` when in fact +the real error has nothing to do with ``"file"``. -The example above is better written :: +A better way to write the above is :: try: - foo = opne("file") # will be changed to "open" as soon as we run it + foo = opne("file") except IOError: sys.exit("could not open file") -There are some situations in which the ``except:`` clause is useful: for -example, in a framework when running callbacks, it is good not to let any -callback disturb the framework. +When this is run, Python will produce a traceback showing the :exc:`NameError`, +and it will be immediately apparent what needs to be fixed. + +.. index:: bare except, except; bare + +Because ``except:`` catches *all* exceptions, including :exc:`SystemExit`, +:exc:`KeyboardInterrupt`, and :exc:`GeneratorExit` (which is not an error and +should not normally be caught by user code), using a bare ``except:`` is almost +never a good idea. In situations where you need to catch all "normal" errors, +such as in a framework that runs callbacks, you can catch the base class for +all normal exceptions, :exc:`Exception`. Unfortunately in Python2 it is +possible for third-party code to raise exceptions that do not inherit from +:exc:`Exception`, so in Python 2.x there are some cases where you may have to +use a bare ``except:`` and manually re-raise the exceptions you don't want +to catch. Exceptions @@ -185,51 +199,60 @@ sys.exit(1) return open(file).readline() -Consider the case the file gets deleted between the time the call to -:func:`os.path.exists` is made and the time :func:`open` is called. That means -the last line will throw an :exc:`IOError`. The same would happen if *file* -exists but has no read permission. Since testing this on a normal machine on -existing and non-existing files make it seem bugless, that means in testing the -results will seem fine, and the code will get shipped. Then an unhandled -:exc:`IOError` escapes to the user, who has to watch the ugly traceback. +Consider the case where the file gets deleted between the time the call to +:func:`os.path.exists` is made and the time :func:`open` is called. In that +case the last line will raise an :exc:`IOError`. The same thing would happen +if *file* exists but has no read permission. Since testing this on a normal +machine on existent and non-existent files makes it seem bugless, the test +results will seem fine, and the code will get shipped. Later an unhandled +:exc:`IOError` (or perhaps some other :exc:`EnvironmentError`) escapes to the +user, who gets to watch the ugly traceback. -Here is a better way to do it. :: +Here is a somewhat better way to do it. :: def get_status(file): try: return open(file).readline() - except (IOError, OSError): - print "file not found" + except EnvironmentError as err: + print "Unable to open file: {}".format(err) sys.exit(1) -In this version, \*either\* the file gets opened and the line is read (so it -works even on flaky NFS or SMB connections), or the message is printed and the -application aborted. - -Still, :func:`get_status` makes too many assumptions --- that it will only be -used in a short running script, and not, say, in a long running server. Sure, -the caller could do something like :: +In this version, *either* the file gets opened and the line is read (so it +works even on flaky NFS or SMB connections), or an error message is printed +that provides all the available information on why the open failed, and the +application is aborted. + +However, even this version of :func:`get_status` makes too many assumptions --- +that it will only be used in a short running script, and not, say, in a long +running server. Sure, the caller could do something like :: try: status = get_status(log) except SystemExit: status = None -So, try to make as few ``except`` clauses in your code --- those will usually be -a catch-all in the :func:`main`, or inside calls which should always succeed. +But there is a better way. You should try to use as few ``except`` clauses in +your code as you can --- the ones you do use will usually be inside calls which +should always succeed, or a catch-all in a main function. -So, the best version is probably :: +So, an even better version of :func:`get_status()` is probably :: def get_status(file): return open(file).readline() -The caller can deal with the exception if it wants (for example, if it tries +The caller can deal with the exception if it wants (for example, if it tries several files in a loop), or just let the exception filter upwards to *its* caller. -The last version is not very good either --- due to implementation details, the -file would not be closed when an exception is raised until the handler finishes, -and perhaps not at all in non-C implementations (e.g., Jython). :: +But the last version still has a serious problem --- due to implementation +details in CPython, the file would not be closed when an exception is raised +until the exception handler finishes; and, worse, in other implementations +(e.g., Jython) it might not be closed at all regardless of whether or not +an exception is raised. + +The best version of this function uses the ``open()`` call as a context +manager, which will ensure that the file gets closed as soon as the +function returns:: def get_status(file): with open(file) as fp: From python-checkins at python.org Sat Sep 11 21:15:40 2010 From: python-checkins at python.org (r.david.murray) Date: Sat, 11 Sep 2010 21:15:40 +0200 (CEST) Subject: [Python-checkins] r84722 - python/branches/release27-maint/Doc/howto/doanddont.rst Message-ID: <20100911191540.9FEF3EEA6B@mail.python.org> Author: r.david.murray Date: Sat Sep 11 21:15:40 2010 New Revision: 84722 Log: Fix typo in previous doc commit. Modified: python/branches/release27-maint/Doc/howto/doanddont.rst Modified: python/branches/release27-maint/Doc/howto/doanddont.rst ============================================================================== --- python/branches/release27-maint/Doc/howto/doanddont.rst (original) +++ python/branches/release27-maint/Doc/howto/doanddont.rst Sat Sep 11 21:15:40 2010 @@ -177,7 +177,7 @@ should not normally be caught by user code), using a bare ``except:`` is almost never a good idea. In situations where you need to catch all "normal" errors, such as in a framework that runs callbacks, you can catch the base class for -all normal exceptions, :exc:`Exception`. Unfortunately in Python2 it is +all normal exceptions, :exc:`Exception`. Unfortunately in Python 2.x it is possible for third-party code to raise exceptions that do not inherit from :exc:`Exception`, so in Python 2.x there are some cases where you may have to use a bare ``except:`` and manually re-raise the exceptions you don't want From python-checkins at python.org Sun Sep 12 00:26:07 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sun, 12 Sep 2010 00:26:07 +0200 (CEST) Subject: [Python-checkins] r84723 - python/branches/release31-maint Message-ID: <20100911222607.DA620FC4F@mail.python.org> Author: hirokazu.yamamoto Date: Sun Sep 12 00:26:07 2010 New Revision: 84723 Log: Blocked revisions 84643 via svnmerge ........ r84643 | hirokazu.yamamoto | 2010-09-09 15:08:36 +0900 | 4 lines Updated VC6 files. * pythoncore.dsp: updated project file * readme.txt: removed dead link * tcl852.patch: fixed patch. it was doubled. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Sun Sep 12 00:35:24 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sun, 12 Sep 2010 00:35:24 +0200 (CEST) Subject: [Python-checkins] r84724 - python/branches/py3k/Include/pythread.h Message-ID: <20100911223524.B5514EE985@mail.python.org> Author: hirokazu.yamamoto Date: Sun Sep 12 00:35:24 2010 New Revision: 84724 Log: Issue #9318: Use Py_LL for old compiler. Modified: python/branches/py3k/Include/pythread.h Modified: python/branches/py3k/Include/pythread.h ============================================================================== --- python/branches/py3k/Include/pythread.h (original) +++ python/branches/py3k/Include/pythread.h Sun Sep 12 00:35:24 2010 @@ -39,9 +39,9 @@ /* In the NT API, the timeout is a DWORD and is expressed in milliseconds */ #if defined (NT_THREADS) -#if (0xFFFFFFFFLL * 1000 < PY_TIMEOUT_MAX) +#if (Py_LL(0xFFFFFFFF) * 1000 < PY_TIMEOUT_MAX) #undef PY_TIMEOUT_MAX -#define PY_TIMEOUT_MAX (0xFFFFFFFFLL * 1000) +#define PY_TIMEOUT_MAX (Py_LL(0xFFFFFFFF) * 1000) #endif #endif From python-checkins at python.org Sun Sep 12 04:11:46 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 12 Sep 2010 04:11:46 +0200 (CEST) Subject: [Python-checkins] r84725 - in python/branches/release27-maint: Lib/collections.py Misc/NEWS Message-ID: <20100912021146.216FCEE98A@mail.python.org> Author: raymond.hettinger Date: Sun Sep 12 04:11:45 2010 New Revision: 84725 Log: Issue #9825: Remove __del__() from OrderedDict. Modified: python/branches/release27-maint/Lib/collections.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/collections.py ============================================================================== --- python/branches/release27-maint/Lib/collections.py (original) +++ python/branches/release27-maint/Lib/collections.py Sun Sep 12 04:11:45 2010 @@ -173,9 +173,6 @@ all(_imap(_eq, self.iteritems(), other.iteritems())) return dict.__eq__(self, other) - def __del__(self): - self.clear() # eliminate cyclical references - ################################################################################ ### namedtuple Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Sep 12 04:11:45 2010 @@ -43,6 +43,13 @@ Library ------- +- Issue #9825: removed __del__ from the definition of collections.OrderedDict. + This prevents user-created self-referencing ordered dictionaries from + becoming permanently uncollectable GC garbage. The downside is that + removing __del__ means that the internal doubly-linked list has to wait for + GC collection rather than freeing memory immediately when the refcnt drops + to zero. + - Issue #9816: random.Random.jumpahead(n) did not produce a sufficiently different internal state for small values of n. Fixed by salting the value. From solipsis at pitrou.net Sun Sep 12 05:08:45 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 12 Sep 2010 05:08:45 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84724): sum=6 Message-ID: py3k results for svn r84724 (hg cset 05f24bf164ac) -------------------------------------------------- test_unicode leaked [2, 2, 2] references, sum=6 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflognoNh-a', '-x'] From python-checkins at python.org Sun Sep 12 05:40:54 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 12 Sep 2010 05:40:54 +0200 (CEST) Subject: [Python-checkins] r84726 - python/branches/py3k/Objects/unicodeobject.c Message-ID: <20100912034054.3595AF882@mail.python.org> Author: benjamin.peterson Date: Sun Sep 12 05:40:54 2010 New Revision: 84726 Log: detect non-ascii characters much earlier (plugs ref leak) Modified: python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Sun Sep 12 05:40:54 2010 @@ -764,6 +764,13 @@ if (*f == 's') ++callcount; } + else if (128 <= (unsigned char)*f) { + PyErr_Format(PyExc_ValueError, + "PyUnicode_FromFormatV() expects an ASCII-encoded format " + "string, got a non-ascii byte: 0x%02x", + (unsigned char)*f); + return NULL; + } } /* step 2: allocate memory for the results of * PyObject_Str()/PyObject_Repr()/PyUnicode_DecodeUTF8() calls */ @@ -1103,13 +1110,6 @@ goto end; } } - else if (128 <= (unsigned char)*f) { - PyErr_Format(PyExc_ValueError, - "PyUnicode_FromFormatV() expects an ASCII-encoded format " - "string, got a non-ascii byte: 0x%02x", - (unsigned char)*f); - goto fail; - } else *s++ = *f; } From python-checkins at python.org Sun Sep 12 06:12:42 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 12 Sep 2010 06:12:42 +0200 (CEST) Subject: [Python-checkins] r84727 - in python/branches/py3k: Lib/collections.py Misc/NEWS Message-ID: <20100912041242.C927AFA7A@mail.python.org> Author: raymond.hettinger Date: Sun Sep 12 06:12:42 2010 New Revision: 84727 Log: Issue #9825: Replace OrderedDict.__del__() with weakrefs. Modified: python/branches/py3k/Lib/collections.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Sun Sep 12 06:12:42 2010 @@ -11,12 +11,16 @@ from keyword import iskeyword as _iskeyword import sys as _sys import heapq as _heapq +from weakref import proxy as _proxy from itertools import repeat as _repeat, chain as _chain, starmap as _starmap ################################################################################ ### OrderedDict ################################################################################ +class _Link(object): + __slots__ = 'prev', 'next', 'key', '__weakref__' + class OrderedDict(dict, MutableMapping): 'Dictionary that remembers insertion order' # An inherited dict maps keys to values. @@ -27,7 +31,7 @@ # The internal self.__map dictionary maps keys to links in a doubly linked list. # The circular doubly linked list starts and ends with a sentinel element. # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. + # The back links are weakref proxies (to prevent circular references). def __init__(self, *args, **kwds): '''Initialize an ordered dictionary. Signature is the same as for @@ -40,51 +44,53 @@ try: self.__root except AttributeError: - self.__root = root = [None, None, None] # sentinel node - PREV = 0 - NEXT = 1 - root[PREV] = root[NEXT] = root + self.__root = root = _Link() # sentinel node for the doubly linked list + root.prev = root.next = root self.__map = {} self.update(*args, **kwds) - def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): + def __setitem__(self, key, value, + dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link): 'od.__setitem__(i, y) <==> od[i]=y' # Setting a new item creates a new link which goes at the end of the linked # list, and the inherited dictionary is updated with the new key/value pair. if key not in self: + self.__map[key] = link = Link() root = self.__root - last = root[PREV] - last[NEXT] = root[PREV] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) + last = root.prev + link.prev, link.next, link.key = last, root, key + last.next = link + root.prev = proxy(link) + dict.__setitem__(self, key, value) - def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__): + def __delitem__(self, key, dict_delitem=dict.__delitem__): 'od.__delitem__(y) <==> del od[y]' # Deleting an existing item uses self.__map to find the link which is # then removed by updating the links in the predecessor and successor nodes. dict_delitem(self, key) link = self.__map.pop(key) - link_prev = link[PREV] - link_next = link[NEXT] - link_prev[NEXT] = link_next - link_next[PREV] = link_prev + link_prev = link.prev + link_next = link.next + link_prev.next = link_next + link_next.prev = link_prev - def __iter__(self, NEXT=1, KEY=2): + def __iter__(self): 'od.__iter__() <==> iter(od)' # Traverse the linked list in order. root = self.__root - curr = root[NEXT] + curr = root.next while curr is not root: - yield curr[KEY] - curr = curr[NEXT] + yield curr.key + curr = curr.next - def __reversed__(self, PREV=0, KEY=2): + def __reversed__(self): 'od.__reversed__() <==> reversed(od)' # Traverse the linked list in reverse order. root = self.__root - curr = root[PREV] + curr = root.prev while curr is not root: - yield curr[KEY] - curr = curr[PREV] + yield curr.key + curr = curr.prev def __reduce__(self): 'Return state information for pickling' @@ -99,16 +105,12 @@ def clear(self): 'od.clear() -> None. Remove all items from od.' - try: - for node in self.__map.values(): - del node[:] - self.__root[:] = [self.__root, self.__root, None] - self.__map.clear() - except AttributeError: - pass + root = self.__root + root.prev = root.next = root + self.__map.clear() dict.clear(self) - def popitem(self, last=True, PREV=0, NEXT=1, KEY=2, dict_pop=dict.pop): + def popitem(self, last=True): '''od.popitem() -> (k, v), return and remove a (key, value) pair. Pairs are returned in LIFO order if last is true or FIFO order if false. @@ -116,21 +118,45 @@ if not self: raise KeyError('dictionary is empty') root = self.__root - if last: # link_prev <--> link <--> root - link = root[PREV] - link_prev = link[PREV] - link_prev[NEXT] = root - root[PREV] = link_prev - else: # root <--> link <--> link_next - link = root[NEXT] - link_next = link[NEXT] - root[NEXT] = link_next - link_next[PREV] = root - key = link[KEY] + if last: + link = root.prev + link_prev = link.prev + link_prev.next = root + root.prev = link_prev + else: + link = root.next + link_next = link.next + root.next = link_next + link_next.prev = root + key = link.key del self.__map[key] - value = dict_pop(self, key) + value = dict.pop(self, key) return key, value + def move_to_end(self, key, last=True): + '''Move an existing element to the end (or beginning if last==False). + + Raises KeyError if the element does not exist. + When last=True, acts like a fast version of self[key]=self.pop(key). + + ''' + link = self.__map[key] + link_prev = link.prev + link_next = link.next + link_prev.next = link_next + link_next.prev = link_prev + root = self.__root + if last: + last = root.prev + link.prev = last + link.next = root + last.next = root.prev = link + else: + first = root.next + link.prev = root + link.next = first + root.next = first.prev = link + setdefault = MutableMapping.setdefault update = MutableMapping.update pop = MutableMapping.pop @@ -170,33 +196,6 @@ all(p==q for p, q in zip(self.items(), other.items())) return dict.__eq__(self, other) - def __del__(self): - self.clear() # eliminate cyclical references - - def move_to_end(self, key, last=True, PREV=0, NEXT=1): - '''Move an existing element to the end (or beginning if last==False). - - Raises KeyError if the element does not exist. - When last=True, acts like a fast version of self[key]=self.pop(key). - - ''' - link = self.__map[key] - link_prev = link[PREV] - link_next = link[NEXT] - link_prev[NEXT] = link_next - link_next[PREV] = link_prev - root = self.__root - if last: - last = root[PREV] - link[PREV] = last - link[NEXT] = root - last[NEXT] = root[PREV] = link - else: - first = root[NEXT] - link[PREV] = root - link[NEXT] = first - root[NEXT] = first[PREV] = link - ################################################################################ ### namedtuple Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 12 06:12:42 2010 @@ -32,6 +32,12 @@ Library ------- +- Issue #9825: Using __del__ in the definition of collections.OrderedDict made + it possible for the user to create self-referencing ordered dictionaries + which become permanently uncollectable GC garbage. Reinstated the Py3.1 + approach of using weakref proxies so that reference cycles never get created + in the first place. + - Issue #9579, #9580: Fix os.confstr() for value longer than 255 bytes and encode the value with filesystem encoding and surrogateescape (instead of utf-8 in strict mode). Patch written by David Watson. From python-checkins at python.org Sun Sep 12 07:15:22 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 12 Sep 2010 07:15:22 +0200 (CEST) Subject: [Python-checkins] r84728 - in python/branches/py3k: Lib/collections.py Lib/test/test_collections.py Misc/NEWS Message-ID: <20100912051522.7D8DAEDAE@mail.python.org> Author: raymond.hettinger Date: Sun Sep 12 07:15:22 2010 New Revision: 84728 Log: Issue #9826: Handle recursive repr in collections.OrderedDict. Modified: python/branches/py3k/Lib/collections.py python/branches/py3k/Lib/test/test_collections.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Sun Sep 12 07:15:22 2010 @@ -41,6 +41,7 @@ ''' if len(args) > 1: raise TypeError('expected at most 1 arguments, got %d' % len(args)) + self.__in_repr = False # detects recursive repr try: self.__root except AttributeError: @@ -95,10 +96,10 @@ def __reduce__(self): 'Return state information for pickling' items = [[k, self[k]] for k in self] - tmp = self.__map, self.__root - del self.__map, self.__root + tmp = self.__map, self.__root, self.__in_repr + del self.__map, self.__root, self.__in_repr inst_dict = vars(self).copy() - self.__map, self.__root = tmp + self.__map, self.__root, self.__in_repr = tmp if inst_dict: return (self.__class__, (items,), inst_dict) return self.__class__, (items,) @@ -167,9 +168,16 @@ def __repr__(self): 'od.__repr__() <==> repr(od)' + if self.__in_repr: + return '...' if not self: return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self.items())) + self.__in_repr = True + try: + result = '%s(%r)' % (self.__class__.__name__, list(self.items())) + finally: + self.__in_repr = False + return result def copy(self): 'od.copy() -> a shallow copy of od' Modified: python/branches/py3k/Lib/test/test_collections.py ============================================================================== --- python/branches/py3k/Lib/test/test_collections.py (original) +++ python/branches/py3k/Lib/test/test_collections.py Sun Sep 12 07:15:22 2010 @@ -951,6 +951,13 @@ self.assertEqual(eval(repr(od)), od) self.assertEqual(repr(OrderedDict()), "OrderedDict()") + def test_repr_recursive(self): + # See issue #9826 + od = OrderedDict.fromkeys('abc') + od['x'] = od + self.assertEqual(repr(od), + "OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])") + def test_setdefault(self): pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] shuffle(pairs) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 12 07:15:22 2010 @@ -32,6 +32,9 @@ Library ------- +- Issue #9826: OrderedDict.__repr__ can now handle self-referential + values: d['x'] = d. + - Issue #9825: Using __del__ in the definition of collections.OrderedDict made it possible for the user to create self-referencing ordered dictionaries which become permanently uncollectable GC garbage. Reinstated the Py3.1 From python-checkins at python.org Sun Sep 12 07:28:42 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 12 Sep 2010 07:28:42 +0200 (CEST) Subject: [Python-checkins] r84729 - in python/branches/release31-maint: Lib/collections.py Lib/test/test_collections.py Misc/NEWS Message-ID: <20100912052842.4791DF9D0@mail.python.org> Author: raymond.hettinger Date: Sun Sep 12 07:28:42 2010 New Revision: 84729 Log: Issue #9826: Handle recursive repr in collections.OrderedDict. Modified: python/branches/release31-maint/Lib/collections.py python/branches/release31-maint/Lib/test/test_collections.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/collections.py ============================================================================== --- python/branches/release31-maint/Lib/collections.py (original) +++ python/branches/release31-maint/Lib/collections.py Sun Sep 12 07:28:42 2010 @@ -43,6 +43,7 @@ ''' if len(args) > 1: raise TypeError('expected at most 1 arguments, got %d' % len(args)) + self.__in_repr = False # detects recursive repr try: self.__root except AttributeError: @@ -100,10 +101,10 @@ def __reduce__(self): 'Return state information for pickling' items = [[k, self[k]] for k in self] - tmp = self.__map, self.__root - del self.__map, self.__root + tmp = self.__map, self.__root, self.__in_repr + del self.__map, self.__root, self.__in_repr inst_dict = vars(self).copy() - self.__map, self.__root = tmp + self.__map, self.__root, self.__in_repr = tmp if inst_dict: return (self.__class__, (items,), inst_dict) return self.__class__, (items,) @@ -128,9 +129,16 @@ def __repr__(self): 'od.__repr__() <==> repr(od)' + if self.__in_repr: + return '...' if not self: return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self.items())) + self.__in_repr = True + try: + result = '%s(%r)' % (self.__class__.__name__, list(self.items())) + finally: + self.__in_repr = False + return result def copy(self): 'od.copy() -> a shallow copy of od' Modified: python/branches/release31-maint/Lib/test/test_collections.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_collections.py (original) +++ python/branches/release31-maint/Lib/test/test_collections.py Sun Sep 12 07:28:42 2010 @@ -908,6 +908,13 @@ self.assertEqual(eval(repr(od)), od) self.assertEqual(repr(OrderedDict()), "OrderedDict()") + def test_repr_recursive(self): + # See issue #9826 + od = OrderedDict.fromkeys('abc') + od['x'] = od + self.assertEqual(repr(od), + "OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])") + def test_setdefault(self): pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] shuffle(pairs) Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Sep 12 07:28:42 2010 @@ -122,6 +122,9 @@ to fetch the original errno, or to filter timeout errors. Now the original error is re-raised. +- Issue #9826: OrderedDict.__repr__ can now handle self-referential + values: d['x'] = d. + - Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True, and the passed buffer was exactly 1024 bytes long, the buffer wouldn't be updated back after the system call. Original patch by Brian Brazil. From python-checkins at python.org Sun Sep 12 09:51:18 2010 From: python-checkins at python.org (victor.stinner) Date: Sun, 12 Sep 2010 09:51:18 +0200 (CEST) Subject: [Python-checkins] r84730 - in python/branches/py3k: Lib/test/test_unicode.py Objects/unicodeobject.c Message-ID: <20100912075118.C7C7CF43F@mail.python.org> Author: victor.stinner Date: Sun Sep 12 09:51:18 2010 New Revision: 84730 Log: Issue #9738, #9836: Fix refleak introduced by r84704 Modified: python/branches/py3k/Lib/test/test_unicode.py python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Lib/test/test_unicode.py ============================================================================== --- python/branches/py3k/Lib/test/test_unicode.py (original) +++ python/branches/py3k/Lib/test/test_unicode.py Sun Sep 12 09:51:18 2010 @@ -1397,7 +1397,7 @@ # non-ascii format, ascii argument self.assertRaisesRegexp(ValueError, '^PyUnicode_FromFormatV\(\) expects an ASCII-encoded format ' - 'string, got a non-ascii byte: 0xe9$', + 'string, got a non-ASCII byte: 0xe9$', format_unicode, b'unicode\xe9=%s', 'ascii') def test_main(): Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Sun Sep 12 09:51:18 2010 @@ -767,9 +767,9 @@ else if (128 <= (unsigned char)*f) { PyErr_Format(PyExc_ValueError, "PyUnicode_FromFormatV() expects an ASCII-encoded format " - "string, got a non-ascii byte: 0x%02x", + "string, got a non-ASCII byte: 0x%02x", (unsigned char)*f); - return NULL; + goto fail; } } /* step 2: allocate memory for the results of From python-checkins at python.org Sun Sep 12 10:00:41 2010 From: python-checkins at python.org (victor.stinner) Date: Sun, 12 Sep 2010 10:00:41 +0200 (CEST) Subject: [Python-checkins] r84731 - python/branches/py3k/Modules/main.c Message-ID: <20100912080041.E8E2EF99B@mail.python.org> Author: victor.stinner Date: Sun Sep 12 10:00:41 2010 New Revision: 84731 Log: Isse #8589: Decode PYTHONWARNINGS from utf-8 on Mac OS X Instead of the locale encoding. Modified: python/branches/py3k/Modules/main.c Modified: python/branches/py3k/Modules/main.c ============================================================================== --- python/branches/py3k/Modules/main.c (original) +++ python/branches/py3k/Modules/main.c Sun Sep 12 10:00:41 2010 @@ -488,7 +488,6 @@ #else if ((p = Py_GETENV("PYTHONWARNINGS")) && *p != '\0') { char *buf, *oldloc; - wchar_t *wchar; PyObject *unicode; /* settle for strtok here as there's no one standard @@ -501,11 +500,16 @@ oldloc = strdup(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, ""); for (p = strtok(buf, ","); p != NULL; p = strtok(NULL, ",")) { - wchar = _Py_char2wchar(p); +#ifdef __APPLE__ + /* Use utf-8 on Mac OS X */ + unicode = PyUnicode_FromString(p); +#else + wchar_t *wchar = _Py_char2wchar(p); if (wchar == NULL) continue; unicode = PyUnicode_FromWideChar(wchar, wcslen(wchar)); PyMem_Free(wchar); +#endif if (unicode == NULL) continue; PySys_AddWarnOptionUnicode(unicode); From python-checkins at python.org Sun Sep 12 13:47:47 2010 From: python-checkins at python.org (vinay.sajip) Date: Sun, 12 Sep 2010 13:47:47 +0200 (CEST) Subject: [Python-checkins] r84732 - python/branches/release31-maint/Doc/library/logging.rst Message-ID: <20100912114747.7A998F343@mail.python.org> Author: vinay.sajip Date: Sun Sep 12 13:47:47 2010 New Revision: 84732 Log: Issue #9827: clarified LogRecord documentation. Modified: python/branches/release31-maint/Doc/library/logging.rst Modified: python/branches/release31-maint/Doc/library/logging.rst ============================================================================== --- python/branches/release31-maint/Doc/library/logging.rst (original) +++ python/branches/release31-maint/Doc/library/logging.rst Sun Sep 12 13:47:47 2010 @@ -540,10 +540,10 @@ In addition to the base :class:`Handler` class, many useful subclasses are provided: -#. :class:`StreamHandler` instances send error messages to streams (file-like +#. :class:`StreamHandler` instances send messages to streams (file-like objects). -#. :class:`FileHandler` instances send error messages to disk files. +#. :class:`FileHandler` instances send messages to disk files. .. module:: logging.handlers @@ -552,31 +552,31 @@ directly. Instead, use :class:`RotatingFileHandler` or :class:`TimedRotatingFileHandler`. -#. :class:`RotatingFileHandler` instances send error messages to disk +#. :class:`RotatingFileHandler` instances send messages to disk files, with support for maximum log file sizes and log file rotation. -#. :class:`TimedRotatingFileHandler` instances send error messages to +#. :class:`TimedRotatingFileHandler` instances send messages to disk files, rotating the log file at certain timed intervals. -#. :class:`SocketHandler` instances send error messages to TCP/IP +#. :class:`SocketHandler` instances send messages to TCP/IP sockets. -#. :class:`DatagramHandler` instances send error messages to UDP +#. :class:`DatagramHandler` instances send messages to UDP sockets. -#. :class:`SMTPHandler` instances send error messages to a designated +#. :class:`SMTPHandler` instances send messages to a designated email address. -#. :class:`SysLogHandler` instances send error messages to a Unix +#. :class:`SysLogHandler` instances send messages to a Unix syslog daemon, possibly on a remote machine. -#. :class:`NTEventLogHandler` instances send error messages to a +#. :class:`NTEventLogHandler` instances send messages to a Windows NT/2000/XP event log. -#. :class:`MemoryHandler` instances send error messages to a buffer +#. :class:`MemoryHandler` instances send messages to a buffer in memory, which is flushed whenever specific criteria are met. -#. :class:`HTTPHandler` instances send error messages to an HTTP +#. :class:`HTTPHandler` instances send messages to an HTTP server using either ``GET`` or ``POST`` semantics. #. :class:`WatchedFileHandler` instances watch the file they are @@ -675,7 +675,7 @@ d = {'clientip': '192.168.0.1', 'user': 'fbloggs'} logging.warning("Protocol problem: %s", "connection reset", extra=d) - would print something like :: + would print something like:: 2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset @@ -842,6 +842,7 @@ and 2.2.x, which do not include the :mod:`logging` package in the standard library. +.. _logger: Logger Objects -------------- @@ -1441,16 +1442,16 @@ threads in a single process *is* supported, logging to a single file from *multiple processes* is *not* supported, because there is no standard way to serialize access to a single file across multiple processes in Python. If you -need to log to a single file from multiple processes, the best way of doing -this is to have all the processes log to a :class:`SocketHandler`, and have a -separate process which implements a socket server which reads from the socket -and logs to file. (If you prefer, you can dedicate one thread in one of the -existing processes to perform this function.) The following section documents -this approach in more detail and includes a working socket receiver which can -be used as a starting point for you to adapt in your own applications. +need to log to a single file from multiple processes, one way of doing this is +to have all the processes log to a :class:`SocketHandler`, and have a separate +process which implements a socket server which reads from the socket and logs +to file. (If you prefer, you can dedicate one thread in one of the existing +processes to perform this function.) The following section documents this +approach in more detail and includes a working socket receiver which can be +used as a starting point for you to adapt in your own applications. If you are using a recent version of Python which includes the -:mod:`multiprocessing` module, you can write your own handler which uses the +:mod:`multiprocessing` module, you could write your own handler which uses the :class:`Lock` class from this module to serialize access to the file from your processes. The existing :class:`FileHandler` and subclasses do not make use of :mod:`multiprocessing` at present, though they may do so in the future. @@ -1594,6 +1595,8 @@ the :meth:`makePickle` method and implementing your alternative there, as well as adapting the above script to use your alternative serialization. +.. _arbitrary-object-messages: + Using arbitrary objects as messages ----------------------------------- @@ -1957,6 +1960,11 @@ The extensions are date-and-time based, using the strftime format ``%Y-%m-%d_%H-%M-%S`` or a leading portion thereof, depending on the rollover interval. + + When computing the next rollover time for the first time (when the handler + is created), the last modification time of an existing log file, or else + the current time, is used to compute when the next rotation will occur. + If the *utc* argument is true, times in UTC will be used; otherwise local time is used. @@ -2452,6 +2460,8 @@ +-------------------------+-----------------------------------------------+ | ``%(process)d`` | Process ID (if available). | +-------------------------+-----------------------------------------------+ +| ``%(processName)s`` | Process name (if available). | ++-------------------------+-----------------------------------------------+ | ``%(message)s`` | The logged message, computed as ``msg % | | | args``. | +-------------------------+-----------------------------------------------+ @@ -2465,7 +2475,6 @@ specified, ``'%(message)s'`` is used. If no *datefmt* is specified, the ISO8601 date format is used. - .. method:: format(record) The record's attribute dictionary is used as the operand to a string From python-checkins at python.org Sun Sep 12 13:51:26 2010 From: python-checkins at python.org (vinay.sajip) Date: Sun, 12 Sep 2010 13:51:26 +0200 (CEST) Subject: [Python-checkins] r84733 - python/branches/release31-maint/Doc/library/logging.rst Message-ID: <20100912115126.920D0F343@mail.python.org> Author: vinay.sajip Date: Sun Sep 12 13:51:26 2010 New Revision: 84733 Log: Issue #9827: clarified LogRecord documentation a little more. Modified: python/branches/release31-maint/Doc/library/logging.rst Modified: python/branches/release31-maint/Doc/library/logging.rst ============================================================================== --- python/branches/release31-maint/Doc/library/logging.rst (original) +++ python/branches/release31-maint/Doc/library/logging.rst Sun Sep 12 13:51:26 2010 @@ -2550,27 +2550,58 @@ LogRecord Objects ----------------- -:class:`LogRecord` instances are created every time something is logged. They -contain all the information pertinent to the event being logged. The main -information passed in is in msg and args, which are combined using msg % args to -create the message field of the record. The record also includes information -such as when the record was created, the source line where the logging call was -made, and any exception information to be logged. +:class:`LogRecord` instances are created automatically by the :class:`Logger` +every time something is logged, and can be created manually via +:func:`makeLogRecord` (for example, from a pickled event received over the +wire). .. class:: LogRecord(name, lvl, pathname, lineno, msg, args, exc_info, func=None) - Returns an instance of :class:`LogRecord` initialized with interesting - information. The *name* is the logger name; *lvl* is the numeric level; - *pathname* is the absolute pathname of the source file in which the logging - call was made; *lineno* is the line number in that file where the logging - call is found; *msg* is the user-supplied message (a format string); *args* - is the tuple which, together with *msg*, makes up the user message; and - *exc_info* is the exception tuple obtained by calling :func:`sys.exc_info` - (or :const:`None`, if no exception information is available). The *func* is - the name of the function from which the logging call was made. If not - specified, it defaults to ``None``. + Contains all the information pertinent to the event being logged. + The primary information is passed in :attr:`msg` and :attr:`args`, which + are combined using ``msg % args`` to create the :attr:`message` field of the + record. + + .. attribute:: args + + Tuple of arguments to be used in formatting :attr:`msg`. + + .. attribute:: exc_info + + Exception tuple (? la `sys.exc_info`) or `None` if no exception + information is availble. + + .. attribute:: func + + Name of the function of origin (i.e. in which the logging call was made). + + .. attribute:: lineno + + Line number in the source file of origin. + + .. attribute:: lvl + + Numeric logging level. + + .. attribute:: message + + Bound to the result of :meth:`getMessage` when + :meth:`Formatter.format(record)` is invoked. + + .. attribute:: msg + + User-supplied :ref:`format string` or arbitrary object + (see :ref:`arbitrary-object-messages`) used in :meth:`getMessage`. + + .. attribute:: name + + Name of the logger that emitted the record. + + .. attribute:: pathname + + Absolute pathname of the source file of origin. .. method:: getMessage() From python-checkins at python.org Sun Sep 12 15:18:01 2010 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 12 Sep 2010 15:18:01 +0200 (CEST) Subject: [Python-checkins] r84734 - in tracker/instances: jython/detectors/patches.py python-dev/detectors/patches.py security/detectors/patches.py Message-ID: <20100912131801.BAFAAC3C7@mail.python.org> Author: martin.v.loewis Date: Sun Sep 12 15:18:01 2010 New Revision: 84734 Log: Issue #353: Don't add patch keyword to new issue if it's already there. Modified: tracker/instances/jython/detectors/patches.py tracker/instances/python-dev/detectors/patches.py tracker/instances/security/detectors/patches.py Modified: tracker/instances/jython/detectors/patches.py ============================================================================== --- tracker/instances/jython/detectors/patches.py (original) +++ tracker/instances/jython/detectors/patches.py Sun Sep 12 15:18:01 2010 @@ -37,7 +37,8 @@ return if not newvalues.has_key('keywords'): newvalues['keywords'] = oldkeywords - newvalues['keywords'].append(patchid) + if patchid not in newvalues['keywords']: + newvalues['keywords'].append(patchid) def init(db): db.file.audit('create', patches_text_plain) Modified: tracker/instances/python-dev/detectors/patches.py ============================================================================== --- tracker/instances/python-dev/detectors/patches.py (original) +++ tracker/instances/python-dev/detectors/patches.py Sun Sep 12 15:18:01 2010 @@ -37,7 +37,8 @@ return if not newvalues.has_key('keywords'): newvalues['keywords'] = oldkeywords - newvalues['keywords'].append(patchid) + if patchid not in newvalues['keywords']: + newvalues['keywords'].append(patchid) def init(db): db.file.audit('create', patches_text_plain) Modified: tracker/instances/security/detectors/patches.py ============================================================================== --- tracker/instances/security/detectors/patches.py (original) +++ tracker/instances/security/detectors/patches.py Sun Sep 12 15:18:01 2010 @@ -37,7 +37,8 @@ return if not newvalues.has_key('keywords'): newvalues['keywords'] = oldkeywords - newvalues['keywords'].append(patchid) + if patchid not in newvalues['keywords']: + newvalues['keywords'].append(patchid) def init(db): db.file.audit('create', patches_text_plain) From python-checkins at python.org Sun Sep 12 15:55:02 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 12 Sep 2010 15:55:02 +0200 (CEST) Subject: [Python-checkins] r84735 - python/branches/py3k/Lib/test/test_nis.py Message-ID: <20100912135502.BF17BEBDE@mail.python.org> Author: benjamin.peterson Date: Sun Sep 12 15:55:02 2010 New Revision: 84735 Log: reenable test_nis on solaris #3402 Modified: python/branches/py3k/Lib/test/test_nis.py Modified: python/branches/py3k/Lib/test/test_nis.py ============================================================================== --- python/branches/py3k/Lib/test/test_nis.py (original) +++ python/branches/py3k/Lib/test/test_nis.py Sun Sep 12 15:55:02 2010 @@ -5,8 +5,6 @@ # Skip test if nis module does not exist. nis = support.import_module('nis') -if sys.platform.startswith("sunos"): - raise unittest.SkipTest("test_nis hangs on Solaris") class NisTests(unittest.TestCase): def test_maps(self): From python-checkins at python.org Sun Sep 12 15:58:40 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 12 Sep 2010 15:58:40 +0200 (CEST) Subject: [Python-checkins] r84736 - in python/branches/release31-maint: Lib/test/test_nis.py Message-ID: <20100912135840.2C1EDF045@mail.python.org> Author: benjamin.peterson Date: Sun Sep 12 15:58:34 2010 New Revision: 84736 Log: Merged revisions 84735 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84735 | benjamin.peterson | 2010-09-12 08:55:02 -0500 (Sun, 12 Sep 2010) | 1 line reenable test_nis on solaris #3402 ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_nis.py Modified: python/branches/release31-maint/Lib/test/test_nis.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_nis.py (original) +++ python/branches/release31-maint/Lib/test/test_nis.py Sun Sep 12 15:58:34 2010 @@ -4,7 +4,6 @@ # Skip test if nis module does not exist. nis = support.import_module('nis') -raise unittest.SkipTest("test_nis hangs on Solaris") class NisTests(unittest.TestCase): def test_maps(self): From python-checkins at python.org Sun Sep 12 16:51:21 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 12 Sep 2010 16:51:21 +0200 (CEST) Subject: [Python-checkins] r84737 - in python/branches/py3k: Lib/test/test_zipfile.py Lib/zipfile.py Misc/NEWS Message-ID: <20100912145121.18AF1EDAE@mail.python.org> Author: antoine.pitrou Date: Sun Sep 12 16:51:20 2010 New Revision: 84737 Log: Issue #9837: The read() method of ZipExtFile objects (as returned by ZipFile.open()) could return more bytes than requested. Modified: python/branches/py3k/Lib/test/test_zipfile.py python/branches/py3k/Lib/zipfile.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/test/test_zipfile.py ============================================================================== --- python/branches/py3k/Lib/test/test_zipfile.py (original) +++ python/branches/py3k/Lib/test/test_zipfile.py Sun Sep 12 16:51:20 2010 @@ -939,6 +939,26 @@ def test_read_with_bad_crc_deflated(self): self.check_read_with_bad_crc(zipfile.ZIP_DEFLATED) + def check_read_return_size(self, compression): + # Issue #9837: ZipExtFile.read() shouldn't return more bytes + # than requested. + for test_size in (1, 4095, 4096, 4097, 16384): + file_size = test_size + 1 + junk = b''.join(struct.pack('B', randint(0, 255)) + for x in range(file_size)) + with zipfile.ZipFile(io.BytesIO(), "w", compression) as zipf: + zipf.writestr('foo', junk) + with zipf.open('foo', 'r') as fp: + buf = fp.read(test_size) + self.assertEqual(len(buf), test_size) + + def test_read_return_size_stored(self): + self.check_read_return_size(zipfile.ZIP_STORED) + + @skipUnless(zlib, "requires zlib") + def test_read_return_size_deflated(self): + self.check_read_return_size(zipfile.ZIP_DEFLATED) + def tearDown(self): unlink(TESTFN) unlink(TESTFN2) Modified: python/branches/py3k/Lib/zipfile.py ============================================================================== --- python/branches/py3k/Lib/zipfile.py (original) +++ python/branches/py3k/Lib/zipfile.py Sun Sep 12 16:51:20 2010 @@ -564,17 +564,20 @@ """Read and return up to n bytes. If the argument is omitted, None, or negative, data is read and returned until EOF is reached.. """ - buf = b'' - while n < 0 or n is None or n > len(buf): - data = self.read1(n) + if n is None: + n = -1 + while True: + if n < 0: + data = self.read1(n) + elif n > len(buf): + data = self.read1(n - len(buf)) + else: + return buf if len(data) == 0: return buf - buf += data - return buf - def _update_crc(self, newdata, eof): # Update the CRC using the given data. if self._expected_crc is None: Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 12 16:51:20 2010 @@ -32,6 +32,9 @@ Library ------- +- Issue #9837: The read() method of ZipExtFile objects (as returned by + ZipFile.open()) could return more bytes than requested. + - Issue #9826: OrderedDict.__repr__ can now handle self-referential values: d['x'] = d. From python-checkins at python.org Sun Sep 12 16:55:22 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 12 Sep 2010 16:55:22 +0200 (CEST) Subject: [Python-checkins] r84738 - in python/branches/release31-maint: Lib/test/test_zipfile.py Message-ID: <20100912145522.5F8CEF275@mail.python.org> Author: antoine.pitrou Date: Sun Sep 12 16:55:22 2010 New Revision: 84738 Log: Merged revisions 84737 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k Only the tests are merged, 3.1 doesn't exhibit the issue. ........ r84737 | antoine.pitrou | 2010-09-12 16:51:20 +0200 (dim., 12 sept. 2010) | 4 lines Issue #9837: The read() method of ZipExtFile objects (as returned by ZipFile.open()) could return more bytes than requested. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_zipfile.py Modified: python/branches/release31-maint/Lib/test/test_zipfile.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_zipfile.py (original) +++ python/branches/release31-maint/Lib/test/test_zipfile.py Sun Sep 12 16:55:22 2010 @@ -879,6 +879,29 @@ def test_read_with_bad_crc_deflated(self): self.check_read_with_bad_crc(zipfile.ZIP_DEFLATED) + def check_read_return_size(self, compression): + # Issue #9837: ZipExtFile.read() shouldn't return more bytes + # than requested. + for test_size in (1, 4095, 4096, 4097, 16384): + file_size = test_size + 1 + junk = b''.join(struct.pack('B', randint(0, 255)) + for x in range(file_size)) + zipf = zipfile.ZipFile(io.BytesIO(), "w", compression) + try: + zipf.writestr('foo', junk) + fp = zipf.open('foo', 'r') + buf = fp.read(test_size) + self.assertEqual(len(buf), test_size) + finally: + zipf.close() + + def test_read_return_size_stored(self): + self.check_read_return_size(zipfile.ZIP_STORED) + + if zlib: + def test_read_return_size_deflated(self): + self.check_read_return_size(zipfile.ZIP_DEFLATED) + def tearDown(self): support.unlink(TESTFN) support.unlink(TESTFN2) From python-checkins at python.org Sun Sep 12 16:56:27 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 12 Sep 2010 16:56:27 +0200 (CEST) Subject: [Python-checkins] r84739 - in python/branches/release27-maint: Lib/test/test_zipfile.py Lib/zipfile.py Misc/NEWS Message-ID: <20100912145627.A85A4DD6C@mail.python.org> Author: antoine.pitrou Date: Sun Sep 12 16:56:27 2010 New Revision: 84739 Log: Merged revisions 84737 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84737 | antoine.pitrou | 2010-09-12 16:51:20 +0200 (dim., 12 sept. 2010) | 4 lines Issue #9837: The read() method of ZipExtFile objects (as returned by ZipFile.open()) could return more bytes than requested. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_zipfile.py python/branches/release27-maint/Lib/zipfile.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/test/test_zipfile.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_zipfile.py (original) +++ python/branches/release27-maint/Lib/test/test_zipfile.py Sun Sep 12 16:56:27 2010 @@ -929,6 +929,26 @@ def test_read_with_bad_crc_deflated(self): self.check_read_with_bad_crc(zipfile.ZIP_DEFLATED) + def check_read_return_size(self, compression): + # Issue #9837: ZipExtFile.read() shouldn't return more bytes + # than requested. + for test_size in (1, 4095, 4096, 4097, 16384): + file_size = test_size + 1 + junk = b''.join(struct.pack('B', randint(0, 255)) + for x in range(file_size)) + with zipfile.ZipFile(io.BytesIO(), "w", compression) as zipf: + zipf.writestr('foo', junk) + with zipf.open('foo', 'r') as fp: + buf = fp.read(test_size) + self.assertEqual(len(buf), test_size) + + def test_read_return_size_stored(self): + self.check_read_return_size(zipfile.ZIP_STORED) + + @skipUnless(zlib, "requires zlib") + def test_read_return_size_deflated(self): + self.check_read_return_size(zipfile.ZIP_DEFLATED) + def tearDown(self): unlink(TESTFN) unlink(TESTFN2) Modified: python/branches/release27-maint/Lib/zipfile.py ============================================================================== --- python/branches/release27-maint/Lib/zipfile.py (original) +++ python/branches/release27-maint/Lib/zipfile.py Sun Sep 12 16:56:27 2010 @@ -565,17 +565,20 @@ """Read and return up to n bytes. If the argument is omitted, None, or negative, data is read and returned until EOF is reached.. """ - buf = '' - while n < 0 or n is None or n > len(buf): - data = self.read1(n) + if n is None: + n = -1 + while True: + if n < 0: + data = self.read1(n) + elif n > len(buf): + data = self.read1(n - len(buf)) + else: + return buf if len(data) == 0: return buf - buf += data - return buf - def _update_crc(self, newdata, eof): # Update the CRC using the given data. if self._expected_crc is None: Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Sep 12 16:56:27 2010 @@ -43,6 +43,9 @@ Library ------- +- Issue #9837: The read() method of ZipExtFile objects (as returned by + ZipFile.open()) could return more bytes than requested. + - Issue #9825: removed __del__ from the definition of collections.OrderedDict. This prevents user-created self-referencing ordered dictionaries from becoming permanently uncollectable GC garbage. The downside is that From python-checkins at python.org Sun Sep 12 16:57:22 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 12 Sep 2010 16:57:22 +0200 (CEST) Subject: [Python-checkins] r84740 - python/branches/release27-maint/Modules Message-ID: <20100912145722.BAA5EE381@mail.python.org> Author: antoine.pitrou Date: Sun Sep 12 16:57:22 2010 New Revision: 84740 Log: Add Modules/ld_so_aix to svn:ignore Modified: python/branches/release27-maint/Modules/ (props changed) From python-checkins at python.org Sun Sep 12 18:06:18 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sun, 12 Sep 2010 18:06:18 +0200 (CEST) Subject: [Python-checkins] r84741 - python/branches/py3k/Modules/_ctypes/_ctypes.c Message-ID: <20100912160618.62E18F860@mail.python.org> Author: hirokazu.yamamoto Date: Sun Sep 12 18:06:18 2010 New Revision: 84741 Log: Fixed refcount bug. I placed Py_INCREF in create_comerror() for compatibility with Python2.7. Modified: python/branches/py3k/Modules/_ctypes/_ctypes.c Modified: python/branches/py3k/Modules/_ctypes/_ctypes.c ============================================================================== --- python/branches/py3k/Modules/_ctypes/_ctypes.c (original) +++ python/branches/py3k/Modules/_ctypes/_ctypes.c Sun Sep 12 18:06:18 2010 @@ -5120,6 +5120,7 @@ PyComError_Type.tp_base = (PyTypeObject*)PyExc_Exception; if (PyType_Ready(&PyComError_Type) < 0) return -1; + Py_INCREF(&PyComError_Type); ComError = (PyObject*)&PyComError_Type; return 0; } From python-checkins at python.org Sun Sep 12 18:19:05 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sun, 12 Sep 2010 18:19:05 +0200 (CEST) Subject: [Python-checkins] r84742 - in python/branches/release31-maint: Modules/_ctypes/_ctypes.c Message-ID: <20100912161905.84945FA7A@mail.python.org> Author: hirokazu.yamamoto Date: Sun Sep 12 18:19:05 2010 New Revision: 84742 Log: Merged revisions 83841,84741 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83841 | thomas.heller | 2010-08-09 03:16:20 +0900 | 2 lines Fix issue6869: refcount problem in the _ctypes extension. ........ r84741 | hirokazu.yamamoto | 2010-09-13 01:06:18 +0900 | 2 lines Fixed refcount bug. I placed Py_INCREF in create_comerror() for compatibility with Python2.7. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Modules/_ctypes/_ctypes.c Modified: python/branches/release31-maint/Modules/_ctypes/_ctypes.c ============================================================================== --- python/branches/release31-maint/Modules/_ctypes/_ctypes.c (original) +++ python/branches/release31-maint/Modules/_ctypes/_ctypes.c Sun Sep 12 18:19:05 2010 @@ -5134,6 +5134,7 @@ PyComError_Type.tp_base = (PyTypeObject*)PyExc_Exception; if (PyType_Ready(&PyComError_Type) < 0) return -1; + Py_INCREF(&PyComError_Type); ComError = (PyObject*)&PyComError_Type; return 0; } @@ -5326,36 +5327,42 @@ Struct_Type.tp_base = &PyCData_Type; if (PyType_Ready(&Struct_Type) < 0) return NULL; + Py_INCREF(&Struct_Type); PyModule_AddObject(m, "Structure", (PyObject *)&Struct_Type); Py_TYPE(&Union_Type) = &UnionType_Type; Union_Type.tp_base = &PyCData_Type; if (PyType_Ready(&Union_Type) < 0) return NULL; + Py_INCREF(&Union_Type); PyModule_AddObject(m, "Union", (PyObject *)&Union_Type); Py_TYPE(&PyCPointer_Type) = &PyCPointerType_Type; PyCPointer_Type.tp_base = &PyCData_Type; if (PyType_Ready(&PyCPointer_Type) < 0) return NULL; + Py_INCREF(&PyCPointer_Type); PyModule_AddObject(m, "_Pointer", (PyObject *)&PyCPointer_Type); Py_TYPE(&PyCArray_Type) = &PyCArrayType_Type; PyCArray_Type.tp_base = &PyCData_Type; if (PyType_Ready(&PyCArray_Type) < 0) return NULL; + Py_INCREF(&PyCArray_Type); PyModule_AddObject(m, "Array", (PyObject *)&PyCArray_Type); Py_TYPE(&Simple_Type) = &PyCSimpleType_Type; Simple_Type.tp_base = &PyCData_Type; if (PyType_Ready(&Simple_Type) < 0) return NULL; + Py_INCREF(&Simple_Type); PyModule_AddObject(m, "_SimpleCData", (PyObject *)&Simple_Type); Py_TYPE(&PyCFuncPtr_Type) = &PyCFuncPtrType_Type; PyCFuncPtr_Type.tp_base = &PyCData_Type; if (PyType_Ready(&PyCFuncPtr_Type) < 0) return NULL; + Py_INCREF(&PyCFuncPtr_Type); PyModule_AddObject(m, "CFuncPtr", (PyObject *)&PyCFuncPtr_Type); /************************************************* From python-checkins at python.org Sun Sep 12 18:31:58 2010 From: python-checkins at python.org (matthias.klose) Date: Sun, 12 Sep 2010 18:31:58 +0200 (CEST) Subject: [Python-checkins] r84743 - in python/branches/py3k: Doc/license.rst Misc/NEWS Modules/expat/COPYING Message-ID: <20100912163158.7902EF11D@mail.python.org> Author: matthias.klose Date: Sun Sep 12 18:31:58 2010 New Revision: 84743 Log: - Issue #9817: Add expat COPYING file; add expat, libffi and expat licenses to Doc/license.rst. Added: python/branches/py3k/Modules/expat/COPYING Modified: python/branches/py3k/Doc/license.rst python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/license.rst ============================================================================== --- python/branches/py3k/Doc/license.rst (original) +++ python/branches/py3k/Doc/license.rst Sun Sep 12 18:31:58 2010 @@ -704,3 +704,92 @@ ***************************************************************/ +expat +----- + +The :mod:`pyexpat` extension is built using an included copy of the expat +sources unless the build is configured :option:`--with-system-expat`:: + + Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + and Clark Cooper + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +libffi +------ + +The :mod:`_ctypes` extension is built using an included copy of the libffi +sources unless the build is configured :option:`--with-system-libffi`:: + + Copyright (c) 1996-2008 Red Hat, Inc and others. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + +zlib +---- + +The :mod:`zlib` extension is built using an included copy of the zlib +sources unless the zlib version found on the system is too old to be +used for the build:: + + Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup at gzip.org madler at alumni.caltech.edu + + Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 12 18:31:58 2010 @@ -2280,6 +2280,9 @@ Documentation ------------- +- Issue #9817: Add expat COPYING file; add expat, libffi and expat licenses + to Doc/license.rst. + - Issue #9524: Document that two CTRL* signals are meant for use only with os.kill. Added: python/branches/py3k/Modules/expat/COPYING ============================================================================== --- (empty file) +++ python/branches/py3k/Modules/expat/COPYING Sun Sep 12 18:31:58 2010 @@ -0,0 +1,21 @@ +Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + and Clark Cooper + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From python-checkins at python.org Sun Sep 12 18:40:53 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 12 Sep 2010 18:40:53 +0200 (CEST) Subject: [Python-checkins] r84744 - python/branches/py3k/Objects/unicodeobject.c Message-ID: <20100912164053.CB6F9EBDE@mail.python.org> Author: benjamin.peterson Date: Sun Sep 12 18:40:53 2010 New Revision: 84744 Log: use return NULL; it's just as correct Modified: python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Sun Sep 12 18:40:53 2010 @@ -769,7 +769,7 @@ "PyUnicode_FromFormatV() expects an ASCII-encoded format " "string, got a non-ASCII byte: 0x%02x", (unsigned char)*f); - goto fail; + return NULL; } } /* step 2: allocate memory for the results of From python-checkins at python.org Sun Sep 12 18:50:20 2010 From: python-checkins at python.org (matthias.klose) Date: Sun, 12 Sep 2010 18:50:20 +0200 (CEST) Subject: [Python-checkins] r84745 - in python/branches/release31-maint: Doc/license.rst Misc/NEWS Modules/expat/COPYING Message-ID: <20100912165020.C37EAEBDE@mail.python.org> Author: matthias.klose Date: Sun Sep 12 18:50:20 2010 New Revision: 84745 Log: Merged revisions 84743 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84743 | matthias.klose | 2010-09-12 18:31:58 +0200 (So, 12 Sep 2010) | 3 lines - Issue #9817: Add expat COPYING file; add expat, libffi and expat licenses to Doc/license.rst. ........ Added: python/branches/release31-maint/Modules/expat/COPYING - copied unchanged from r84743, /python/branches/py3k/Modules/expat/COPYING Modified: python/branches/release31-maint/Doc/license.rst python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Doc/license.rst ============================================================================== --- python/branches/release31-maint/Doc/license.rst (original) +++ python/branches/release31-maint/Doc/license.rst Sun Sep 12 18:50:20 2010 @@ -701,3 +701,92 @@ ***************************************************************/ +expat +----- + +The :mod:`pyexpat` extension is built using an included copy of the expat +sources unless the build is configured :option:`--with-system-expat`:: + + Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + and Clark Cooper + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +libffi +------ + +The :mod:`_ctypes` extension is built using an included copy of the libffi +sources unless the build is configured :option:`--with-system-libffi`:: + + Copyright (c) 1996-2008 Red Hat, Inc and others. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + +zlib +---- + +The :mod:`zlib` extension is built using an included copy of the zlib +sources unless the zlib version found on the system is too old to be +used for the build:: + + Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup at gzip.org madler at alumni.caltech.edu + + Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Sep 12 18:50:20 2010 @@ -622,6 +622,9 @@ Documentation ------------- +- Issue #9817: Add expat COPYING file; add expat, libffi and expat licenses + to Doc/license.rst. + - Issue #9255: Document that the 'test' package is meant for interal Python use only. From python-checkins at python.org Sun Sep 12 18:51:29 2010 From: python-checkins at python.org (matthias.klose) Date: Sun, 12 Sep 2010 18:51:29 +0200 (CEST) Subject: [Python-checkins] r84746 - in python/branches/release27-maint: Doc/license.rst Misc/NEWS Modules/expat/COPYING Message-ID: <20100912165129.B53F7F9D0@mail.python.org> Author: matthias.klose Date: Sun Sep 12 18:51:29 2010 New Revision: 84746 Log: Merged revisions 84743 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84743 | matthias.klose | 2010-09-12 18:31:58 +0200 (So, 12 Sep 2010) | 3 lines - Issue #9817: Add expat COPYING file; add expat, libffi and expat licenses to Doc/license.rst. ........ Added: python/branches/release27-maint/Modules/expat/COPYING - copied unchanged from r84743, /python/branches/py3k/Modules/expat/COPYING Modified: python/branches/release27-maint/Doc/license.rst python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Doc/license.rst ============================================================================== --- python/branches/release27-maint/Doc/license.rst (original) +++ python/branches/release27-maint/Doc/license.rst Sun Sep 12 18:51:29 2010 @@ -748,3 +748,92 @@ ***************************************************************/ +expat +----- + +The :mod:`pyexpat` extension is built using an included copy of the expat +sources unless the build is configured :option:`--with-system-expat`:: + + Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + and Clark Cooper + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +libffi +------ + +The :mod:`_ctypes` extension is built using an included copy of the libffi +sources unless the build is configured :option:`--with-system-libffi`:: + + Copyright (c) 1996-2008 Red Hat, Inc and others. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + +zlib +---- + +The :mod:`zlib` extension is built using an included copy of the zlib +sources unless the zlib version found on the system is too old to be +used for the build:: + + Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup at gzip.org madler at alumni.caltech.edu + + Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Sep 12 18:51:29 2010 @@ -351,6 +351,9 @@ Documentation ------------- +- Issue #9817: Add expat COPYING file; add expat, libffi and expat licenses + to Doc/license.rst. + - Issue #9524: Document that two CTRL* signals are meant for use only with os.kill. From python-checkins at python.org Sun Sep 12 19:14:26 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 12 Sep 2010 19:14:26 +0200 (CEST) Subject: [Python-checkins] r84747 - in python/branches/py3k/Misc: NEWS python-config.in Message-ID: <20100912171426.CFAABD9B7@mail.python.org> Author: georg.brandl Date: Sun Sep 12 19:14:26 2010 New Revision: 84747 Log: 9806: add --extension-suffix option to python-config. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Misc/python-config.in Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 12 19:14:26 2010 @@ -137,6 +137,10 @@ Build ----- +- Issue #9806: python-config now has an ``--extension-suffix`` option that + outputs the suffix for dynamic libraries including the ABI version name + defined by PEP 3149. + - Issue #941346: Improve the build process under AIX and allow Python to be built as a shared library. Patch by S?bastien Sabl?. Modified: python/branches/py3k/Misc/python-config.in ============================================================================== --- python/branches/py3k/Misc/python-config.in (original) +++ python/branches/py3k/Misc/python-config.in Sun Sep 12 19:14:26 2010 @@ -6,7 +6,7 @@ import sysconfig valid_opts = ['prefix', 'exec-prefix', 'includes', 'libs', 'cflags', - 'ldflags', 'help'] + 'ldflags', 'extension-suffix', 'help'] def exit_with_usage(code=1): print("Usage: {0} [{1}]".format( @@ -54,3 +54,5 @@ libs.extend(getvar('LINKFORSHARED').split()) print(' '.join(libs)) + elif opt == '--extension-suffix': + print(sysconfig.get_config_var('SO')) From python-checkins at python.org Sun Sep 12 20:13:47 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 12 Sep 2010 20:13:47 +0200 (CEST) Subject: [Python-checkins] r84748 - python/branches/py3k/Lib/collections.py Message-ID: <20100912181347.03FB2E381@mail.python.org> Author: raymond.hettinger Date: Sun Sep 12 20:13:46 2010 New Revision: 84748 Log: Use weakrefs for both forward and backward links. Modified: python/branches/py3k/Lib/collections.py Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Sun Sep 12 20:13:46 2010 @@ -31,7 +31,9 @@ # The internal self.__map dictionary maps keys to links in a doubly linked list. # The circular doubly linked list starts and ends with a sentinel element. # The sentinel element never gets deleted (this simplifies the algorithm). - # The back links are weakref proxies (to prevent circular references). + # The prev/next links are weakref proxies (to prevent circular references). + # Individual links are kept alive by the hard reference in self.__map. + # Those hard references disappear when a key is deleted from an OrderedDict. def __init__(self, *args, **kwds): '''Initialize an ordered dictionary. Signature is the same as for @@ -60,8 +62,7 @@ root = self.__root last = root.prev link.prev, link.next, link.key = last, root, key - last.next = link - root.prev = proxy(link) + last.next = root.prev = proxy(link) dict.__setitem__(self, key, value) def __delitem__(self, key, dict_delitem=dict.__delitem__): From python-checkins at python.org Sun Sep 12 20:16:01 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 12 Sep 2010 20:16:01 +0200 (CEST) Subject: [Python-checkins] r84749 - python/branches/py3k/Lib/collections.py Message-ID: <20100912181601.413F0F09D@mail.python.org> Author: raymond.hettinger Date: Sun Sep 12 20:16:01 2010 New Revision: 84749 Log: Put tests in more logical order. Modified: python/branches/py3k/Lib/collections.py Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Sun Sep 12 20:16:01 2010 @@ -169,10 +169,10 @@ def __repr__(self): 'od.__repr__() <==> repr(od)' - if self.__in_repr: - return '...' if not self: return '%s()' % (self.__class__.__name__,) + if self.__in_repr: + return '...' self.__in_repr = True try: result = '%s(%r)' % (self.__class__.__name__, list(self.items())) From python-checkins at python.org Sun Sep 12 22:32:57 2010 From: python-checkins at python.org (victor.stinner) Date: Sun, 12 Sep 2010 22:32:57 +0200 (CEST) Subject: [Python-checkins] r84750 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100912203257.3E7CCEE981@mail.python.org> Author: victor.stinner Date: Sun Sep 12 22:32:57 2010 New Revision: 84750 Log: Document changes on mbcs encoding Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Sep 12 22:32:57 2010 @@ -544,3 +544,10 @@ opaque C pointers in Python objects, the :ctype:`PyCapsule` API should be used instead; the new type has a well-defined interface for passing typing safety information and a less complicated signature for calling a destructor. + +* mbcs encoding doesn't ignore the error handler argument anymore. By default + (strict mode), it raises an UnicodeDecodeError on undecodable byte sequence + and UnicodeEncodeError on unencodable character. To get the mbcs encoding of + Python 3.1, use ``'ignore'`` error handler to decode and ``'replace'`` error + handler to encode. mbcs now supports ``'strict'`` and ``'ignore'`` error + handlers for decoding, and ``'strict'`` and ``'replace'`` for encoding. From python-checkins at python.org Sun Sep 12 22:58:19 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 12 Sep 2010 22:58:19 +0200 (CEST) Subject: [Python-checkins] r84751 - python/branches/py3k/Misc/python-config.in Message-ID: <20100912205819.CF49EF0F1@mail.python.org> Author: georg.brandl Date: Sun Sep 12 22:58:19 2010 New Revision: 84751 Log: Get rid of tabs. Modified: python/branches/py3k/Misc/python-config.in Modified: python/branches/py3k/Misc/python-config.in ============================================================================== --- python/branches/py3k/Misc/python-config.in (original) +++ python/branches/py3k/Misc/python-config.in Sun Sep 12 22:58:19 2010 @@ -55,4 +55,4 @@ print(' '.join(libs)) elif opt == '--extension-suffix': - print(sysconfig.get_config_var('SO')) + print(sysconfig.get_config_var('SO')) From python-checkins at python.org Mon Sep 13 00:42:57 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Mon, 13 Sep 2010 00:42:57 +0200 (CEST) Subject: [Python-checkins] r84752 - in python/branches/py3k: Doc/whatsnew/3.2.rst Include/unicodeobject.h Misc/NEWS Objects/unicodectype.c Objects/unicodetype_db.h Tools/unicode/makeunicodedata.py configure configure.in pyconfig.h.in Message-ID: <20100912224257.E3472F4F4@mail.python.org> Author: amaury.forgeotdarc Date: Mon Sep 13 00:42:57 2010 New Revision: 84752 Log: #9210: remove --with-wctype-functions configure option. The internal unicode database is now always used. (after 5 years: see http://mail.python.org/pipermail/python-dev/2004-December/050193.html ) Modified: python/branches/py3k/Doc/whatsnew/3.2.rst python/branches/py3k/Include/unicodeobject.h python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/unicodectype.c python/branches/py3k/Objects/unicodetype_db.h python/branches/py3k/Tools/unicode/makeunicodedata.py python/branches/py3k/configure python/branches/py3k/configure.in python/branches/py3k/pyconfig.h.in Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Mon Sep 13 00:42:57 2010 @@ -525,6 +525,11 @@ (Contributed by Antoine Pitrou; :issue:`9203`.) +* The option ``--with-wctype-functions`` was removed. The built-in unicode + database is now used for all functions. + + (Contributed by Amaury Forgeot D'Arc; :issue:`9210`.) + Porting to Python 3.2 ===================== Modified: python/branches/py3k/Include/unicodeobject.h ============================================================================== --- python/branches/py3k/Include/unicodeobject.h (original) +++ python/branches/py3k/Include/unicodeobject.h Mon Sep 13 00:42:57 2010 @@ -78,7 +78,7 @@ #define Py_UNICODE_WIDE #endif -/* Set these flags if the platform has "wchar.h", "wctype.h" and the +/* Set these flags if the platform has "wchar.h" and the wchar_t type is a 16-bit unsigned type */ /* #define HAVE_WCHAR_H */ /* #define HAVE_USABLE_WCHAR_T */ @@ -309,39 +309,6 @@ /* --- Internal Unicode Operations ---------------------------------------- */ -/* If you want Python to use the compiler's wctype.h functions instead - of the ones supplied with Python, define WANT_WCTYPE_FUNCTIONS or - configure Python using --with-wctype-functions. This reduces the - interpreter's code size. */ - -#if defined(Py_UNICODE_WIDE) && defined(HAVE_USABLE_WCHAR_T) && defined(WANT_WCTYPE_FUNCTIONS) - -#include - -#define Py_UNICODE_ISSPACE(ch) iswspace(ch) - -#define Py_UNICODE_ISLOWER(ch) iswlower(ch) -#define Py_UNICODE_ISUPPER(ch) iswupper(ch) -#define Py_UNICODE_ISTITLE(ch) _PyUnicode_IsTitlecase(ch) -#define Py_UNICODE_ISLINEBREAK(ch) _PyUnicode_IsLinebreak(ch) - -#define Py_UNICODE_TOLOWER(ch) towlower(ch) -#define Py_UNICODE_TOUPPER(ch) towupper(ch) -#define Py_UNICODE_TOTITLE(ch) _PyUnicode_ToTitlecase(ch) - -#define Py_UNICODE_ISDECIMAL(ch) _PyUnicode_IsDecimalDigit(ch) -#define Py_UNICODE_ISDIGIT(ch) _PyUnicode_IsDigit(ch) -#define Py_UNICODE_ISNUMERIC(ch) _PyUnicode_IsNumeric(ch) -#define Py_UNICODE_ISPRINTABLE(ch) _PyUnicode_IsPrintable(ch) - -#define Py_UNICODE_TODECIMAL(ch) _PyUnicode_ToDecimalDigit(ch) -#define Py_UNICODE_TODIGIT(ch) _PyUnicode_ToDigit(ch) -#define Py_UNICODE_TONUMERIC(ch) _PyUnicode_ToNumeric(ch) - -#define Py_UNICODE_ISALPHA(ch) iswalpha(ch) - -#else - /* Since splitting on whitespace is an important use case, and whitespace in most situations is solely ASCII whitespace, we optimize for the common case by using a quick look-up table @@ -371,8 +338,6 @@ #define Py_UNICODE_ISALPHA(ch) _PyUnicode_IsAlpha(ch) -#endif - #define Py_UNICODE_ISALNUM(ch) \ (Py_UNICODE_ISALPHA(ch) || \ Py_UNICODE_ISDECIMAL(ch) || \ Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 13 00:42:57 2010 @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #9210: Configure option --with-wctype-functions was removed. Using the + functions from the libc caused the methods .upper() and lower() to become + locale aware and created subtly wrong results. + - Issue #9738: PyUnicode_FromFormat() and PyErr_Format() raise an error on a non-ASCII byte in the format string. Modified: python/branches/py3k/Objects/unicodectype.c ============================================================================== --- python/branches/py3k/Objects/unicodectype.c (original) +++ python/branches/py3k/Objects/unicodectype.c Mon Sep 13 00:42:57 2010 @@ -163,8 +163,6 @@ return (ctype->flags & PRINTABLE_MASK) != 0; } -#ifndef WANT_WCTYPE_FUNCTIONS - /* Returns 1 for Unicode characters having the category 'Ll', 0 otherwise. */ @@ -223,34 +221,3 @@ return (ctype->flags & ALPHA_MASK) != 0; } -#else - -/* Export the interfaces using the wchar_t type for portability - reasons: */ - -int _PyUnicode_IsLowercase(Py_UCS4 ch) -{ - return iswlower(ch); -} - -int _PyUnicode_IsUppercase(Py_UCS4 ch) -{ - return iswupper(ch); -} - -Py_UCS4 _PyUnicode_ToLowercase(Py_UCS4 ch) -{ - return towlower(ch); -} - -Py_UCS4 _PyUnicode_ToUppercase(Py_UCS4 ch) -{ - return towupper(ch); -} - -int _PyUnicode_IsAlpha(Py_UCS4 ch) -{ - return iswalpha(ch); -} - -#endif Modified: python/branches/py3k/Objects/unicodetype_db.h ============================================================================== --- python/branches/py3k/Objects/unicodetype_db.h (original) +++ python/branches/py3k/Objects/unicodetype_db.h Mon Sep 13 00:42:57 2010 @@ -3247,9 +3247,6 @@ */ int _PyUnicode_IsWhitespace(register const Py_UCS4 ch) { -#ifdef WANT_WCTYPE_FUNCTIONS - return iswspace(ch); -#else switch (ch) { case 0x0009: case 0x000A: @@ -3284,7 +3281,6 @@ return 1; } return 0; -#endif } /* Returns 1 for Unicode characters having the line break Modified: python/branches/py3k/Tools/unicode/makeunicodedata.py ============================================================================== --- python/branches/py3k/Tools/unicode/makeunicodedata.py (original) +++ python/branches/py3k/Tools/unicode/makeunicodedata.py Mon Sep 13 00:42:57 2010 @@ -503,9 +503,6 @@ print(" */", file=fp) print('int _PyUnicode_IsWhitespace(register const Py_UCS4 ch)', file=fp) print('{', file=fp) - print('#ifdef WANT_WCTYPE_FUNCTIONS', file=fp) - print(' return iswspace(ch);', file=fp) - print('#else', file=fp) print(' switch (ch) {', file=fp) for codepoint in sorted(spaces): @@ -514,7 +511,6 @@ print(' }', file=fp) print(' return 0;', file=fp) - print('#endif', file=fp) print('}', file=fp) print(file=fp) Modified: python/branches/py3k/configure ============================================================================== --- python/branches/py3k/configure (original) +++ python/branches/py3k/configure Mon Sep 13 00:42:57 2010 @@ -747,7 +747,6 @@ with_tsc with_pymalloc with_valgrind -with_wctype_functions with_fpectl with_libm with_libc @@ -1418,7 +1417,6 @@ --with(out)-tsc enable/disable timestamp counter profile --with(out)-pymalloc disable/enable specialized mallocs --with-valgrind Enable Valgrind support - --with-wctype-functions use wctype.h functions --with-fpectl enable SIGFPE catching --with-libm=STRING math library --with-libc=STRING C library @@ -9232,28 +9230,6 @@ OPT="-DDYNAMIC_ANNOTATIONS_ENABLED=1 $OPT" fi -# Check for --with-wctype-functions -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-wctype-functions" >&5 -$as_echo_n "checking for --with-wctype-functions... " >&6; } - -# Check whether --with-wctype-functions was given. -if test "${with_wctype_functions+set}" = set; then : - withval=$with_wctype_functions; -if test "$withval" != no -then - -$as_echo "#define WANT_WCTYPE_FUNCTIONS 1" >>confdefs.h - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - # -I${DLINCLDIR} is added to the compile rule for importdl.o Modified: python/branches/py3k/configure.in ============================================================================== --- python/branches/py3k/configure.in (original) +++ python/branches/py3k/configure.in Mon Sep 13 00:42:57 2010 @@ -2493,21 +2493,6 @@ OPT="-DDYNAMIC_ANNOTATIONS_ENABLED=1 $OPT" fi -# Check for --with-wctype-functions -AC_MSG_CHECKING(for --with-wctype-functions) -AC_ARG_WITH(wctype-functions, - AS_HELP_STRING([--with-wctype-functions], [use wctype.h functions]), -[ -if test "$withval" != no -then - AC_DEFINE(WANT_WCTYPE_FUNCTIONS, 1, - [Define if you want wctype.h functions to be used instead of the - one supplied by Python itself. (see Include/unicodectype.h).]) - AC_MSG_RESULT(yes) -else AC_MSG_RESULT(no) -fi], -[AC_MSG_RESULT(no)]) - # -I${DLINCLDIR} is added to the compile rule for importdl.o AC_SUBST(DLINCLDIR) DLINCLDIR=. Modified: python/branches/py3k/pyconfig.h.in ============================================================================== --- python/branches/py3k/pyconfig.h.in (original) +++ python/branches/py3k/pyconfig.h.in Mon Sep 13 00:42:57 2010 @@ -1077,10 +1077,6 @@ /* Define if you want SIGFPE handled (see Include/pyfpe.h). */ #undef WANT_SIGFPE_HANDLER -/* Define if you want wctype.h functions to be used instead of the one - supplied by Python itself. (see Include/unicodectype.h). */ -#undef WANT_WCTYPE_FUNCTIONS - /* Define if WINDOW in curses.h offers a field _flags. */ #undef WINDOW_HAS_FLAGS From python-checkins at python.org Mon Sep 13 00:55:41 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 13 Sep 2010 00:55:41 +0200 (CEST) Subject: [Python-checkins] r84753 - python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py Message-ID: <20100912225541.13B0DEE981@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 13 00:55:40 2010 New Revision: 84753 Log: Issue #9313: Skips test_remove_visual_c_ref on old MSVC. Modified: python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py Modified: python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py ============================================================================== --- python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py (original) +++ python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py Mon Sep 13 00:55:40 2010 @@ -109,6 +109,11 @@ self.assertTrue('Desktop' in keys) def test_remove_visual_c_ref(self): + from distutils.msvccompiler import get_build_version + if get_build_version() < 8.0: + # this test is only for MSVC8.0 or above + return + from distutils.msvc9compiler import MSVCCompiler tempdir = self.mkdtemp() manifest = os.path.join(tempdir, 'manifest') From python-checkins at python.org Mon Sep 13 03:25:38 2010 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 13 Sep 2010 03:25:38 +0200 (CEST) Subject: [Python-checkins] r84754 - python/branches/py3k/Doc/library/imp.rst Message-ID: <20100913012538.7626CFB4F@mail.python.org> Author: benjamin.peterson Date: Mon Sep 13 03:25:38 2010 New Revision: 84754 Log: remove duplicate statement Modified: python/branches/py3k/Doc/library/imp.rst Modified: python/branches/py3k/Doc/library/imp.rst ============================================================================== --- python/branches/py3k/Doc/library/imp.rst (original) +++ python/branches/py3k/Doc/library/imp.rst Mon Sep 13 03:25:38 2010 @@ -113,8 +113,7 @@ .. function:: acquire_lock() Acquire the interpreter's import lock for the current thread. This lock should - be used by import hooks to ensure thread-safety when importing modules. On - platforms without threads, this function does nothing. + be used by import hooks to ensure thread-safety when importing modules. Once a thread has acquired the import lock, the same thread may acquire it again without blocking; the thread must release it once for each time it has From python-checkins at python.org Mon Sep 13 03:30:04 2010 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 13 Sep 2010 03:30:04 +0200 (CEST) Subject: [Python-checkins] r84755 - python/branches/py3k/Doc/library/imp.rst Message-ID: <20100913013004.838C0F76F@mail.python.org> Author: benjamin.peterson Date: Mon Sep 13 03:30:04 2010 New Revision: 84755 Log: remove less complete of duplicate docs Modified: python/branches/py3k/Doc/library/imp.rst Modified: python/branches/py3k/Doc/library/imp.rst ============================================================================== --- python/branches/py3k/Doc/library/imp.rst (original) +++ python/branches/py3k/Doc/library/imp.rst Mon Sep 13 03:30:04 2010 @@ -190,19 +190,6 @@ continue to use the old class definition. The same is true for derived classes. -.. function:: acquire_lock() - - Acquires the interpreter's import lock for the current thread. This lock should - be used by import hooks to ensure thread-safety when importing modules. On - platforms without threads, this function does nothing. - - -.. function:: release_lock() - - Release the interpreter's import lock. On platforms without threads, this - function does nothing. - - The following functions and data provide conveniences for handling :pep:`3147` byte-compiled file paths. From python-checkins at python.org Mon Sep 13 03:31:57 2010 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 13 Sep 2010 03:31:57 +0200 (CEST) Subject: [Python-checkins] r84756 - python/branches/py3k/Doc/library/imp.rst Message-ID: <20100913013157.A1EABFD78@mail.python.org> Author: benjamin.peterson Date: Mon Sep 13 03:31:57 2010 New Revision: 84756 Log: add spaces Modified: python/branches/py3k/Doc/library/imp.rst Modified: python/branches/py3k/Doc/library/imp.rst ============================================================================== --- python/branches/py3k/Doc/library/imp.rst (original) +++ python/branches/py3k/Doc/library/imp.rst Mon Sep 13 03:31:57 2010 @@ -209,6 +209,7 @@ *path* need not exist. + .. function:: source_from_cache(path) Given the *path* to a :pep:`3147` file name, return the associated source code @@ -217,6 +218,7 @@ ``/foo/bar/baz.py``. *path* need not exist, however if it does not conform to :pep:`3147` format, a ``ValueError`` is raised. + .. function:: get_tag() Return the :pep:`3147` magic tag string matching this version of Python's From python-checkins at python.org Mon Sep 13 03:48:02 2010 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 13 Sep 2010 03:48:02 +0200 (CEST) Subject: [Python-checkins] r84757 - in python/branches/release27-maint: Doc/library/imp.rst Message-ID: <20100913014802.8EBEAF538@mail.python.org> Author: benjamin.peterson Date: Mon Sep 13 03:48:02 2010 New Revision: 84757 Log: Merged revisions 84754 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84754 | benjamin.peterson | 2010-09-12 20:25:38 -0500 (Sun, 12 Sep 2010) | 1 line remove duplicate statement ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/imp.rst Modified: python/branches/release27-maint/Doc/library/imp.rst ============================================================================== --- python/branches/release27-maint/Doc/library/imp.rst (original) +++ python/branches/release27-maint/Doc/library/imp.rst Mon Sep 13 03:48:02 2010 @@ -116,8 +116,7 @@ .. function:: acquire_lock() Acquire the interpreter's import lock for the current thread. This lock should - be used by import hooks to ensure thread-safety when importing modules. On - platforms without threads, this function does nothing. + be used by import hooks to ensure thread-safety when importing modules. Once a thread has acquired the import lock, the same thread may acquire it again without blocking; the thread must release it once for each time it has From python-checkins at python.org Mon Sep 13 03:52:40 2010 From: python-checkins at python.org (benjamin.peterson) Date: Mon, 13 Sep 2010 03:52:40 +0200 (CEST) Subject: [Python-checkins] r84758 - in python/branches/release31-maint: Doc/library/imp.rst Message-ID: <20100913015240.4C920F43F@mail.python.org> Author: benjamin.peterson Date: Mon Sep 13 03:52:40 2010 New Revision: 84758 Log: Merged revisions 84754-84755 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84754 | benjamin.peterson | 2010-09-12 20:25:38 -0500 (Sun, 12 Sep 2010) | 1 line remove duplicate statement ........ r84755 | benjamin.peterson | 2010-09-12 20:30:04 -0500 (Sun, 12 Sep 2010) | 1 line remove less complete of duplicate docs ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/imp.rst Modified: python/branches/release31-maint/Doc/library/imp.rst ============================================================================== --- python/branches/release31-maint/Doc/library/imp.rst (original) +++ python/branches/release31-maint/Doc/library/imp.rst Mon Sep 13 03:52:40 2010 @@ -113,8 +113,7 @@ .. function:: acquire_lock() Acquire the interpreter's import lock for the current thread. This lock should - be used by import hooks to ensure thread-safety when importing modules. On - platforms without threads, this function does nothing. + be used by import hooks to ensure thread-safety when importing modules. Once a thread has acquired the import lock, the same thread may acquire it again without blocking; the thread must release it once for each time it has @@ -191,19 +190,6 @@ continue to use the old class definition. The same is true for derived classes. -.. function:: acquire_lock() - - Acquires the interpreter's import lock for the current thread. This lock should - be used by import hooks to ensure thread-safety when importing modules. On - platforms without threads, this function does nothing. - - -.. function:: release_lock() - - Release the interpreter's import lock. On platforms without threads, this - function does nothing. - - The following constants with integer values, defined in this module, are used to indicate the search result of :func:`find_module`. From python-checkins at python.org Mon Sep 13 04:28:18 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 13 Sep 2010 04:28:18 +0200 (CEST) Subject: [Python-checkins] r84759 - in python/branches/py3k/Lib/test: string_tests.py test_unicode.py Message-ID: <20100913022818.BE69EFBBD@mail.python.org> Author: florent.xicluna Date: Mon Sep 13 04:28:18 2010 New Revision: 84759 Log: Reenable test_ucs4 and remove some duplicated lines. Modified: python/branches/py3k/Lib/test/string_tests.py python/branches/py3k/Lib/test/test_unicode.py Modified: python/branches/py3k/Lib/test/string_tests.py ============================================================================== --- python/branches/py3k/Lib/test/string_tests.py (original) +++ python/branches/py3k/Lib/test/string_tests.py Mon Sep 13 04:28:18 2010 @@ -1131,7 +1131,7 @@ format = '%%.%if' % prec value = 0.01 for x in range(60): - value = value * 3.141592655 / 3.0 * 10.0 + value = value * 3.14159265359 / 3.0 * 10.0 self.checkcall(format, "__mod__", value) def test_inplace_rewrites(self): Modified: python/branches/py3k/Lib/test/test_unicode.py ============================================================================== --- python/branches/py3k/Lib/test/test_unicode.py (original) +++ python/branches/py3k/Lib/test/test_unicode.py Mon Sep 13 04:28:18 2010 @@ -62,7 +62,7 @@ self.assertRaises(SyntaxError, eval, '\'\\Uffffffff\'') self.assertRaises(SyntaxError, eval, '\'\\U%08x\'' % 0x110000) # raw strings should not have unicode escapes - self.assertNotEquals(r"\u0020", " ") + self.assertNotEqual(r"\u0020", " ") def test_ascii(self): if not sys.platform.startswith('java'): @@ -286,13 +286,7 @@ def test_comparison(self): # Comparisons: self.assertEqual('abc', 'abc') - self.assertEqual('abc', 'abc') - self.assertEqual('abc', 'abc') - self.assertTrue('abcd' > 'abc') self.assertTrue('abcd' > 'abc') - self.assertTrue('abcd' > 'abc') - self.assertTrue('abc' < 'abcd') - self.assertTrue('abc' < 'abcd') self.assertTrue('abc' < 'abcd') if 0: @@ -652,8 +646,6 @@ self.assertRaises(IndexError, '{1}'.format, 'abc') self.assertRaises(KeyError, '{x}'.format) self.assertRaises(ValueError, "}{".format) - self.assertRaises(ValueError, "{".format) - self.assertRaises(ValueError, "}".format) self.assertRaises(ValueError, "abc{0:{}".format) self.assertRaises(ValueError, "{0".format) self.assertRaises(IndexError, "{0.}".format) @@ -1265,21 +1257,20 @@ y = x.encode("raw-unicode-escape").decode("raw-unicode-escape") self.assertEqual(x, y) - # FIXME - #y = r'\U00100000' - #x = y.encode("raw-unicode-escape").decode("raw-unicode-escape") - #self.assertEqual(x, y) - #y = r'\U00010000' - #x = y.encode("raw-unicode-escape").decode("raw-unicode-escape") - #self.assertEqual(x, y) - - #try: - # '\U11111111'.decode("raw-unicode-escape") - #except UnicodeDecodeError as e: - # self.assertEqual(e.start, 0) - # self.assertEqual(e.end, 10) - #else: - # self.fail("Should have raised UnicodeDecodeError") + y = br'\U00100000' + x = y.decode("raw-unicode-escape").encode("raw-unicode-escape") + self.assertEqual(x, y) + y = br'\U00010000' + x = y.decode("raw-unicode-escape").encode("raw-unicode-escape") + self.assertEqual(x, y) + + try: + br'\U11111111'.decode("raw-unicode-escape") + except UnicodeDecodeError as e: + self.assertEqual(e.start, 0) + self.assertEqual(e.end, 10) + else: + self.fail("Should have raised UnicodeDecodeError") def test_conversion(self): # Make sure __unicode__() works properly @@ -1382,8 +1373,8 @@ def __str__(self): return '__str__ overridden' s = S('xxx') - self.assertEquals("%s" % s, '__str__ overridden') - self.assertEquals("{}".format(s), '__str__ overridden') + self.assertEqual("%s" % s, '__str__ overridden') + self.assertEqual("{}".format(s), '__str__ overridden') def test_from_format(self): # Ensure that PyUnicode_FromFormat() raises an error for a non-ascii From solipsis at pitrou.net Mon Sep 13 05:09:34 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 13 Sep 2010 05:09:34 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84754): sum=0 Message-ID: py3k results for svn r84754 (hg cset ca273dad2359) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogBn7lPm', '-x'] From python-checkins at python.org Mon Sep 13 07:36:21 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 13 Sep 2010 07:36:21 +0200 (CEST) Subject: [Python-checkins] r84760 - python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py Message-ID: <20100913053621.38CE1F525@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 13 07:36:21 2010 New Revision: 84760 Log: Issue #9313: Use unittest.skipUnless to skip old MSVC. Modified: python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py Modified: python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py ============================================================================== --- python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py (original) +++ python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py Mon Sep 13 07:36:21 2010 @@ -60,7 +60,12 @@ """ +if sys.platform=="win32": + from distutils.msvccompiler import get_build_version + @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + at unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" + " MSVC8.0 or above") class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): @@ -68,10 +73,6 @@ # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler # is not found - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None @@ -86,11 +87,6 @@ msvc9compiler.find_vcvarsall = old_find_vcvarsall def test_reg_class(self): - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return - from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') @@ -109,11 +105,6 @@ self.assertTrue('Desktop' in keys) def test_remove_visual_c_ref(self): - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return - from distutils.msvc9compiler import MSVCCompiler tempdir = self.mkdtemp() manifest = os.path.join(tempdir, 'manifest') From python-checkins at python.org Mon Sep 13 07:48:30 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 13 Sep 2010 07:48:30 +0200 (CEST) Subject: [Python-checkins] r84761 - in python/branches/release31-maint: Lib/distutils/tests/test_msvc9compiler.py Message-ID: <20100913054830.AA5D3EE998@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 13 07:48:30 2010 New Revision: 84761 Log: Merged revisions 84753,84760 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84753 | hirokazu.yamamoto | 2010-09-13 07:55:40 +0900 | 1 line Issue #9313: Skips test_remove_visual_c_ref on old MSVC. ........ r84760 | hirokazu.yamamoto | 2010-09-13 14:36:21 +0900 | 1 line Issue #9313: Use unittest.skipUnless to skip old MSVC. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/distutils/tests/test_msvc9compiler.py Modified: python/branches/release31-maint/Lib/distutils/tests/test_msvc9compiler.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/tests/test_msvc9compiler.py (original) +++ python/branches/release31-maint/Lib/distutils/tests/test_msvc9compiler.py Mon Sep 13 07:48:30 2010 @@ -60,7 +60,12 @@ """ +if sys.platform=="win32": + from distutils.msvccompiler import get_build_version + @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + at unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" + " MSVC8.0 or above") class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): @@ -68,10 +73,6 @@ # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler # is not found - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None @@ -86,11 +87,6 @@ msvc9compiler.find_vcvarsall = old_find_vcvarsall def test_reg_class(self): - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return - from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') From python-checkins at python.org Mon Sep 13 07:59:39 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 13 Sep 2010 07:59:39 +0200 (CEST) Subject: [Python-checkins] r84762 - in python/branches/py3k/PC/VS8.0: _multiprocessing.vcproj _sqlite3.vcproj bdist_wininst.vcproj pyd.vsprops pyd_d.vsprops pyproject.vsprops pythoncore.vcproj sqlite3.vcproj sqlite3.vsprops Message-ID: <20100913055939.3464CF78A@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 13 07:59:38 2010 New Revision: 84762 Log: Updated PC/VS8.0 with PCBuild/vs9to8.py. Added: python/branches/py3k/PC/VS8.0/sqlite3.vsprops Modified: python/branches/py3k/PC/VS8.0/_multiprocessing.vcproj python/branches/py3k/PC/VS8.0/_sqlite3.vcproj python/branches/py3k/PC/VS8.0/bdist_wininst.vcproj python/branches/py3k/PC/VS8.0/pyd.vsprops python/branches/py3k/PC/VS8.0/pyd_d.vsprops python/branches/py3k/PC/VS8.0/pyproject.vsprops python/branches/py3k/PC/VS8.0/pythoncore.vcproj python/branches/py3k/PC/VS8.0/sqlite3.vcproj Modified: python/branches/py3k/PC/VS8.0/_multiprocessing.vcproj ============================================================================== --- python/branches/py3k/PC/VS8.0/_multiprocessing.vcproj (original) +++ python/branches/py3k/PC/VS8.0/_multiprocessing.vcproj Mon Sep 13 07:59:38 2010 @@ -3,7 +3,7 @@ ProjectType="Visual C++" Version="8.00" Name="_multiprocessing" - ProjectGUID="{9e48b300-37d1-11dd-8c41-005056c00008}" + ProjectGUID="{9E48B300-37D1-11DD-8C41-005056C00008}" RootNamespace="_multiprocessing" Keyword="Win32Proj" TargetFrameworkVersion="196613" Modified: python/branches/py3k/PC/VS8.0/_sqlite3.vcproj ============================================================================== --- python/branches/py3k/PC/VS8.0/_sqlite3.vcproj (original) +++ python/branches/py3k/PC/VS8.0/_sqlite3.vcproj Mon Sep 13 07:59:38 2010 @@ -42,7 +42,7 @@ /> + + + - - @@ -847,6 +843,10 @@ > + + @@ -879,7 +879,7 @@ > + + @@ -947,10 +951,6 @@ > - - @@ -998,38 +998,6 @@ RelativePath="..\..\Modules\_csv.c" > - - - - - - - - - - - - - - - - @@ -1075,6 +1043,14 @@ > + + + + @@ -1175,10 +1151,6 @@ > - - @@ -1195,6 +1167,42 @@ > + + + + + + + + + + + + + + + + + + - - @@ -1771,7 +1775,7 @@ > + + Modified: python/branches/py3k/PC/VS8.0/sqlite3.vcproj ============================================================================== --- python/branches/py3k/PC/VS8.0/sqlite3.vcproj (original) +++ python/branches/py3k/PC/VS8.0/sqlite3.vcproj Mon Sep 13 07:59:38 2010 @@ -22,7 +22,7 @@ @@ -166,8 +164,7 @@ /> @@ -229,8 +226,7 @@ /> @@ -291,8 +287,7 @@ /> @@ -354,8 +349,7 @@ /> @@ -415,8 +409,7 @@ /> @@ -478,8 +471,7 @@ /> @@ -535,7 +527,7 @@ Name="Source Files" > Added: python/branches/py3k/PC/VS8.0/sqlite3.vsprops ============================================================================== --- (empty file) +++ python/branches/py3k/PC/VS8.0/sqlite3.vsprops Mon Sep 13 07:59:38 2010 @@ -0,0 +1,14 @@ + + + + From python-checkins at python.org Mon Sep 13 08:21:55 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 13 Sep 2010 08:21:55 +0200 (CEST) Subject: [Python-checkins] r84763 - in python/branches/py3k: PC/VS8.0/pyd.vsprops PCbuild/pyd.vsprops Message-ID: <20100913062155.241E3EE998@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 13 08:21:54 2010 New Revision: 84763 Log: Cosmetic fix to project files. Modified: python/branches/py3k/PC/VS8.0/pyd.vsprops python/branches/py3k/PCbuild/pyd.vsprops Modified: python/branches/py3k/PC/VS8.0/pyd.vsprops ============================================================================== --- python/branches/py3k/PC/VS8.0/pyd.vsprops (original) +++ python/branches/py3k/PC/VS8.0/pyd.vsprops Mon Sep 13 08:21:54 2010 @@ -1,4 +1,4 @@ - + Author: hirokazu.yamamoto Date: Mon Sep 13 08:36:09 2010 New Revision: 84764 Log: Merged revisions 84753,84760 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84753 | hirokazu.yamamoto | 2010-09-13 07:55:40 +0900 | 1 line Issue #9313: Skips test_remove_visual_c_ref on old MSVC. ........ r84760 | hirokazu.yamamoto | 2010-09-13 14:36:21 +0900 | 1 line Issue #9313: Use unittest.skipUnless to skip old MSVC. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/distutils/tests/test_msvc9compiler.py Modified: python/branches/release27-maint/Lib/distutils/tests/test_msvc9compiler.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/tests/test_msvc9compiler.py (original) +++ python/branches/release27-maint/Lib/distutils/tests/test_msvc9compiler.py Mon Sep 13 08:36:09 2010 @@ -60,7 +60,12 @@ """ +if sys.platform=="win32": + from distutils.msvccompiler import get_build_version + @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + at unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" + " MSVC8.0 or above") class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): @@ -68,10 +73,6 @@ # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler # is not found - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None @@ -86,11 +87,6 @@ msvc9compiler.find_vcvarsall = old_find_vcvarsall def test_reg_class(self): - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return - from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') From python-checkins at python.org Mon Sep 13 09:18:30 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 13 Sep 2010 09:18:30 +0200 (CEST) Subject: [Python-checkins] r84765 - python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py Message-ID: <20100913071830.B7275F629@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 13 09:18:30 2010 New Revision: 84765 Log: The order of two decorators was wrong. Try to fix buildbot error in other way. Modified: python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py Modified: python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py ============================================================================== --- python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py (original) +++ python/branches/py3k/Lib/distutils/tests/test_msvc9compiler.py Mon Sep 13 09:18:30 2010 @@ -62,10 +62,14 @@ if sys.platform=="win32": from distutils.msvccompiler import get_build_version + if get_build_version()>=8.0: + SKIP_MESSAGE = None + else: + SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" +else: + SKIP_MESSAGE = "These tests are only for win32" - at unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") - at unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" - " MSVC8.0 or above") + at unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): From python-checkins at python.org Mon Sep 13 09:25:54 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 13 Sep 2010 09:25:54 +0200 (CEST) Subject: [Python-checkins] r84765 - svn:log Message-ID: <20100913072554.1024BF76F@mail.python.org> Author: hirokazu.yamamoto Revision: 84765 Property Name: svn:log Action: modified Property diff: --- old property value +++ new property value @@ -1 +1,2 @@ -The order of two decorators was wrong. Try to fix buildbot error in other way. \ No newline at end of file +get_build_version() is needed even where sys.platform != "win32". +Try to fix buildbot error in other way. \ No newline at end of file From python-checkins at python.org Mon Sep 13 09:46:37 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 13 Sep 2010 09:46:37 +0200 (CEST) Subject: [Python-checkins] r84766 - in python/branches/release27-maint: Doc/reference/simple_stmts.rst Lib/string.py Lib/test/string_tests.py Lib/test/test_bytes.py Lib/test/test_unicode.py Misc/pymemcompat.h Message-ID: <20100913074637.D9610F76F@mail.python.org> Author: florent.xicluna Date: Mon Sep 13 09:46:37 2010 New Revision: 84766 Log: Merged revisions 84470-84471,84566-84567,84759 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84470 | florent.xicluna | 2010-09-03 22:00:37 +0200 (ven., 03 sept. 2010) | 1 line Strengthen BytesWarning tests. ........ r84471 | florent.xicluna | 2010-09-03 22:23:40 +0200 (ven., 03 sept. 2010) | 1 line Typo ........ r84566 | florent.xicluna | 2010-09-06 22:27:15 +0200 (lun., 06 sept. 2010) | 1 line typo ........ r84567 | florent.xicluna | 2010-09-06 22:27:55 +0200 (lun., 06 sept. 2010) | 1 line typo ........ r84759 | florent.xicluna | 2010-09-13 04:28:18 +0200 (lun., 13 sept. 2010) | 1 line Reenable test_ucs4 and remove some duplicated lines. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/reference/simple_stmts.rst python/branches/release27-maint/Lib/string.py python/branches/release27-maint/Lib/test/string_tests.py python/branches/release27-maint/Lib/test/test_bytes.py python/branches/release27-maint/Lib/test/test_unicode.py python/branches/release27-maint/Misc/pymemcompat.h Modified: python/branches/release27-maint/Doc/reference/simple_stmts.rst ============================================================================== --- python/branches/release27-maint/Doc/reference/simple_stmts.rst (original) +++ python/branches/release27-maint/Doc/reference/simple_stmts.rst Mon Sep 13 09:46:37 2010 @@ -836,7 +836,7 @@ exists. Two dots means up one package level. Three dots is up two levels, etc. So if you execute ``from . import mod`` from a module in the ``pkg`` package then you will end up importing ``pkg.mod``. If you execute ``from ..subpkg2 -imprt mod`` from within ``pkg.subpkg1`` you will import ``pkg.subpkg2.mod``. +import mod`` from within ``pkg.subpkg1`` you will import ``pkg.subpkg2.mod``. The specification for relative imports is contained within :pep:`328`. :func:`importlib.import_module` is provided to support applications that Modified: python/branches/release27-maint/Lib/string.py ============================================================================== --- python/branches/release27-maint/Lib/string.py (original) +++ python/branches/release27-maint/Lib/string.py Mon Sep 13 09:46:37 2010 @@ -607,7 +607,7 @@ return str(value) elif conversion is None: return value - raise ValueError("Unknown converion specifier {0!s}".format(conversion)) + raise ValueError("Unknown conversion specifier {0!s}".format(conversion)) # returns an iterable that contains tuples of the form: Modified: python/branches/release27-maint/Lib/test/string_tests.py ============================================================================== --- python/branches/release27-maint/Lib/test/string_tests.py (original) +++ python/branches/release27-maint/Lib/test/string_tests.py Mon Sep 13 09:46:37 2010 @@ -1119,7 +1119,7 @@ format = '%%.%if' % prec value = 0.01 for x in xrange(60): - value = value * 3.141592655 / 3.0 * 10.0 + value = value * 3.14159265359 / 3.0 * 10.0 self.checkcall(format, "__mod__", value) def test_inplace_rewrites(self): Modified: python/branches/release27-maint/Lib/test/test_bytes.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_bytes.py (original) +++ python/branches/release27-maint/Lib/test/test_bytes.py Mon Sep 13 09:46:37 2010 @@ -9,14 +9,28 @@ import re import sys import copy +import functools import pickle import tempfile import unittest -import warnings import test.test_support import test.string_tests import test.buffer_tests + +if sys.flags.bytes_warning: + def check_bytes_warnings(func): + @functools.wraps(func) + def wrapper(*args, **kw): + with test.test_support.check_warnings(('', BytesWarning)): + return func(*args, **kw) + return wrapper +else: + # no-op + def check_bytes_warnings(func): + return func + + class Indexable: def __init__(self, value=0): self.value = value @@ -26,12 +40,6 @@ class BaseBytesTest(unittest.TestCase): - def setUp(self): - self.warning_filters = warnings.filters[:] - - def tearDown(self): - warnings.filters = self.warning_filters - def test_basics(self): b = self.type2test() self.assertEqual(type(b), self.type2test) @@ -120,8 +128,8 @@ self.assertFalse(b3 < b2) self.assertFalse(b3 <= b2) + @check_bytes_warnings def test_compare_to_str(self): - warnings.simplefilter('ignore', BytesWarning) # Byte comparisons with unicode should always fail! # Test this for all expected byte orders and Unicode character sizes self.assertEqual(self.type2test(b"\0a\0b\0c") == u"abc", False) @@ -795,14 +803,8 @@ # Test various combinations of bytes and bytearray # - def setUp(self): - self.warning_filters = warnings.filters[:] - - def tearDown(self): - warnings.filters = self.warning_filters - + @check_bytes_warnings def test_repr_str(self): - warnings.simplefilter('ignore', BytesWarning) for f in str, repr: self.assertEqual(f(bytearray()), "bytearray(b'')") self.assertEqual(f(bytearray([0])), "bytearray(b'\\x00')") @@ -853,8 +855,8 @@ b = bytearray(buf) self.assertEqual(b, bytearray(sample)) + @check_bytes_warnings def test_to_str(self): - warnings.simplefilter('ignore', BytesWarning) self.assertEqual(str(b''), "b''") self.assertEqual(str(b'x'), "b'x'") self.assertEqual(str(b'\x80'), "b'\\x80'") Modified: python/branches/release27-maint/Lib/test/test_unicode.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_unicode.py (original) +++ python/branches/release27-maint/Lib/test/test_unicode.py Mon Sep 13 09:46:37 2010 @@ -593,9 +593,9 @@ ) # UTF-8 specific decoding tests - self.assertEqual(unicode('\xf0\xa3\x91\x96', 'utf-8'), u'\U00023456' ) - self.assertEqual(unicode('\xf0\x90\x80\x82', 'utf-8'), u'\U00010002' ) - self.assertEqual(unicode('\xe2\x82\xac', 'utf-8'), u'\u20ac' ) + self.assertEqual(unicode('\xf0\xa3\x91\x96', 'utf-8'), u'\U00023456') + self.assertEqual(unicode('\xf0\x90\x80\x82', 'utf-8'), u'\U00010002') + self.assertEqual(unicode('\xe2\x82\xac', 'utf-8'), u'\u20ac') # Other possible utf-8 test cases: # * strict decoding testing for all of the @@ -1360,8 +1360,8 @@ def __unicode__(self): return u'__unicode__ overridden' u = U(u'xxx') - self.assertEquals("%s" % u, u'__unicode__ overridden') - self.assertEquals("{}".format(u), u'__unicode__ overridden') + self.assertEqual("%s" % u, u'__unicode__ overridden') + self.assertEqual("{}".format(u), u'__unicode__ overridden') def test_main(): Modified: python/branches/release27-maint/Misc/pymemcompat.h ============================================================================== --- python/branches/release27-maint/Misc/pymemcompat.h (original) +++ python/branches/release27-maint/Misc/pymemcompat.h Mon Sep 13 09:46:37 2010 @@ -72,7 +72,7 @@ It is possible to support both the 2.0 and 2.2 GC APIs, but it's not pretty and this comment block is too narrow to contain a - desciption of what's required... */ + description of what's required... */ #if PY_VERSION_HEX < 0x020200B1 #define PyObject_GC_New PyObject_New From python-checkins at python.org Mon Sep 13 09:48:22 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 13 Sep 2010 09:48:22 +0200 (CEST) Subject: [Python-checkins] r84767 - python/branches/release31-maint/Lib/distutils/tests/test_msvc9compiler.py Message-ID: <20100913074822.40037F76F@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 13 09:48:22 2010 New Revision: 84767 Log: Merged revisions 84765 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84765 | hirokazu.yamamoto | 2010-09-13 16:18:30 +0900 | 2 lines get_build_version() is needed even where sys.platform != "win32". Try to fix buildbot error in other way. ........ Modified: python/branches/release31-maint/Lib/distutils/tests/test_msvc9compiler.py Modified: python/branches/release31-maint/Lib/distutils/tests/test_msvc9compiler.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/tests/test_msvc9compiler.py (original) +++ python/branches/release31-maint/Lib/distutils/tests/test_msvc9compiler.py Mon Sep 13 09:48:22 2010 @@ -62,10 +62,14 @@ if sys.platform=="win32": from distutils.msvccompiler import get_build_version + if get_build_version()>=8.0: + SKIP_MESSAGE = None + else: + SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" +else: + SKIP_MESSAGE = "These tests are only for win32" - at unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") - at unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" - " MSVC8.0 or above") + at unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): From python-checkins at python.org Mon Sep 13 09:50:44 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 13 Sep 2010 09:50:44 +0200 (CEST) Subject: [Python-checkins] r84768 - python/branches/release27-maint Message-ID: <20100913075044.0D25EF76F@mail.python.org> Author: florent.xicluna Date: Mon Sep 13 09:50:43 2010 New Revision: 84768 Log: Blocked revisions 84081-84082,84363,84467-84469,84485 via svnmerge ........ r84081 | florent.xicluna | 2010-08-15 22:16:27 +0200 (dim., 15 ao?t 2010) | 1 line Replace the deprecated ConfigParser.readfp() method, and fix the incomplete merge in r82293. ........ r84082 | florent.xicluna | 2010-08-15 22:21:26 +0200 (dim., 15 ao?t 2010) | 1 line Replace readfp() with read_file() in configparser documentation. ........ r84363 | florent.xicluna | 2010-08-30 16:05:50 +0200 (lun., 30 ao?t 2010) | 1 line remove pointless coding cookies ........ r84467 | florent.xicluna | 2010-09-03 21:52:03 +0200 (ven., 03 sept. 2010) | 1 line Use a context manager for some file objects. ........ r84468 | florent.xicluna | 2010-09-03 21:54:02 +0200 (ven., 03 sept. 2010) | 1 line Remove unused import, fix typo and rewrap docstrings. ........ r84469 | florent.xicluna | 2010-09-03 21:55:26 +0200 (ven., 03 sept. 2010) | 1 line Remove redundant context manager. ........ r84485 | florent.xicluna | 2010-09-04 01:47:32 +0200 (sam., 04 sept. 2010) | 2 lines Welcome to the UTF-8 world. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Mon Sep 13 10:07:29 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 13 Sep 2010 10:07:29 +0200 (CEST) Subject: [Python-checkins] r84769 - python/branches/release31-maint Message-ID: <20100913080729.2E0E8F525@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 13 10:07:29 2010 New Revision: 84769 Log: Recorded merge of revisions 84765 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84765 | hirokazu.yamamoto | 2010-09-13 16:18:30 +0900 | 2 lines get_build_version() is needed even where sys.platform != "win32". Try to fix buildbot error in other way. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Mon Sep 13 10:14:41 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Mon, 13 Sep 2010 10:14:41 +0200 (CEST) Subject: [Python-checkins] r84770 - in python/branches/release27-maint: Lib/distutils/tests/test_msvc9compiler.py Message-ID: <20100913081441.9DCFEF343@mail.python.org> Author: hirokazu.yamamoto Date: Mon Sep 13 10:14:41 2010 New Revision: 84770 Log: Merged revisions 84765 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84765 | hirokazu.yamamoto | 2010-09-13 16:18:30 +0900 | 2 lines get_build_version() is needed even where sys.platform != "win32". Try to fix buildbot error in other way. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/distutils/tests/test_msvc9compiler.py Modified: python/branches/release27-maint/Lib/distutils/tests/test_msvc9compiler.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/tests/test_msvc9compiler.py (original) +++ python/branches/release27-maint/Lib/distutils/tests/test_msvc9compiler.py Mon Sep 13 10:14:41 2010 @@ -62,10 +62,14 @@ if sys.platform=="win32": from distutils.msvccompiler import get_build_version + if get_build_version()>=8.0: + SKIP_MESSAGE = None + else: + SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" +else: + SKIP_MESSAGE = "These tests are only for win32" - at unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") - at unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" - " MSVC8.0 or above") + at unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): From python-checkins at python.org Mon Sep 13 10:20:19 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 13 Sep 2010 10:20:19 +0200 (CEST) Subject: [Python-checkins] r84771 - python/branches/release27-maint/Lib/test/test_io.py Message-ID: <20100913082019.DDF9CF525@mail.python.org> Author: florent.xicluna Date: Mon Sep 13 10:20:19 2010 New Revision: 84771 Log: Silence warning about 1/0 Modified: python/branches/release27-maint/Lib/test/test_io.py Modified: python/branches/release27-maint/Lib/test/test_io.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_io.py (original) +++ python/branches/release27-maint/Lib/test/test_io.py Mon Sep 13 10:20:19 2010 @@ -2484,7 +2484,7 @@ signal.signal(signal.SIGALRM, self.oldalrm) def alarm_interrupt(self, sig, frame): - 1/0 + 1 // 0 @unittest.skipUnless(threading, 'Threading required for this test.') def check_interrupted_write(self, item, bytes, **fdopen_kwargs): From python-checkins at python.org Mon Sep 13 10:21:43 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 13 Sep 2010 10:21:43 +0200 (CEST) Subject: [Python-checkins] r84772 - in python/branches/release27-maint/Lib/test: test_str.py test_unicode.py Message-ID: <20100913082143.63BB0F525@mail.python.org> Author: florent.xicluna Date: Mon Sep 13 10:21:43 2010 New Revision: 84772 Log: Check PendingDeprecationWarning after issue #7994. Modified: python/branches/release27-maint/Lib/test/test_str.py python/branches/release27-maint/Lib/test/test_unicode.py Modified: python/branches/release27-maint/Lib/test/test_str.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_str.py (original) +++ python/branches/release27-maint/Lib/test/test_str.py Mon Sep 13 10:21:43 2010 @@ -276,7 +276,7 @@ # format specifiers for user defined type self.assertEqual('{0:abc}'.format(C()), 'abc') - # !r and !s coersions + # !r and !s coercions self.assertEqual('{0!s}'.format('Hello'), 'Hello') self.assertEqual('{0!s:}'.format('Hello'), 'Hello') self.assertEqual('{0!s:15}'.format('Hello'), 'Hello ') @@ -290,12 +290,15 @@ self.assertEqual('{0}'.format([]), '[]') self.assertEqual('{0}'.format([1]), '[1]') self.assertEqual('{0}'.format(E('data')), 'E(data)') - self.assertEqual('{0:^10}'.format(E('data')), ' E(data) ') - self.assertEqual('{0:^10s}'.format(E('data')), ' E(data) ') self.assertEqual('{0:d}'.format(G('data')), 'G(data)') - self.assertEqual('{0:>15s}'.format(G('data')), ' string is data') self.assertEqual('{0!s}'.format(G('data')), 'string is data') + msg = 'object.__format__ with a non-empty format string is deprecated' + with test_support.check_warnings((msg, PendingDeprecationWarning)): + self.assertEqual('{0:^10}'.format(E('data')), ' E(data) ') + self.assertEqual('{0:^10s}'.format(E('data')), ' E(data) ') + self.assertEqual('{0:>15s}'.format(G('data')), ' string is data') + self.assertEqual("{0:date: %Y-%m-%d}".format(I(year=2007, month=8, day=27)), Modified: python/branches/release27-maint/Lib/test/test_unicode.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_unicode.py (original) +++ python/branches/release27-maint/Lib/test/test_unicode.py Mon Sep 13 10:21:43 2010 @@ -1209,7 +1209,7 @@ # format specifiers for user defined type self.assertEqual(u'{0:abc}'.format(C()), u'abc') - # !r and !s coersions + # !r and !s coercions self.assertEqual(u'{0!s}'.format(u'Hello'), u'Hello') self.assertEqual(u'{0!s:}'.format(u'Hello'), u'Hello') self.assertEqual(u'{0!s:15}'.format(u'Hello'), u'Hello ') @@ -1223,12 +1223,15 @@ self.assertEqual(u'{0}'.format([]), u'[]') self.assertEqual(u'{0}'.format([1]), u'[1]') self.assertEqual(u'{0}'.format(E(u'data')), u'E(data)') - self.assertEqual(u'{0:^10}'.format(E(u'data')), u' E(data) ') - self.assertEqual(u'{0:^10s}'.format(E(u'data')), u' E(data) ') self.assertEqual(u'{0:d}'.format(G(u'data')), u'G(data)') - self.assertEqual(u'{0:>15s}'.format(G(u'data')), u' string is data') self.assertEqual(u'{0!s}'.format(G(u'data')), u'string is data') + msg = 'object.__format__ with a non-empty format string is deprecated' + with test_support.check_warnings((msg, PendingDeprecationWarning)): + self.assertEqual(u'{0:^10}'.format(E(u'data')), u' E(data) ') + self.assertEqual(u'{0:^10s}'.format(E(u'data')), u' E(data) ') + self.assertEqual(u'{0:>15s}'.format(G(u'data')), u' string is data') + self.assertEqual(u"{0:date: %Y-%m-%d}".format(I(year=2007, month=8, day=27)), From python-checkins at python.org Mon Sep 13 10:53:00 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 13 Sep 2010 10:53:00 +0200 (CEST) Subject: [Python-checkins] r84773 - in python/branches/release27-maint: Lib/test/string_tests.py Lib/test/test_unicode.py Misc/NEWS Message-ID: <20100913085300.4120EF4CB@mail.python.org> Author: florent.xicluna Date: Mon Sep 13 10:53:00 2010 New Revision: 84773 Log: Strengthen test_unicode with explicit type checking for assertEqual tests. Modified: python/branches/release27-maint/Lib/test/string_tests.py python/branches/release27-maint/Lib/test/test_unicode.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/test/string_tests.py ============================================================================== --- python/branches/release27-maint/Lib/test/string_tests.py (original) +++ python/branches/release27-maint/Lib/test/string_tests.py Mon Sep 13 10:53:00 2010 @@ -254,7 +254,7 @@ r2 = j in i self.assertEqual(r1, r2) if loc != -1: - self.assertEqual(i[loc:loc+len(j)], j) + self.assertEqual(i[loc:loc+len(j)], self.fixtype(j)) # issue 7458 self.checkequal(-1, 'ab', 'rfind', 'xxx', sys.maxsize + 1, 0) Modified: python/branches/release27-maint/Lib/test/test_unicode.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_unicode.py (original) +++ python/branches/release27-maint/Lib/test/test_unicode.py Mon Sep 13 10:53:00 2010 @@ -34,6 +34,16 @@ ): type2test = unicode + def assertEqual(self, first, second, msg=None): + # strict assertEqual method: reject implicit bytes/unicode equality + super(UnicodeTest, self).assertEqual(first, second, msg) + if isinstance(first, unicode) or isinstance(second, unicode): + self.assertIsInstance(first, unicode) + self.assertIsInstance(second, unicode) + elif isinstance(first, str) or isinstance(second, str): + self.assertIsInstance(first, str) + self.assertIsInstance(second, str) + def checkequalnofix(self, result, object, methodname, *args): method = getattr(object, methodname) realresult = method(*args) @@ -197,9 +207,9 @@ def test_comparison(self): # Comparisons: - self.assertEqual(u'abc', 'abc') - self.assertEqual('abc', u'abc') - self.assertEqual(u'abc', u'abc') + self.assertTrue(u'abc' == 'abc') + self.assertTrue('abc' == u'abc') + self.assertTrue(u'abc' == u'abc') self.assertTrue(u'abcd' > 'abc') self.assertTrue('abcd' > u'abc') self.assertTrue(u'abcd' > u'abc') @@ -398,8 +408,10 @@ for num in range(0x00,0x80): char = chr(num) - self.assertEqual(u"%c" % char, char) - self.assertEqual(u"%c" % num, char) + self.assertEqual(u"%c" % char, unicode(char)) + self.assertEqual(u"%c" % num, unicode(char)) + self.assertTrue(char == u"%c" % char) + self.assertTrue(char == u"%c" % num) # Issue 7649 for num in range(0x80,0x100): uchar = unichr(num) @@ -558,9 +570,11 @@ set_o = '!"#$%&*;<=>@[]^_`{|}' for c in set_d: self.assertEqual(c.encode('utf7'), c.encode('ascii')) - self.assertEqual(c.encode('ascii').decode('utf7'), c) + self.assertEqual(c.encode('ascii').decode('utf7'), unicode(c)) + self.assertTrue(c == c.encode('ascii').decode('utf7')) for c in set_o: - self.assertEqual(c.encode('ascii').decode('utf7'), c) + self.assertEqual(c.encode('ascii').decode('utf7'), unicode(c)) + self.assertTrue(c == c.encode('ascii').decode('utf7')) def test_codecs_utf8(self): self.assertEqual(u''.encode('utf-8'), '') @@ -1364,7 +1378,7 @@ return u'__unicode__ overridden' u = U(u'xxx') self.assertEqual("%s" % u, u'__unicode__ overridden') - self.assertEqual("{}".format(u), u'__unicode__ overridden') + self.assertEqual("{}".format(u), '__unicode__ overridden') def test_main(): Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Mon Sep 13 10:53:00 2010 @@ -337,6 +337,8 @@ Tests ----- +- Strengthen test_unicode with explicit type checking for assertEqual tests. + - Issue #8857: Provide a test case for socket.getaddrinfo. - Issue #7564: Skip test_ioctl if another process is attached to /dev/tty. From python-checkins at python.org Mon Sep 13 16:16:52 2010 From: python-checkins at python.org (antoine.pitrou) Date: Mon, 13 Sep 2010 16:16:52 +0200 (CEST) Subject: [Python-checkins] r84774 - in python/branches/py3k: Include/ceval.h Misc/NEWS Python/ceval.c Python/ceval_gil.h Python/pythonrun.c Message-ID: <20100913141652.6B23BFAEF@mail.python.org> Author: antoine.pitrou Date: Mon Sep 13 16:16:46 2010 New Revision: 84774 Log: Issue #9828: Destroy the GIL in Py_Finalize(), so that it gets properly re-created on a subsequent call to Py_Initialize(). The problem (a crash) wouldn't appear in 3.1 or 2.7 where the GIL's structure is more trivial. Modified: python/branches/py3k/Include/ceval.h python/branches/py3k/Misc/NEWS python/branches/py3k/Python/ceval.c python/branches/py3k/Python/ceval_gil.h python/branches/py3k/Python/pythonrun.c Modified: python/branches/py3k/Include/ceval.h ============================================================================== --- python/branches/py3k/Include/ceval.h (original) +++ python/branches/py3k/Include/ceval.h Mon Sep 13 16:16:46 2010 @@ -160,6 +160,7 @@ PyAPI_FUNC(int) PyEval_ThreadsInitialized(void); PyAPI_FUNC(void) PyEval_InitThreads(void); +PyAPI_FUNC(void) _PyEval_FiniThreads(void); PyAPI_FUNC(void) PyEval_AcquireLock(void); PyAPI_FUNC(void) PyEval_ReleaseLock(void); PyAPI_FUNC(void) PyEval_AcquireThread(PyThreadState *tstate); Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 13 16:16:46 2010 @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #9828: Destroy the GIL in Py_Finalize(), so that it gets properly + re-created on a subsequent call to Py_Initialize(). The problem (a crash) + wouldn't appear in 3.1 or 2.7 where the GIL's structure is more trivial. + - Issue #9210: Configure option --with-wctype-functions was removed. Using the functions from the libc caused the methods .upper() and lower() to become locale aware and created subtly wrong results. Modified: python/branches/py3k/Python/ceval.c ============================================================================== --- python/branches/py3k/Python/ceval.c (original) +++ python/branches/py3k/Python/ceval.c Mon Sep 13 16:16:46 2010 @@ -313,6 +313,15 @@ } void +_PyEval_FiniThreads(void) +{ + if (!gil_created()) + return; + destroy_gil(); + assert(!gil_created()); +} + +void PyEval_AcquireLock(void) { PyThreadState *tstate = PyThreadState_GET(); @@ -368,10 +377,6 @@ if (!gil_created()) return; - /*XXX Can't use PyThread_free_lock here because it does too - much error-checking. Doing this cleanly would require - adding a new function to each thread_*.h. Instead, just - create a new lock and waste a little bit of memory */ recreate_gil(); pending_lock = PyThread_allocate_lock(); take_gil(tstate); Modified: python/branches/py3k/Python/ceval_gil.h ============================================================================== --- python/branches/py3k/Python/ceval_gil.h (original) +++ python/branches/py3k/Python/ceval_gil.h Mon Sep 13 16:16:46 2010 @@ -95,6 +95,9 @@ #define MUTEX_INIT(mut) \ if (pthread_mutex_init(&mut, NULL)) { \ Py_FatalError("pthread_mutex_init(" #mut ") failed"); }; +#define MUTEX_FINI(mut) \ + if (pthread_mutex_destroy(&mut)) { \ + Py_FatalError("pthread_mutex_destroy(" #mut ") failed"); }; #define MUTEX_LOCK(mut) \ if (pthread_mutex_lock(&mut)) { \ Py_FatalError("pthread_mutex_lock(" #mut ") failed"); }; @@ -106,6 +109,9 @@ #define COND_INIT(cond) \ if (pthread_cond_init(&cond, NULL)) { \ Py_FatalError("pthread_cond_init(" #cond ") failed"); }; +#define COND_FINI(cond) \ + if (pthread_cond_destroy(&cond)) { \ + Py_FatalError("pthread_cond_destroy(" #cond ") failed"); }; #define COND_SIGNAL(cond) \ if (pthread_cond_signal(&cond)) { \ Py_FatalError("pthread_cond_signal(" #cond ") failed"); }; @@ -305,9 +311,24 @@ _Py_atomic_store_explicit(&gil_locked, 0, _Py_memory_order_release); } +static void destroy_gil(void) +{ + MUTEX_FINI(gil_mutex); +#ifdef FORCE_SWITCHING + MUTEX_FINI(switch_mutex); +#endif + COND_FINI(gil_cond); +#ifdef FORCE_SWITCHING + COND_FINI(switch_cond); +#endif + _Py_atomic_store_explicit(&gil_locked, -1, _Py_memory_order_release); + _Py_ANNOTATE_RWLOCK_DESTROY(&gil_locked); +} + static void recreate_gil(void) { _Py_ANNOTATE_RWLOCK_DESTROY(&gil_locked); + /* XXX should we destroy the old OS resources here? */ create_gil(); } Modified: python/branches/py3k/Python/pythonrun.c ============================================================================== --- python/branches/py3k/Python/pythonrun.c (original) +++ python/branches/py3k/Python/pythonrun.c Mon Sep 13 16:16:46 2010 @@ -514,6 +514,10 @@ PyGrammar_RemoveAccelerators(&_PyParser_Grammar); +#ifdef WITH_THREAD + _PyEval_FiniThreads(); +#endif + #ifdef Py_TRACE_REFS /* Display addresses (& refcnts) of all objects still alive. * An address can be used to find the repr of the object, printed From python-checkins at python.org Mon Sep 13 16:18:44 2010 From: python-checkins at python.org (barry.warsaw) Date: Mon, 13 Sep 2010 16:18:44 +0200 (CEST) Subject: [Python-checkins] r84775 - peps/trunk/pep-3149.txt Message-ID: <20100913141844.28C78FB1E@mail.python.org> Author: barry.warsaw Date: Mon Sep 13 16:18:43 2010 New Revision: 84775 Log: Rephrase and emphasize inapplicability to Windows Modified: peps/trunk/pep-3149.txt Modified: peps/trunk/pep-3149.txt ============================================================================== --- peps/trunk/pep-3149.txt (original) +++ peps/trunk/pep-3149.txt Mon Sep 13 16:18:43 2010 @@ -68,15 +68,15 @@ with Python 2.6 being the default. In order to share as much as possible between the available Python -versions, these distributions install third party (i.e. non-standard -library) packages into `/usr/share/pyshared` and symlink to them from -`/usr/lib/pythonX.Y/dist-packages`. The symlinks exist because in a -pre-PEP 3147 world (i.e < Python 3.2), the `.pyc` files resulting from -byte compilation by the various installed Pythons will name collide -with each other. For Python versions >= 3.2, all pure-Python packages -can be shared, because the `.pyc` files will no longer cause file -system naming conflicts. Eliminating these symlinks makes for a -simpler, more robust Python distribution. +versions, these distributions install third party package modules +(``.pyc`` and ``.so`` files) into `/usr/share/pyshared` and symlink to +them from `/usr/lib/pythonX.Y/dist-packages`. The symlinks exist +because in a pre-PEP 3147 world (i.e < Python 3.2), the `.pyc` files +resulting from byte compilation by the various installed Pythons will +name collide with each other. For Python versions >= 3.2, all +pure-Python packages can be shared, because the `.pyc` files will no +longer cause file system naming conflicts. Eliminating these symlinks +makes for a simpler, more robust Python distribution. A similar situation arises with shared library extensions. Because extension modules are typically named `foo.so` for a `foo` extension @@ -126,11 +126,6 @@ When the other two flags are also enabled, the file names would be ``foo.cpython-32dmu.so``. -(This PEP only addresses build issues on POSIX systems that use the -``configure`` script. While Windows or other platform support is not -explicitly disallowed under this PEP, platform expertise is needed in -order to evaluate, describe, and implement support on such platforms.) - The shared library file name tag is used unconditionally; it cannot be changed. The tag and extension module suffix are available through the ``sysconfig`` modules via the following variables:: @@ -144,11 +139,16 @@ platform extension for shared library files, and is the exact suffix added to the extension module name. -For an arbitrary package `foo`, you would see these files when the +For an arbitrary package `foo`, you might see these files when the distribution package was installed:: - /usr/share/pyshared/foo.cpython-32m.so - /usr/share/pyshared/foo.cpython-33m.so + /usr/lib/python/foo.cpython-32m.so + /usr/lib/python/foo.cpython-33m.so + +(These paths are for example purposes only. Distributions are free to +use whatever filesystem layout they choose, and nothing in this PEP +changes the locations where from-source builds of Python are +installed.) Python's dynamic module loader will recognize and import shared library extension modules with a tag that matches its build-time @@ -173,6 +173,17 @@ major and minor version in the `.dll` file name. +Windows +======= + +This PEP only addresses build issues on POSIX systems that use the +``configure`` script. While Windows or other platform support is not +explicitly disallowed under this PEP, platform expertise is needed in +order to evaluate, describe, and implement support on such platforms. +It is not currently clear that the facilities in this PEP are even +useful for Windows. + + PEP 384 ======= From python-checkins at python.org Mon Sep 13 18:35:02 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 13 Sep 2010 18:35:02 +0200 (CEST) Subject: [Python-checkins] r84776 - in python/branches/py3k: Lib/test/regrtest.py Misc/NEWS Message-ID: <20100913163502.8CD8DFC2E@mail.python.org> Author: florent.xicluna Date: Mon Sep 13 18:35:02 2010 New Revision: 84776 Log: Make test.regrtest.__file__ absolute, this was not always the case when running profile or trace, for example. (issue #9323) Modified: python/branches/py3k/Lib/test/regrtest.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/test/regrtest.py ============================================================================== --- python/branches/py3k/Lib/test/regrtest.py (original) +++ python/branches/py3k/Lib/test/regrtest.py Mon Sep 13 18:35:02 2010 @@ -1458,7 +1458,13 @@ return self.expected if __name__ == '__main__': - # Simplification for findtestdir(). + # findtestdir() gets the dirname out of __file__, so we have to make it + # absolute before changing the working directory. + # For example __file__ may be relative when running trace or profile. + # See issue #9323. + __file__ = os.path.abspath(__file__) + + # sanity check assert __file__ == os.path.abspath(sys.argv[0]) # When tests are run from the Python build directory, it is best practice Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 13 18:35:02 2010 @@ -142,6 +142,12 @@ as wide (UCS4) unicode builds for both the host interpreter (embedded inside gdb) and the interpreter under test. +Tests +----- + +- Issue #9323: Make test.regrtest.__file__ absolute, this was not always the + case when running profile or trace, for example. + Build ----- From python-checkins at python.org Mon Sep 13 18:45:02 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Mon, 13 Sep 2010 18:45:02 +0200 (CEST) Subject: [Python-checkins] r84777 - in python/branches/release27-maint: Lib/test/test_trace.py Lib/test/tracedmodules Lib/test/tracedmodules/__init__.py Lib/test/tracedmodules/testmod.py Lib/trace.py Misc/NEWS Message-ID: <20100913164502.45C03EE99A@mail.python.org> Author: alexander.belopolsky Date: Mon Sep 13 18:45:02 2010 New Revision: 84777 Log: Issue #9315: Fix for the trace module to record correct class name when tracing methods. Unit tests. Patch by Eli Bendersky. Added: python/branches/release27-maint/Lib/test/test_trace.py python/branches/release27-maint/Lib/test/tracedmodules/ python/branches/release27-maint/Lib/test/tracedmodules/__init__.py python/branches/release27-maint/Lib/test/tracedmodules/testmod.py Modified: python/branches/release27-maint/Lib/trace.py python/branches/release27-maint/Misc/NEWS Added: python/branches/release27-maint/Lib/test/test_trace.py ============================================================================== --- (empty file) +++ python/branches/release27-maint/Lib/test/test_trace.py Mon Sep 13 18:45:02 2010 @@ -0,0 +1,320 @@ +import os +import sys +from test.test_support import (run_unittest, TESTFN, rmtree, unlink, + captured_stdout) +import unittest + +import trace +from trace import CoverageResults, Trace + +from test.tracedmodules import testmod + + +#------------------------------- Utilities -----------------------------------# + +def fix_ext_py(filename): + """Given a .pyc/.pyo filename converts it to the appropriate .py""" + if filename.endswith(('.pyc', '.pyo')): + filename = filename[:-1] + return filename + +def my_file_and_modname(): + """The .py file and module name of this file (__file__)""" + modname = os.path.splitext(os.path.basename(__file__))[0] + return fix_ext_py(__file__), modname + +def get_firstlineno(func): + return func.__code__.co_firstlineno + +#-------------------- Target functions for tracing ---------------------------# +# +# The relative line numbers of lines in these functions matter for verifying +# tracing. Please modify the appropriate tests if you change one of the +# functions. Absolute line numbers don't matter. +# + +def traced_func_linear(x, y): + a = x + b = y + c = a + b + return c + +def traced_func_loop(x, y): + c = x + for i in range(5): + c += y + return c + +def traced_func_importing(x, y): + return x + y + testmod.func(1) + +def traced_func_simple_caller(x): + c = traced_func_linear(x, x) + return c + x + +def traced_func_importing_caller(x): + k = traced_func_simple_caller(x) + k += traced_func_importing(k, x) + return k + +def traced_func_generator(num): + c = 5 # executed once + for i in range(num): + yield i + c + +def traced_func_calling_generator(): + k = 0 + for i in traced_func_generator(10): + k += i + +def traced_doubler(num): + return num * 2 + +def traced_caller_list_comprehension(): + k = 10 + mylist = [traced_doubler(i) for i in range(k)] + return mylist + + +class TracedClass(object): + def __init__(self, x): + self.a = x + + def inst_method_linear(self, y): + return self.a + y + + def inst_method_calling(self, x): + c = self.inst_method_linear(x) + return c + traced_func_linear(x, c) + + @classmethod + def class_method_linear(cls, y): + return y * 2 + + @staticmethod + def static_method_linear(y): + return y * 2 + + +#------------------------------ Test cases -----------------------------------# + + +class TestLineCounts(unittest.TestCase): + """White-box testing of line-counting, via runfunc""" + def setUp(self): + self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) + self.my_py_filename = fix_ext_py(__file__) + + def test_traced_func_linear(self): + result = self.tracer.runfunc(traced_func_linear, 2, 5) + self.assertEqual(result, 7) + + # all lines are executed once + expected = {} + firstlineno = get_firstlineno(traced_func_linear) + for i in range(1, 5): + expected[(self.my_py_filename, firstlineno + i)] = 1 + + self.assertEqual(self.tracer.results().counts, expected) + + def test_traced_func_loop(self): + self.tracer.runfunc(traced_func_loop, 2, 3) + + firstlineno = get_firstlineno(traced_func_loop) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + (self.my_py_filename, firstlineno + 2): 6, + (self.my_py_filename, firstlineno + 3): 5, + (self.my_py_filename, firstlineno + 4): 1, + } + self.assertEqual(self.tracer.results().counts, expected) + + def test_traced_func_importing(self): + self.tracer.runfunc(traced_func_importing, 2, 5) + + firstlineno = get_firstlineno(traced_func_importing) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + (fix_ext_py(testmod.__file__), 2): 1, + (fix_ext_py(testmod.__file__), 3): 1, + } + + self.assertEqual(self.tracer.results().counts, expected) + + def test_trace_func_generator(self): + self.tracer.runfunc(traced_func_calling_generator) + + firstlineno_calling = get_firstlineno(traced_func_calling_generator) + firstlineno_gen = get_firstlineno(traced_func_generator) + expected = { + (self.my_py_filename, firstlineno_calling + 1): 1, + (self.my_py_filename, firstlineno_calling + 2): 11, + (self.my_py_filename, firstlineno_calling + 3): 10, + (self.my_py_filename, firstlineno_gen + 1): 1, + (self.my_py_filename, firstlineno_gen + 2): 11, + (self.my_py_filename, firstlineno_gen + 3): 10, + } + self.assertEqual(self.tracer.results().counts, expected) + + def test_trace_list_comprehension(self): + self.tracer.runfunc(traced_caller_list_comprehension) + + firstlineno_calling = get_firstlineno(traced_caller_list_comprehension) + firstlineno_called = get_firstlineno(traced_doubler) + expected = { + (self.my_py_filename, firstlineno_calling + 1): 1, + (self.my_py_filename, firstlineno_calling + 2): 11, + (self.my_py_filename, firstlineno_calling + 3): 1, + (self.my_py_filename, firstlineno_called + 1): 10, + } + self.assertEqual(self.tracer.results().counts, expected) + + + def test_linear_methods(self): + # XXX todo: later add 'static_method_linear' and 'class_method_linear' + # here, once issue1764286 is resolved + # + for methname in ['inst_method_linear',]: + tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) + traced_obj = TracedClass(25) + method = getattr(traced_obj, methname) + tracer.runfunc(method, 20) + + firstlineno = get_firstlineno(method) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + } + self.assertEqual(tracer.results().counts, expected) + + +class TestRunExecCounts(unittest.TestCase): + """A simple sanity test of line-counting, via runctx (exec)""" + def setUp(self): + self.my_py_filename = fix_ext_py(__file__) + + def test_exec_counts(self): + self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) + code = r'''traced_func_loop(2, 5)''' + code = compile(code, __file__, 'exec') + self.tracer.runctx(code, globals(), vars()) + + firstlineno = get_firstlineno(traced_func_loop) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + (self.my_py_filename, firstlineno + 2): 6, + (self.my_py_filename, firstlineno + 3): 5, + (self.my_py_filename, firstlineno + 4): 1, + } + + # When used through 'run', some other spurios counts are produced, like + # the settrace of threading, which we ignore, just making sure that the + # counts fo traced_func_loop were right. + # + for k in expected.keys(): + self.assertEqual(self.tracer.results().counts[k], expected[k]) + + +class TestFuncs(unittest.TestCase): + """White-box testing of funcs tracing""" + def setUp(self): + self.tracer = Trace(count=0, trace=0, countfuncs=1) + self.filemod = my_file_and_modname() + self.maxDiff = None + def test_simple_caller(self): + self.tracer.runfunc(traced_func_simple_caller, 1) + + expected = { + self.filemod + ('traced_func_simple_caller',): 1, + self.filemod + ('traced_func_linear',): 1, + } + self.assertEqual(self.tracer.results().calledfuncs, expected) + + def test_loop_caller_importing(self): + self.tracer.runfunc(traced_func_importing_caller, 1) + + expected = { + self.filemod + ('traced_func_simple_caller',): 1, + self.filemod + ('traced_func_linear',): 1, + self.filemod + ('traced_func_importing_caller',): 1, + self.filemod + ('traced_func_importing',): 1, + (fix_ext_py(testmod.__file__), 'testmod', 'func'): 1, + } + self.assertEqual(self.tracer.results().calledfuncs, expected) + + def test_inst_method_calling(self): + obj = TracedClass(20) + self.tracer.runfunc(obj.inst_method_calling, 1) + + expected = { + self.filemod + ('TracedClass.inst_method_calling',): 1, + self.filemod + ('TracedClass.inst_method_linear',): 1, + self.filemod + ('traced_func_linear',): 1, + } + self.assertEqual(self.tracer.results().calledfuncs, expected) + + +class TestCallers(unittest.TestCase): + """White-box testing of callers tracing""" + def setUp(self): + self.tracer = Trace(count=0, trace=0, countcallers=1) + self.filemod = my_file_and_modname() + + def test_loop_caller_importing(self): + self.tracer.runfunc(traced_func_importing_caller, 1) + + expected = { + ((os.path.splitext(trace.__file__)[0] + '.py', 'trace', 'Trace.runfunc'), + (self.filemod + ('traced_func_importing_caller',))): 1, + ((self.filemod + ('traced_func_simple_caller',)), + (self.filemod + ('traced_func_linear',))): 1, + ((self.filemod + ('traced_func_importing_caller',)), + (self.filemod + ('traced_func_simple_caller',))): 1, + ((self.filemod + ('traced_func_importing_caller',)), + (self.filemod + ('traced_func_importing',))): 1, + ((self.filemod + ('traced_func_importing',)), + (fix_ext_py(testmod.__file__), 'testmod', 'func')): 1, + } + self.assertEqual(self.tracer.results().callers, expected) + + +# Created separately for issue #3821 +class TestCoverage(unittest.TestCase): + def tearDown(self): + rmtree(TESTFN) + unlink(TESTFN) + + def _coverage(self, tracer): + tracer.run('from test import test_pprint; test_pprint.test_main()') + r = tracer.results() + r.write_results(show_missing=True, summary=True, coverdir=TESTFN) + + def test_coverage(self): + tracer = trace.Trace(trace=0, count=1) + with captured_stdout() as stdout: + self._coverage(tracer) + stdout = stdout.getvalue() + self.assertTrue("pprint.py" in stdout) + self.assertTrue("case.py" in stdout) # from unittest + files = os.listdir(TESTFN) + self.assertTrue("pprint.cover" in files) + self.assertTrue("unittest.case.cover" in files) + + def test_coverage_ignore(self): + # Ignore all files, nothing should be traced nor printed + libpath = os.path.normpath(os.path.dirname(os.__file__)) + # sys.prefix does not work when running from a checkout + tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix, libpath], + trace=0, count=1) + with captured_stdout() as stdout: + self._coverage(tracer) + if os.path.exists(TESTFN): + files = os.listdir(TESTFN) + self.assertEquals(files, []) + + +def test_main(): + run_unittest(__name__) + + +if __name__ == '__main__': + test_main() Added: python/branches/release27-maint/Lib/test/tracedmodules/__init__.py ============================================================================== --- (empty file) +++ python/branches/release27-maint/Lib/test/tracedmodules/__init__.py Mon Sep 13 18:45:02 2010 @@ -0,0 +1,4 @@ +"""This package contains modules that help testing the trace.py module. Note +that the exact location of functions in these modules is important, as trace.py +takes the real line numbers into account. +""" Added: python/branches/release27-maint/Lib/test/tracedmodules/testmod.py ============================================================================== --- (empty file) +++ python/branches/release27-maint/Lib/test/tracedmodules/testmod.py Mon Sep 13 18:45:02 2010 @@ -0,0 +1,3 @@ +def func(x): + b = x + 1 + return b + 2 Modified: python/branches/release27-maint/Lib/trace.py ============================================================================== --- python/branches/release27-maint/Lib/trace.py (original) +++ python/branches/release27-maint/Lib/trace.py Mon Sep 13 18:45:02 2010 @@ -56,7 +56,7 @@ import time import token import tokenize -import types +import inspect import gc try: @@ -398,7 +398,7 @@ # and check the constants for references to other code objects for c in code.co_consts: - if isinstance(c, types.CodeType): + if inspect.iscode(c): # find another code object, so recurse into it linenos.update(find_lines(c, strs)) return linenos @@ -545,7 +545,7 @@ ## use of gc.get_referrers() was suggested by Michael Hudson # all functions which refer to this code object funcs = [f for f in gc.get_referrers(code) - if hasattr(f, "func_doc")] + if inspect.isfunction(f)] # require len(func) == 1 to avoid ambiguity caused by calls to # new.function(): "In the face of ambiguity, refuse the # temptation to guess." @@ -557,17 +557,13 @@ if hasattr(c, "__bases__")] if len(classes) == 1: # ditto for new.classobj() - clsname = str(classes[0]) + clsname = classes[0].__name__ # cache the result - assumption is that new.* is # not called later to disturb this relationship # _caller_cache could be flushed if functions in # the new module get called. self._caller_cache[code] = clsname if clsname is not None: - # final hack - module name shows up in str(cls), but we've already - # computed module name, so remove it - clsname = clsname.split(".")[1:] - clsname = ".".join(clsname) funcname = "%s.%s" % (clsname, funcname) return filename, modulename, funcname Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Mon Sep 13 18:45:02 2010 @@ -255,6 +255,9 @@ - Issue #9164: Ensure sysconfig handles dupblice archs while building on OSX +- Issue #9315: Fix for the trace module to record correct class name + for tracing methods. + Extension Modules ----------------- @@ -337,6 +340,8 @@ Tests ----- +- Issue #9315: Added tests for the trace module. Patch by Eli Bendersky. + - Strengthen test_unicode with explicit type checking for assertEqual tests. - Issue #8857: Provide a test case for socket.getaddrinfo. From python-checkins at python.org Mon Sep 13 19:29:12 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 13 Sep 2010 19:29:12 +0200 (CEST) Subject: [Python-checkins] r84778 - python/branches/release27-maint Message-ID: <20100913172912.24BD6EE98D@mail.python.org> Author: florent.xicluna Date: Mon Sep 13 19:29:11 2010 New Revision: 84778 Log: Unblocked revisions 83524 via svnmerge ........ r83524 | georg.brandl | 2010-08-02 14:20:23 +0200 (lun., 02 ao?t 2010) | 1 line #9428: fix running scripts from profile/cProfile with their own name and the right namespace. Same fix as for trace.py in #1690103. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Mon Sep 13 19:36:36 2010 From: python-checkins at python.org (florent.xicluna) Date: Mon, 13 Sep 2010 19:36:36 +0200 (CEST) Subject: [Python-checkins] r84779 - in python/branches/release27-maint: Lib/cProfile.py Lib/profile.py Lib/test/regrtest.py Misc/NEWS Message-ID: <20100913173636.D9F8EF955@mail.python.org> Author: florent.xicluna Date: Mon Sep 13 19:36:36 2010 New Revision: 84779 Log: Merged revisions 83524,84776 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83524 | georg.brandl | 2010-08-02 14:20:23 +0200 (lun., 02 ao?t 2010) | 1 line #9428: fix running scripts from profile/cProfile with their own name and the right namespace. Same fix as for trace.py in #1690103. ........ r84776 | florent.xicluna | 2010-09-13 18:35:02 +0200 (lun., 13 sept. 2010) | 1 line Make test.regrtest.__file__ absolute, this was not always the case when running profile or trace, for example. (issue #9323) ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/cProfile.py python/branches/release27-maint/Lib/profile.py python/branches/release27-maint/Lib/test/regrtest.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/cProfile.py ============================================================================== --- python/branches/release27-maint/Lib/cProfile.py (original) +++ python/branches/release27-maint/Lib/cProfile.py Mon Sep 13 19:36:36 2010 @@ -36,7 +36,7 @@ result = prof.print_stats(sort) return result -def runctx(statement, globals, locals, filename=None): +def runctx(statement, globals, locals, filename=None, sort=-1): """Run statement under profiler, supplying your own globals and locals, optionally saving results in filename. @@ -53,7 +53,7 @@ if filename is not None: prof.dump_stats(filename) else: - result = prof.print_stats() + result = prof.print_stats(sort) return result # Backwards compatibility. @@ -169,7 +169,8 @@ parser.add_option('-o', '--outfile', dest="outfile", help="Save stats to ", default=None) parser.add_option('-s', '--sort', dest="sort", - help="Sort order when printing to stdout, based on pstats.Stats class", default=-1) + help="Sort order when printing to stdout, based on pstats.Stats class", + default=-1) if not sys.argv[1:]: parser.print_usage() @@ -178,9 +179,17 @@ (options, args) = parser.parse_args() sys.argv[:] = args - if (len(sys.argv) > 0): - sys.path.insert(0, os.path.dirname(sys.argv[0])) - run('execfile(%r)' % (sys.argv[0],), options.outfile, options.sort) + if len(args) > 0: + progname = args[0] + sys.path.insert(0, os.path.dirname(progname)) + with open(progname, 'rb') as fp: + code = compile(fp.read(), progname, 'exec') + globs = { + '__file__': progname, + '__name__': '__main__', + '__package__': None, + } + runctx(code, globs, None, options.outfile, options.sort) else: parser.print_usage() return parser Modified: python/branches/release27-maint/Lib/profile.py ============================================================================== --- python/branches/release27-maint/Lib/profile.py (original) +++ python/branches/release27-maint/Lib/profile.py Mon Sep 13 19:36:36 2010 @@ -75,7 +75,7 @@ else: return prof.print_stats(sort) -def runctx(statement, globals, locals, filename=None): +def runctx(statement, globals, locals, filename=None, sort=-1): """Run statement under profiler, supplying your own globals and locals, optionally saving results in filename. @@ -90,7 +90,7 @@ if filename is not None: prof.dump_stats(filename) else: - return prof.print_stats() + return prof.print_stats(sort) # Backwards compatibility. def help(): @@ -589,18 +589,27 @@ parser.add_option('-o', '--outfile', dest="outfile", help="Save stats to ", default=None) parser.add_option('-s', '--sort', dest="sort", - help="Sort order when printing to stdout, based on pstats.Stats class", default=-1) + help="Sort order when printing to stdout, based on pstats.Stats class", + default=-1) if not sys.argv[1:]: parser.print_usage() sys.exit(2) (options, args) = parser.parse_args() + sys.argv[:] = args - if (len(args) > 0): - sys.argv[:] = args - sys.path.insert(0, os.path.dirname(sys.argv[0])) - run('execfile(%r)' % (sys.argv[0],), options.outfile, options.sort) + if len(args) > 0: + progname = args[0] + sys.path.insert(0, os.path.dirname(progname)) + with open(progname, 'rb') as fp: + code = compile(fp.read(), progname, 'exec') + globs = { + '__file__': progname, + '__name__': '__main__', + '__package__': None, + } + runctx(code, globs, None, options.outfile, options.sort) else: parser.print_usage() return parser Modified: python/branches/release27-maint/Lib/test/regrtest.py ============================================================================== --- python/branches/release27-maint/Lib/test/regrtest.py (original) +++ python/branches/release27-maint/Lib/test/regrtest.py Mon Sep 13 19:36:36 2010 @@ -1501,7 +1501,13 @@ return self.expected if __name__ == '__main__': - # Simplification for findtestdir(). + # findtestdir() gets the dirname out of __file__, so we have to make it + # absolute before changing the working directory. + # For example __file__ may be relative when running trace or profile. + # See issue #9323. + __file__ = os.path.abspath(__file__) + + # sanity check assert __file__ == os.path.abspath(sys.argv[0]) # When tests are run from the Python build directory, it is best practice Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Mon Sep 13 19:36:36 2010 @@ -151,6 +151,9 @@ - Issue #9354: Provide getsockopt() in asyncore's file_wrapper. +- Issue #9428: Fix running scripts with the profile/cProfile modules from + the command line. + - Issue #7781: Fix restricting stats by entry counts in the pstats interactive browser. @@ -340,8 +343,11 @@ Tests ----- +- Issue #9323: Make test.regrtest.__file__ absolute, this was not always the + case when running profile or trace, for example. + - Issue #9315: Added tests for the trace module. Patch by Eli Bendersky. - + - Strengthen test_unicode with explicit type checking for assertEqual tests. - Issue #8857: Provide a test case for socket.getaddrinfo. From python-checkins at python.org Mon Sep 13 20:14:34 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Mon, 13 Sep 2010 20:14:34 +0200 (CEST) Subject: [Python-checkins] r84780 - in python/branches/py3k: Lib/test/test_trace.py Lib/trace.py Misc/NEWS Message-ID: <20100913181434.64524FC16@mail.python.org> Author: alexander.belopolsky Date: Mon Sep 13 20:14:34 2010 New Revision: 84780 Log: Issue #9315: Fix for the trace module to record correct class name when tracing methods. Unit tests. Patch by Eli Bendersky. Modified: python/branches/py3k/Lib/test/test_trace.py python/branches/py3k/Lib/trace.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/test/test_trace.py ============================================================================== --- python/branches/py3k/Lib/test/test_trace.py (original) +++ python/branches/py3k/Lib/test/test_trace.py Mon Sep 13 20:14:34 2010 @@ -1,10 +1,286 @@ -# Testing the trace module - -from test.support import run_unittest, TESTFN, rmtree, unlink, captured_stdout +import os +import sys +from test.support import (run_unittest, TESTFN, rmtree, unlink, + captured_stdout) import unittest + import trace -import os, sys +from trace import CoverageResults, Trace + +from test.tracedmodules import testmod + + +#------------------------------- Utilities -----------------------------------# + +def fix_ext_py(filename): + """Given a .pyc/.pyo filename converts it to the appropriate .py""" + if filename.endswith(('.pyc', '.pyo')): + filename = filename[:-1] + return filename + +def my_file_and_modname(): + """The .py file and module name of this file (__file__)""" + modname = os.path.splitext(os.path.basename(__file__))[0] + return fix_ext_py(__file__), modname + +def get_firstlineno(func): + return func.__code__.co_firstlineno + +#-------------------- Target functions for tracing ---------------------------# +# +# The relative line numbers of lines in these functions matter for verifying +# tracing. Please modify the appropriate tests if you change one of the +# functions. Absolute line numbers don't matter. +# + +def traced_func_linear(x, y): + a = x + b = y + c = a + b + return c + +def traced_func_loop(x, y): + c = x + for i in range(5): + c += y + return c + +def traced_func_importing(x, y): + return x + y + testmod.func(1) + +def traced_func_simple_caller(x): + c = traced_func_linear(x, x) + return c + x + +def traced_func_importing_caller(x): + k = traced_func_simple_caller(x) + k += traced_func_importing(k, x) + return k + +def traced_func_generator(num): + c = 5 # executed once + for i in range(num): + yield i + c + +def traced_func_calling_generator(): + k = 0 + for i in traced_func_generator(10): + k += i + +def traced_doubler(num): + return num * 2 + +def traced_caller_list_comprehension(): + k = 10 + mylist = [traced_doubler(i) for i in range(k)] + return mylist + + +class TracedClass(object): + def __init__(self, x): + self.a = x + + def inst_method_linear(self, y): + return self.a + y + + def inst_method_calling(self, x): + c = self.inst_method_linear(x) + return c + traced_func_linear(x, c) + + @classmethod + def class_method_linear(cls, y): + return y * 2 + + @staticmethod + def static_method_linear(y): + return y * 2 + + +#------------------------------ Test cases -----------------------------------# + + +class TestLineCounts(unittest.TestCase): + """White-box testing of line-counting, via runfunc""" + def setUp(self): + self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) + self.my_py_filename = fix_ext_py(__file__) + self.maxDiff = None + + def test_traced_func_linear(self): + result = self.tracer.runfunc(traced_func_linear, 2, 5) + self.assertEqual(result, 7) + + # all lines are executed once + expected = {} + firstlineno = get_firstlineno(traced_func_linear) + for i in range(1, 5): + expected[(self.my_py_filename, firstlineno + i)] = 1 + + self.assertEqual(self.tracer.results().counts, expected) + def test_traced_func_loop(self): + self.tracer.runfunc(traced_func_loop, 2, 3) + + firstlineno = get_firstlineno(traced_func_loop) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + (self.my_py_filename, firstlineno + 2): 6, + (self.my_py_filename, firstlineno + 3): 5, + (self.my_py_filename, firstlineno + 4): 1, + } + self.assertEqual(self.tracer.results().counts, expected) + + def test_traced_func_importing(self): + self.tracer.runfunc(traced_func_importing, 2, 5) + + firstlineno = get_firstlineno(traced_func_importing) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + (fix_ext_py(testmod.__file__), 2): 1, + (fix_ext_py(testmod.__file__), 3): 1, + } + + self.assertEqual(self.tracer.results().counts, expected) + + def test_trace_func_generator(self): + self.tracer.runfunc(traced_func_calling_generator) + + firstlineno_calling = get_firstlineno(traced_func_calling_generator) + firstlineno_gen = get_firstlineno(traced_func_generator) + expected = { + (self.my_py_filename, firstlineno_calling + 1): 1, + (self.my_py_filename, firstlineno_calling + 2): 11, + (self.my_py_filename, firstlineno_calling + 3): 10, + (self.my_py_filename, firstlineno_gen + 1): 1, + (self.my_py_filename, firstlineno_gen + 2): 11, + (self.my_py_filename, firstlineno_gen + 3): 10, + } + self.assertEqual(self.tracer.results().counts, expected) + + def test_trace_list_comprehension(self): + self.tracer.runfunc(traced_caller_list_comprehension) + + firstlineno_calling = get_firstlineno(traced_caller_list_comprehension) + firstlineno_called = get_firstlineno(traced_doubler) + expected = { + (self.my_py_filename, firstlineno_calling + 1): 1, + # List compehentions work differently in 3.x, so the count + # below changed compared to 2.x. + (self.my_py_filename, firstlineno_calling + 2): 12, + (self.my_py_filename, firstlineno_calling + 3): 1, + (self.my_py_filename, firstlineno_called + 1): 10, + } + self.assertEqual(self.tracer.results().counts, expected) + + + def test_linear_methods(self): + # XXX todo: later add 'static_method_linear' and 'class_method_linear' + # here, once issue1764286 is resolved + # + for methname in ['inst_method_linear',]: + tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) + traced_obj = TracedClass(25) + method = getattr(traced_obj, methname) + tracer.runfunc(method, 20) + + firstlineno = get_firstlineno(method) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + } + self.assertEqual(tracer.results().counts, expected) + + +class TestRunExecCounts(unittest.TestCase): + """A simple sanity test of line-counting, via runctx (exec)""" + def setUp(self): + self.my_py_filename = fix_ext_py(__file__) + + def test_exec_counts(self): + self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) + code = r'''traced_func_loop(2, 5)''' + code = compile(code, __file__, 'exec') + self.tracer.runctx(code, globals(), vars()) + + firstlineno = get_firstlineno(traced_func_loop) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + (self.my_py_filename, firstlineno + 2): 6, + (self.my_py_filename, firstlineno + 3): 5, + (self.my_py_filename, firstlineno + 4): 1, + } + + # When used through 'run', some other spurios counts are produced, like + # the settrace of threading, which we ignore, just making sure that the + # counts fo traced_func_loop were right. + # + for k in expected.keys(): + self.assertEqual(self.tracer.results().counts[k], expected[k]) + + +class TestFuncs(unittest.TestCase): + """White-box testing of funcs tracing""" + def setUp(self): + self.tracer = Trace(count=0, trace=0, countfuncs=1) + self.filemod = my_file_and_modname() + + def test_simple_caller(self): + self.tracer.runfunc(traced_func_simple_caller, 1) + + expected = { + self.filemod + ('traced_func_simple_caller',): 1, + self.filemod + ('traced_func_linear',): 1, + } + self.assertEqual(self.tracer.results().calledfuncs, expected) + + def test_loop_caller_importing(self): + self.tracer.runfunc(traced_func_importing_caller, 1) + + expected = { + self.filemod + ('traced_func_simple_caller',): 1, + self.filemod + ('traced_func_linear',): 1, + self.filemod + ('traced_func_importing_caller',): 1, + self.filemod + ('traced_func_importing',): 1, + (fix_ext_py(testmod.__file__), 'testmod', 'func'): 1, + } + self.assertEqual(self.tracer.results().calledfuncs, expected) + + def test_inst_method_calling(self): + obj = TracedClass(20) + self.tracer.runfunc(obj.inst_method_calling, 1) + + expected = { + self.filemod + ('TracedClass.inst_method_calling',): 1, + self.filemod + ('TracedClass.inst_method_linear',): 1, + self.filemod + ('traced_func_linear',): 1, + } + self.assertEqual(self.tracer.results().calledfuncs, expected) + + +class TestCallers(unittest.TestCase): + """White-box testing of callers tracing""" + def setUp(self): + self.tracer = Trace(count=0, trace=0, countcallers=1) + self.filemod = my_file_and_modname() + + def test_loop_caller_importing(self): + self.tracer.runfunc(traced_func_importing_caller, 1) + + expected = { + ((os.path.splitext(trace.__file__)[0] + '.py', 'trace', 'Trace.runfunc'), + (self.filemod + ('traced_func_importing_caller',))): 1, + ((self.filemod + ('traced_func_simple_caller',)), + (self.filemod + ('traced_func_linear',))): 1, + ((self.filemod + ('traced_func_importing_caller',)), + (self.filemod + ('traced_func_simple_caller',))): 1, + ((self.filemod + ('traced_func_importing_caller',)), + (self.filemod + ('traced_func_importing',))): 1, + ((self.filemod + ('traced_func_importing',)), + (fix_ext_py(testmod.__file__), 'testmod', 'func')): 1, + } + self.assertEqual(self.tracer.results().callers, expected) + + +# Created separately for issue #3821 class TestCoverage(unittest.TestCase): def tearDown(self): rmtree(TESTFN) @@ -34,7 +310,6 @@ trace=0, count=1) with captured_stdout() as stdout: self._coverage(tracer) - self.assertEquals(stdout.getvalue(), "") if os.path.exists(TESTFN): files = os.listdir(TESTFN) self.assertEquals(files, []) @@ -43,5 +318,6 @@ def test_main(): run_unittest(__name__) -if __name__ == "__main__": + +if __name__ == '__main__': test_main() Modified: python/branches/py3k/Lib/trace.py ============================================================================== --- python/branches/py3k/Lib/trace.py (original) +++ python/branches/py3k/Lib/trace.py Mon Sep 13 20:14:34 2010 @@ -57,7 +57,7 @@ import time import token import tokenize -import types +import inspect import gc import pickle @@ -395,7 +395,7 @@ # and check the constants for references to other code objects for c in code.co_consts: - if isinstance(c, types.CodeType): + if inspect.iscode(c): # find another code object, so recurse into it linenos.update(find_lines(c, strs)) return linenos @@ -544,7 +544,7 @@ ## use of gc.get_referrers() was suggested by Michael Hudson # all functions which refer to this code object funcs = [f for f in gc.get_referrers(code) - if hasattr(f, "__doc__")] + if inspect.isfunction(f)] # require len(func) == 1 to avoid ambiguity caused by calls to # new.function(): "In the face of ambiguity, refuse the # temptation to guess." @@ -556,17 +556,13 @@ if hasattr(c, "__bases__")] if len(classes) == 1: # ditto for new.classobj() - clsname = str(classes[0]) + clsname = classes[0].__name__ # cache the result - assumption is that new.* is # not called later to disturb this relationship # _caller_cache could be flushed if functions in # the new module get called. self._caller_cache[code] = clsname if clsname is not None: - # final hack - module name shows up in str(cls), but we've already - # computed module name, so remove it - clsname = clsname.split(".")[1:] - clsname = ".".join(clsname) funcname = "%s.%s" % (clsname, funcname) return filename, modulename, funcname Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 13 20:14:34 2010 @@ -145,6 +145,8 @@ Tests ----- +- Issue #9315: Added tests for the trace module. Patch by Eli Bendersky. + - Issue #9323: Make test.regrtest.__file__ absolute, this was not always the case when running profile or trace, for example. @@ -2070,6 +2072,9 @@ - Issue #8235: _socket: Add the constant ``SO_SETFIB``. SO_SETFIB is a socket option available on FreeBSD 7.1 and newer. +- Issue #9315: Fix for the trace module to record correct class name + for tracing methods. + Extension Modules ----------------- From python-checkins at python.org Mon Sep 13 20:15:33 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Mon, 13 Sep 2010 20:15:33 +0200 (CEST) Subject: [Python-checkins] r84781 - python/branches/py3k/Lib/test/test_trace.py Message-ID: <20100913181533.5748AEE991@mail.python.org> Author: alexander.belopolsky Date: Mon Sep 13 20:15:33 2010 New Revision: 84781 Log: Removed debugging setting Modified: python/branches/py3k/Lib/test/test_trace.py Modified: python/branches/py3k/Lib/test/test_trace.py ============================================================================== --- python/branches/py3k/Lib/test/test_trace.py (original) +++ python/branches/py3k/Lib/test/test_trace.py Mon Sep 13 20:15:33 2010 @@ -104,7 +104,6 @@ def setUp(self): self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) self.my_py_filename = fix_ext_py(__file__) - self.maxDiff = None def test_traced_func_linear(self): result = self.tracer.runfunc(traced_func_linear, 2, 5) From python-checkins at python.org Mon Sep 13 20:16:55 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Mon, 13 Sep 2010 20:16:55 +0200 (CEST) Subject: [Python-checkins] r84782 - python/branches/release27-maint/Lib/test/test_trace.py Message-ID: <20100913181655.81C70EE9AE@mail.python.org> Author: alexander.belopolsky Date: Mon Sep 13 20:16:55 2010 New Revision: 84782 Log: Removed debugging setting Modified: python/branches/release27-maint/Lib/test/test_trace.py Modified: python/branches/release27-maint/Lib/test/test_trace.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_trace.py (original) +++ python/branches/release27-maint/Lib/test/test_trace.py Mon Sep 13 20:16:55 2010 @@ -219,7 +219,7 @@ def setUp(self): self.tracer = Trace(count=0, trace=0, countfuncs=1) self.filemod = my_file_and_modname() - self.maxDiff = None + def test_simple_caller(self): self.tracer.runfunc(traced_func_simple_caller, 1) From python-checkins at python.org Mon Sep 13 20:38:54 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Mon, 13 Sep 2010 20:38:54 +0200 (CEST) Subject: [Python-checkins] r84783 - in python/branches/release31-maint: Lib/test/test_trace.py Lib/test/tracedmodules Lib/test/tracedmodules/__init__.py Lib/test/tracedmodules/testmod.py Lib/trace.py Misc/NEWS Message-ID: <20100913183854.B967AEE9B4@mail.python.org> Author: alexander.belopolsky Date: Mon Sep 13 20:38:54 2010 New Revision: 84783 Log: Merged revisions 84780-84781 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k with some manual fixes ........ r84780 | alexander.belopolsky | 2010-09-13 14:14:34 -0400 (Mon, 13 Sep 2010) | 3 lines Issue #9315: Fix for the trace module to record correct class name when tracing methods. Unit tests. Patch by Eli Bendersky. ........ r84781 | alexander.belopolsky | 2010-09-13 14:15:33 -0400 (Mon, 13 Sep 2010) | 1 line Removed debugging setting ........ Added: python/branches/release31-maint/Lib/test/test_trace.py python/branches/release31-maint/Lib/test/tracedmodules/ python/branches/release31-maint/Lib/test/tracedmodules/__init__.py python/branches/release31-maint/Lib/test/tracedmodules/testmod.py Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/trace.py python/branches/release31-maint/Misc/NEWS Added: python/branches/release31-maint/Lib/test/test_trace.py ============================================================================== --- (empty file) +++ python/branches/release31-maint/Lib/test/test_trace.py Mon Sep 13 20:38:54 2010 @@ -0,0 +1,322 @@ +import os +import sys +from test.support import (run_unittest, TESTFN, rmtree, unlink, + captured_stdout) +import unittest + +import trace +from trace import CoverageResults, Trace + +from test.tracedmodules import testmod + + +#------------------------------- Utilities -----------------------------------# + +def fix_ext_py(filename): + """Given a .pyc/.pyo filename converts it to the appropriate .py""" + if filename.endswith(('.pyc', '.pyo')): + filename = filename[:-1] + return filename + +def my_file_and_modname(): + """The .py file and module name of this file (__file__)""" + modname = os.path.splitext(os.path.basename(__file__))[0] + return fix_ext_py(__file__), modname + +def get_firstlineno(func): + return func.__code__.co_firstlineno + +#-------------------- Target functions for tracing ---------------------------# +# +# The relative line numbers of lines in these functions matter for verifying +# tracing. Please modify the appropriate tests if you change one of the +# functions. Absolute line numbers don't matter. +# + +def traced_func_linear(x, y): + a = x + b = y + c = a + b + return c + +def traced_func_loop(x, y): + c = x + for i in range(5): + c += y + return c + +def traced_func_importing(x, y): + return x + y + testmod.func(1) + +def traced_func_simple_caller(x): + c = traced_func_linear(x, x) + return c + x + +def traced_func_importing_caller(x): + k = traced_func_simple_caller(x) + k += traced_func_importing(k, x) + return k + +def traced_func_generator(num): + c = 5 # executed once + for i in range(num): + yield i + c + +def traced_func_calling_generator(): + k = 0 + for i in traced_func_generator(10): + k += i + +def traced_doubler(num): + return num * 2 + +def traced_caller_list_comprehension(): + k = 10 + mylist = [traced_doubler(i) for i in range(k)] + return mylist + + +class TracedClass(object): + def __init__(self, x): + self.a = x + + def inst_method_linear(self, y): + return self.a + y + + def inst_method_calling(self, x): + c = self.inst_method_linear(x) + return c + traced_func_linear(x, c) + + @classmethod + def class_method_linear(cls, y): + return y * 2 + + @staticmethod + def static_method_linear(y): + return y * 2 + + +#------------------------------ Test cases -----------------------------------# + + +class TestLineCounts(unittest.TestCase): + """White-box testing of line-counting, via runfunc""" + def setUp(self): + self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) + self.my_py_filename = fix_ext_py(__file__) + + def test_traced_func_linear(self): + result = self.tracer.runfunc(traced_func_linear, 2, 5) + self.assertEqual(result, 7) + + # all lines are executed once + expected = {} + firstlineno = get_firstlineno(traced_func_linear) + for i in range(1, 5): + expected[(self.my_py_filename, firstlineno + i)] = 1 + + self.assertEqual(self.tracer.results().counts, expected) + + def test_traced_func_loop(self): + self.tracer.runfunc(traced_func_loop, 2, 3) + + firstlineno = get_firstlineno(traced_func_loop) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + (self.my_py_filename, firstlineno + 2): 6, + (self.my_py_filename, firstlineno + 3): 5, + (self.my_py_filename, firstlineno + 4): 1, + } + self.assertEqual(self.tracer.results().counts, expected) + + def test_traced_func_importing(self): + self.tracer.runfunc(traced_func_importing, 2, 5) + + firstlineno = get_firstlineno(traced_func_importing) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + (fix_ext_py(testmod.__file__), 2): 1, + (fix_ext_py(testmod.__file__), 3): 1, + } + + self.assertEqual(self.tracer.results().counts, expected) + + def test_trace_func_generator(self): + self.tracer.runfunc(traced_func_calling_generator) + + firstlineno_calling = get_firstlineno(traced_func_calling_generator) + firstlineno_gen = get_firstlineno(traced_func_generator) + expected = { + (self.my_py_filename, firstlineno_calling + 1): 1, + (self.my_py_filename, firstlineno_calling + 2): 11, + (self.my_py_filename, firstlineno_calling + 3): 10, + (self.my_py_filename, firstlineno_gen + 1): 1, + (self.my_py_filename, firstlineno_gen + 2): 11, + (self.my_py_filename, firstlineno_gen + 3): 10, + } + self.assertEqual(self.tracer.results().counts, expected) + + def test_trace_list_comprehension(self): + self.tracer.runfunc(traced_caller_list_comprehension) + + firstlineno_calling = get_firstlineno(traced_caller_list_comprehension) + firstlineno_called = get_firstlineno(traced_doubler) + expected = { + (self.my_py_filename, firstlineno_calling + 1): 1, + # List compehentions work differently in 3.x, so the count + # below changed compared to 2.x. + (self.my_py_filename, firstlineno_calling + 2): 12, + (self.my_py_filename, firstlineno_calling + 3): 1, + (self.my_py_filename, firstlineno_called + 1): 10, + } + self.assertEqual(self.tracer.results().counts, expected) + + + def test_linear_methods(self): + # XXX todo: later add 'static_method_linear' and 'class_method_linear' + # here, once issue1764286 is resolved + # + for methname in ['inst_method_linear',]: + tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) + traced_obj = TracedClass(25) + method = getattr(traced_obj, methname) + tracer.runfunc(method, 20) + + firstlineno = get_firstlineno(method) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + } + self.assertEqual(tracer.results().counts, expected) + + +class TestRunExecCounts(unittest.TestCase): + """A simple sanity test of line-counting, via runctx (exec)""" + def setUp(self): + self.my_py_filename = fix_ext_py(__file__) + + def test_exec_counts(self): + self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) + code = r'''traced_func_loop(2, 5)''' + code = compile(code, __file__, 'exec') + self.tracer.runctx(code, globals(), vars()) + + firstlineno = get_firstlineno(traced_func_loop) + expected = { + (self.my_py_filename, firstlineno + 1): 1, + (self.my_py_filename, firstlineno + 2): 6, + (self.my_py_filename, firstlineno + 3): 5, + (self.my_py_filename, firstlineno + 4): 1, + } + + # When used through 'run', some other spurios counts are produced, like + # the settrace of threading, which we ignore, just making sure that the + # counts fo traced_func_loop were right. + # + for k in expected.keys(): + self.assertEqual(self.tracer.results().counts[k], expected[k]) + + +class TestFuncs(unittest.TestCase): + """White-box testing of funcs tracing""" + def setUp(self): + self.tracer = Trace(count=0, trace=0, countfuncs=1) + self.filemod = my_file_and_modname() + + def test_simple_caller(self): + self.tracer.runfunc(traced_func_simple_caller, 1) + + expected = { + self.filemod + ('traced_func_simple_caller',): 1, + self.filemod + ('traced_func_linear',): 1, + } + self.assertEqual(self.tracer.results().calledfuncs, expected) + + def test_loop_caller_importing(self): + self.tracer.runfunc(traced_func_importing_caller, 1) + + expected = { + self.filemod + ('traced_func_simple_caller',): 1, + self.filemod + ('traced_func_linear',): 1, + self.filemod + ('traced_func_importing_caller',): 1, + self.filemod + ('traced_func_importing',): 1, + (fix_ext_py(testmod.__file__), 'testmod', 'func'): 1, + } + self.assertEqual(self.tracer.results().calledfuncs, expected) + + def test_inst_method_calling(self): + obj = TracedClass(20) + self.tracer.runfunc(obj.inst_method_calling, 1) + + expected = { + self.filemod + ('TracedClass.inst_method_calling',): 1, + self.filemod + ('TracedClass.inst_method_linear',): 1, + self.filemod + ('traced_func_linear',): 1, + } + self.assertEqual(self.tracer.results().calledfuncs, expected) + + +class TestCallers(unittest.TestCase): + """White-box testing of callers tracing""" + def setUp(self): + self.tracer = Trace(count=0, trace=0, countcallers=1) + self.filemod = my_file_and_modname() + + def test_loop_caller_importing(self): + self.tracer.runfunc(traced_func_importing_caller, 1) + + expected = { + ((os.path.splitext(trace.__file__)[0] + '.py', 'trace', 'Trace.runfunc'), + (self.filemod + ('traced_func_importing_caller',))): 1, + ((self.filemod + ('traced_func_simple_caller',)), + (self.filemod + ('traced_func_linear',))): 1, + ((self.filemod + ('traced_func_importing_caller',)), + (self.filemod + ('traced_func_simple_caller',))): 1, + ((self.filemod + ('traced_func_importing_caller',)), + (self.filemod + ('traced_func_importing',))): 1, + ((self.filemod + ('traced_func_importing',)), + (fix_ext_py(testmod.__file__), 'testmod', 'func')): 1, + } + self.assertEqual(self.tracer.results().callers, expected) + + +# Created separately for issue #3821 +class TestCoverage(unittest.TestCase): + def tearDown(self): + rmtree(TESTFN) + unlink(TESTFN) + + def _coverage(self, tracer): + tracer.run('from test import test_pprint; test_pprint.test_main()') + r = tracer.results() + r.write_results(show_missing=True, summary=True, coverdir=TESTFN) + + def test_coverage(self): + tracer = trace.Trace(trace=0, count=1) + with captured_stdout() as stdout: + self._coverage(tracer) + stdout = stdout.getvalue() + self.assertTrue("pprint.py" in stdout) + self.assertTrue("unittest.py" in stdout) + files = os.listdir(TESTFN) + self.assertTrue("pprint.cover" in files) + self.assertTrue("unittest.cover" in files) + + def test_coverage_ignore(self): + # Ignore all files, nothing should be traced nor printed + libpath = os.path.normpath(os.path.dirname(os.__file__)) + # sys.prefix does not work when running from a checkout + tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix, libpath], + trace=0, count=1) + with captured_stdout() as stdout: + self._coverage(tracer) + if os.path.exists(TESTFN): + files = os.listdir(TESTFN) + self.assertEquals(files, []) + + +def test_main(): + run_unittest(__name__) + + +if __name__ == '__main__': + test_main() Added: python/branches/release31-maint/Lib/test/tracedmodules/__init__.py ============================================================================== --- (empty file) +++ python/branches/release31-maint/Lib/test/tracedmodules/__init__.py Mon Sep 13 20:38:54 2010 @@ -0,0 +1,9 @@ +"""This package contains modules that help testing the trace.py module. Note +that the exact location of functions in these modules is important, as trace.py +takes the real line numbers into account. +""" +"""This directory contains modules that help testing the trace.py module. Note +that the exact location of functions in these modules is important, as trace.py +takes the real line numbers into account. + +""" Added: python/branches/release31-maint/Lib/test/tracedmodules/testmod.py ============================================================================== --- (empty file) +++ python/branches/release31-maint/Lib/test/tracedmodules/testmod.py Mon Sep 13 20:38:54 2010 @@ -0,0 +1,3 @@ +def func(x): + b = x + 1 + return b + 2 Modified: python/branches/release31-maint/Lib/trace.py ============================================================================== --- python/branches/release31-maint/Lib/trace.py (original) +++ python/branches/release31-maint/Lib/trace.py Mon Sep 13 20:38:54 2010 @@ -57,7 +57,7 @@ import time import token import tokenize -import types +import inspect import gc import pickle @@ -395,7 +395,7 @@ # and check the constants for references to other code objects for c in code.co_consts: - if isinstance(c, types.CodeType): + if inspect.iscode(c): # find another code object, so recurse into it linenos.update(find_lines(c, strs)) return linenos @@ -544,7 +544,7 @@ ## use of gc.get_referrers() was suggested by Michael Hudson # all functions which refer to this code object funcs = [f for f in gc.get_referrers(code) - if hasattr(f, "__doc__")] + if inspect.isfunction(f)] # require len(func) == 1 to avoid ambiguity caused by calls to # new.function(): "In the face of ambiguity, refuse the # temptation to guess." @@ -556,17 +556,13 @@ if hasattr(c, "__bases__")] if len(classes) == 1: # ditto for new.classobj() - clsname = str(classes[0]) + clsname = classes[0].__name__ # cache the result - assumption is that new.* is # not called later to disturb this relationship # _caller_cache could be flushed if functions in # the new module get called. self._caller_cache[code] = clsname if clsname is not None: - # final hack - module name shows up in str(cls), but we've already - # computed module name, so remove it - clsname = clsname.split(".")[1:] - clsname = ".".join(clsname) funcname = "%s.%s" % (clsname, funcname) return filename, modulename, funcname Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Mon Sep 13 20:38:54 2010 @@ -584,6 +584,8 @@ Tests ----- +- Issue #9315: Added tests for the trace module. Patch by Eli Bendersky. + - Issue #7564: Skip test_ioctl if another process is attached to /dev/tty. - Issue #8857: Provide a test case for socket.getaddrinfo. @@ -1019,6 +1021,9 @@ - Issue #1068268: The subprocess module now handles EINTR in internal os.waitpid and os.read system calls where appropriate. +- Issue #9315: Fix for the trace module to record correct class name + for tracing methods. + Extension Modules ----------------- From python-checkins at python.org Mon Sep 13 21:41:37 2010 From: python-checkins at python.org (victor.stinner) Date: Mon, 13 Sep 2010 21:41:37 +0200 (CEST) Subject: [Python-checkins] r84784 - in python/branches/py3k: Lib/macpath.py Misc/NEWS Message-ID: <20100913194137.0B7EDEE988@mail.python.org> Author: victor.stinner Date: Mon Sep 13 21:41:36 2010 New Revision: 84784 Log: Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X (macpath module). Modified: python/branches/py3k/Lib/macpath.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/macpath.py ============================================================================== --- python/branches/py3k/Lib/macpath.py (original) +++ python/branches/py3k/Lib/macpath.py Mon Sep 13 21:41:36 2010 @@ -202,4 +202,4 @@ pass return path -supports_unicode_filenames = False +supports_unicode_filenames = True Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 13 21:41:36 2010 @@ -40,6 +40,9 @@ Library ------- +- Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X + (macpath module). + - Issue #9837: The read() method of ZipExtFile objects (as returned by ZipFile.open()) could return more bytes than requested. From python-checkins at python.org Mon Sep 13 22:02:39 2010 From: python-checkins at python.org (vinay.sajip) Date: Mon, 13 Sep 2010 22:02:39 +0200 (CEST) Subject: [Python-checkins] r84785 - python/branches/py3k/Doc/library/logging.rst Message-ID: <20100913200239.CA4F3EE997@mail.python.org> Author: vinay.sajip Date: Mon Sep 13 22:02:39 2010 New Revision: 84785 Log: Expanded QueueHandler documentation. Modified: python/branches/py3k/Doc/library/logging.rst Modified: python/branches/py3k/Doc/library/logging.rst ============================================================================== --- python/branches/py3k/Doc/library/logging.rst (original) +++ python/branches/py3k/Doc/library/logging.rst Mon Sep 13 22:02:39 2010 @@ -2605,7 +2605,9 @@ .. class:: QueueHandler(queue) Returns a new instance of the :class:`QueueHandler` class. The instance is - initialized with the queue to send messages to. + initialized with the queue to send messages to. The queue can be any queue- + like object; it's passed as-is to the :meth:`enqueue` method, which needs + to know how to send messages to it. .. method:: emit(record) @@ -2623,6 +2625,37 @@ The :class:`QueueHandler` class was not present in previous versions. +.. _zeromq-handlers: + +You can use a :class:`QueueHandler` subclass to send messages to other kinds +of queues, for example a ZeroMQ "publish" socket. In the example below,the +socket is created separately and passed to the handler (as its 'queue'):: + + import zmq # using pyzmq, the Python binding for ZeroMQ + import json # for serializing records portably + + ctx = zmq.Context() + sock = zmq.Socket(ctx, zmq.PUB) # or zmq.PUSH, or other suitable value + sock.bind('tcp://*:5556') # or wherever + + class ZeroMQSocketHandler(QueueHandler): + def enqueue(self, record): + data = json.dumps(record.__dict__) + self.queue.send(data) + +Of course there are other ways of organizing this, for example passing in the +data needed by the handler to create the socket:: + + class ZeroMQSocketHandler(QueueHandler): + def __init__(self, uri, socktype=zmq.PUB, ctx=None): + self.ctx = ctx or zmq.Context() + socket = zmq.Socket(self.ctx, socktype) + super(ZeroMQSocketHandler, self).__init__(socket) + + def enqueue(self, record): + data = json.dumps(record.__dict__) + self.queue.send(data) + .. _formatter-objects: From python-checkins at python.org Mon Sep 13 22:28:57 2010 From: python-checkins at python.org (vinay.sajip) Date: Mon, 13 Sep 2010 22:28:57 +0200 (CEST) Subject: [Python-checkins] r84786 - python/branches/py3k/Lib/logging/__init__.py Message-ID: <20100913202857.B3DCFEE9D9@mail.python.org> Author: vinay.sajip Date: Mon Sep 13 22:28:57 2010 New Revision: 84786 Log: Removed unused, commented-out code. Modified: python/branches/py3k/Lib/logging/__init__.py Modified: python/branches/py3k/Lib/logging/__init__.py ============================================================================== --- python/branches/py3k/Lib/logging/__init__.py (original) +++ python/branches/py3k/Lib/logging/__init__.py Mon Sep 13 22:28:57 2010 @@ -1498,15 +1498,6 @@ else: return root -#def getRootLogger(): -# """ -# Return the root logger. -# -# Note that getLogger('') now does the same thing, so this function is -# deprecated and may disappear in the future. -# """ -# return root - def critical(msg, *args, **kwargs): """ Log a message with severity 'CRITICAL' on the root logger. From python-checkins at python.org Mon Sep 13 22:31:35 2010 From: python-checkins at python.org (victor.stinner) Date: Mon, 13 Sep 2010 22:31:35 +0200 (CEST) Subject: [Python-checkins] r84787 - in python/branches/release27-maint: Doc/library/os.path.rst Lib/macpath.py Misc/NEWS Message-ID: <20100913203135.0EA28EE9C4@mail.python.org> Author: victor.stinner Date: Mon Sep 13 22:31:34 2010 New Revision: 84787 Log: Merged revisions 84701,84784 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84701 | victor.stinner | 2010-09-11 02:22:12 +0200 (sam., 11 sept. 2010) | 5 lines Issue #767645: fix os.path.supports_unicode_filenames definition os.listdir(str) always returns unicode and it can return non-ascii filenames even if supports_unicode_filenames is False. ........ r84784 | victor.stinner | 2010-09-13 21:41:36 +0200 (lun., 13 sept. 2010) | 2 lines Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X (macpath module). ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/os.path.rst python/branches/release27-maint/Lib/macpath.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Doc/library/os.path.rst ============================================================================== --- python/branches/release27-maint/Doc/library/os.path.rst (original) +++ python/branches/release27-maint/Doc/library/os.path.rst Mon Sep 13 22:31:34 2010 @@ -337,8 +337,7 @@ .. data:: supports_unicode_filenames True if arbitrary Unicode strings can be used as file names (within limitations - imposed by the file system), and if :func:`os.listdir` returns Unicode strings - for a Unicode argument. + imposed by the file system). .. versionadded:: 2.3 Modified: python/branches/release27-maint/Lib/macpath.py ============================================================================== --- python/branches/release27-maint/Lib/macpath.py (original) +++ python/branches/release27-maint/Lib/macpath.py Mon Sep 13 22:31:34 2010 @@ -212,4 +212,4 @@ pass return path -supports_unicode_filenames = False +supports_unicode_filenames = True Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Mon Sep 13 22:31:34 2010 @@ -43,6 +43,9 @@ Library ------- +- Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X + (macpath module). + - Issue #9837: The read() method of ZipExtFile objects (as returned by ZipFile.open()) could return more bytes than requested. From python-checkins at python.org Mon Sep 13 22:37:50 2010 From: python-checkins at python.org (vinay.sajip) Date: Mon, 13 Sep 2010 22:37:50 +0200 (CEST) Subject: [Python-checkins] r84788 - python/branches/py3k/Doc/library/logging.rst Message-ID: <20100913203750.BA50AEE99C@mail.python.org> Author: vinay.sajip Date: Mon Sep 13 22:37:50 2010 New Revision: 84788 Log: Enhanced HTTPHandler documentation. Modified: python/branches/py3k/Doc/library/logging.rst Modified: python/branches/py3k/Doc/library/logging.rst ============================================================================== --- python/branches/py3k/Doc/library/logging.rst (original) +++ python/branches/py3k/Doc/library/logging.rst Mon Sep 13 22:37:50 2010 @@ -2578,12 +2578,16 @@ ``POST`` semantics. -.. class:: HTTPHandler(host, url, method='GET') +.. class:: HTTPHandler(host, url, method='GET', secure=False, credentials=None) - Returns a new instance of the :class:`HTTPHandler` class. The instance is - initialized with a host address, url and HTTP method. The *host* can be of the - form ``host:port``, should you need to use a specific port number. If no - *method* is specified, ``GET`` is used. + Returns a new instance of the :class:`HTTPHandler` class. The *host* can be + of the form ``host:port``, should you need to use a specific port number. + If no *method* is specified, ``GET`` is used. If *secure* is True, an HTTPS + connection will be used. If *credentials* is specified, it should be a + 2-tuple consisting of userid and password, which will be placed in an HTTP + 'Authorization' header using Basic authentication. If you specify + credentials, you should also specify secure=True so that your userid and + password are not passed in cleartext across the wire. .. method:: emit(record) From python-checkins at python.org Mon Sep 13 22:40:30 2010 From: python-checkins at python.org (vinay.sajip) Date: Mon, 13 Sep 2010 22:40:30 +0200 (CEST) Subject: [Python-checkins] r84789 - in python/branches/py3k: Lib/logging/handlers.py Misc/NEWS Message-ID: <20100913204030.69B50EE988@mail.python.org> Author: vinay.sajip Date: Mon Sep 13 22:40:30 2010 New Revision: 84789 Log: logging: enhanced HTTPHandler Modified: python/branches/py3k/Lib/logging/handlers.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/logging/handlers.py ============================================================================== --- python/branches/py3k/Lib/logging/handlers.py (original) +++ python/branches/py3k/Lib/logging/handlers.py Mon Sep 13 22:40:30 2010 @@ -977,7 +977,7 @@ A class which sends records to a Web server, using either GET or POST semantics. """ - def __init__(self, host, url, method="GET"): + def __init__(self, host, url, method="GET", secure=False, credentials=None): """ Initialize the instance with the host, the request URL, and the method ("GET" or "POST") @@ -989,12 +989,14 @@ self.host = host self.url = url self.method = method + self.secure = secure + self.credentials = credentials def mapLogRecord(self, record): """ Default implementation of mapping the log record into a dict that is sent as the CGI data. Overwrite in your class. - Contributed by Franz Glasner. + Contributed by Franz Glasner. """ return record.__dict__ @@ -1007,7 +1009,10 @@ try: import http.client, urllib.parse host = self.host - h = http.client.HTTP(host) + if self.secure: + h = http.client.HTTPSConnection(host) + else: + h = http.client.HTTPConnection(host) url = self.url data = urllib.parse.urlencode(self.mapLogRecord(record)) if self.method == "GET": @@ -1027,8 +1032,13 @@ h.putheader("Content-type", "application/x-www-form-urlencoded") h.putheader("Content-length", str(len(data))) + if self.credentials: + import base64 + s = ('u%s:%s' % self.credentials).encode('utf-8') + s = 'Basic ' + base64.b64encode(s).strip() + h.putheader('Authorization', s) h.endheaders(data if self.method == "POST" else None) - h.getreply() #can't do anything with the result + h.getresponse() #can't do anything with the result except (KeyboardInterrupt, SystemExit): raise except: Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 13 22:40:30 2010 @@ -40,6 +40,8 @@ Library ------- +- logging: Enhanced HTTPHandler with secure and credentials initializers. + - Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X (macpath module). From python-checkins at python.org Mon Sep 13 22:48:43 2010 From: python-checkins at python.org (eric.smith) Date: Mon, 13 Sep 2010 22:48:43 +0200 (CEST) Subject: [Python-checkins] r84790 - in python/branches/py3k: Lib/test/test_builtin.py Lib/test/test_unicode.py Misc/NEWS Objects/typeobject.c Message-ID: <20100913204843.B36C1F0F1@mail.python.org> Author: eric.smith Date: Mon Sep 13 22:48:43 2010 New Revision: 84790 Log: Issue 7994: Make object.__format__() raise a PendingDeprecationWarning if the format string is not empty. Manually merge r79596 and r84772 from 2.x. Also, apparently test_format() from test_builtin never made it into 3.x. I've added it as well. It tests the basic format() infrastructure. Modified: python/branches/py3k/Lib/test/test_builtin.py python/branches/py3k/Lib/test/test_unicode.py python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/typeobject.c Modified: python/branches/py3k/Lib/test/test_builtin.py ============================================================================== --- python/branches/py3k/Lib/test/test_builtin.py (original) +++ python/branches/py3k/Lib/test/test_builtin.py Mon Sep 13 22:48:43 2010 @@ -1279,6 +1279,116 @@ return i self.assertRaises(ValueError, list, zip(BadSeq(), BadSeq())) + def test_format(self): + # Test the basic machinery of the format() builtin. Don't test + # the specifics of the various formatters + self.assertEqual(format(3, ''), '3') + + # Returns some classes to use for various tests. There's + # an old-style version, and a new-style version + def classes_new(): + class A(object): + def __init__(self, x): + self.x = x + def __format__(self, format_spec): + return str(self.x) + format_spec + class DerivedFromA(A): + pass + + class Simple(object): pass + class DerivedFromSimple(Simple): + def __init__(self, x): + self.x = x + def __format__(self, format_spec): + return str(self.x) + format_spec + class DerivedFromSimple2(DerivedFromSimple): pass + return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2 + + def class_test(A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2): + self.assertEqual(format(A(3), 'spec'), '3spec') + self.assertEqual(format(DerivedFromA(4), 'spec'), '4spec') + self.assertEqual(format(DerivedFromSimple(5), 'abc'), '5abc') + self.assertEqual(format(DerivedFromSimple2(10), 'abcdef'), + '10abcdef') + + class_test(*classes_new()) + + def empty_format_spec(value): + # test that: + # format(x, '') == str(x) + # format(x) == str(x) + self.assertEqual(format(value, ""), str(value)) + self.assertEqual(format(value), str(value)) + + # for builtin types, format(x, "") == str(x) + empty_format_spec(17**13) + empty_format_spec(1.0) + empty_format_spec(3.1415e104) + empty_format_spec(-3.1415e104) + empty_format_spec(3.1415e-104) + empty_format_spec(-3.1415e-104) + empty_format_spec(object) + empty_format_spec(None) + + # TypeError because self.__format__ returns the wrong type + class BadFormatResult: + def __format__(self, format_spec): + return 1.0 + self.assertRaises(TypeError, format, BadFormatResult(), "") + + # TypeError because format_spec is not unicode or str + self.assertRaises(TypeError, format, object(), 4) + self.assertRaises(TypeError, format, object(), object()) + + # tests for object.__format__ really belong elsewhere, but + # there's no good place to put them + x = object().__format__('') + self.assertTrue(x.startswith('15s}'.format(G('data')), ' string is data') self.assertEqual('{0!s}'.format(G('data')), 'string is data') + msg = 'object.__format__ with a non-empty format string is deprecated' + with support.check_warnings((msg, PendingDeprecationWarning)): + self.assertEqual('{0:^10}'.format(E('data')), ' E(data) ') + self.assertEqual('{0:^10s}'.format(E('data')), ' E(data) ') + self.assertEqual('{0:>15s}'.format(G('data')), ' string is data') + self.assertEqual("{0:date: %Y-%m-%d}".format(I(year=2007, month=8, day=27)), Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 13 22:48:43 2010 @@ -10,6 +10,14 @@ Core and Builtins ----------------- +- Issue #7994: Issue a PendingDeprecationWarning if object.__format__ + is called with a non-empty format string. This is an effort to + future-proof user code. If a derived class does not currently + implement __format__ but later adds its own __format__, it would + most likely break user code that had supplied a format string. This + will be changed to a DeprecationWaring in Python 3.3 and it will be + an error in Python 3.4. + - Issue #9828: Destroy the GIL in Py_Finalize(), so that it gets properly re-created on a subsequent call to Py_Initialize(). The problem (a crash) wouldn't appear in 3.1 or 2.7 where the GIL's structure is more trivial. Modified: python/branches/py3k/Objects/typeobject.c ============================================================================== --- python/branches/py3k/Objects/typeobject.c (original) +++ python/branches/py3k/Objects/typeobject.c Mon Sep 13 22:48:43 2010 @@ -3315,9 +3315,26 @@ return NULL; self_as_str = PyObject_Str(self); - if (self_as_str != NULL) - result = PyObject_Format(self_as_str, format_spec); + if (self_as_str != NULL) { + /* Issue 7994: If we're converting to a string, we + should reject format specifications */ + if (PyUnicode_GET_SIZE(format_spec) > 0) { + if (PyErr_WarnEx(PyExc_PendingDeprecationWarning, + "object.__format__ with a non-empty format " + "string is deprecated", 1) < 0) { + goto done; + } + /* Eventually this will become an error: + PyErr_Format(PyExc_TypeError, + "non-empty format string passed to object.__format__"); + goto done; + */ + } + result = PyObject_Format(self_as_str, format_spec); + } + +done: Py_XDECREF(self_as_str); return result; From python-checkins at python.org Mon Sep 13 23:16:30 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Mon, 13 Sep 2010 23:16:30 +0200 (CEST) Subject: [Python-checkins] r84791 - in python/branches/py3k: Doc/library/stdtypes.rst Lib/test/test_builtin.py Misc/NEWS Objects/rangeobject.c Message-ID: <20100913211630.20F48EE9D9@mail.python.org> Author: daniel.stutzbach Date: Mon Sep 13 23:16:29 2010 New Revision: 84791 Log: Issue #9213: Add index and count methods to range objects, needed to meet the API of the collections.Sequence ABC. Modified: python/branches/py3k/Doc/library/stdtypes.rst python/branches/py3k/Lib/test/test_builtin.py python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/rangeobject.c Modified: python/branches/py3k/Doc/library/stdtypes.rst ============================================================================== --- python/branches/py3k/Doc/library/stdtypes.rst (original) +++ python/branches/py3k/Doc/library/stdtypes.rst Mon Sep 13 23:16:29 2010 @@ -1554,9 +1554,23 @@ object will always take the same amount of memory, no matter the size of the range it represents. There are no consistent performance advantages. -Range objects have very little behavior: they only support indexing, iteration, -and the :func:`len` function. +Range objects have relatively little behavior: they support indexing, +iteration, the :func:`len` function, and the following methods. +.. method:: range.count(x) + + Return the number of *i*'s for which ``s[i] == x``. Normally the + result will be 0 or 1, but it could be greater if *x* defines an + unusual equality function. + + .. versionadded:: 3.2 + +.. method:: range.index(x) + + Return the smallest *i* such that ``s[i] == x``. Raises + :exc:`ValueError` when *x* is not in the range. + + .. versionadded:: 3.2 .. _typesseq-mutable: Modified: python/branches/py3k/Lib/test/test_builtin.py ============================================================================== --- python/branches/py3k/Lib/test/test_builtin.py (original) +++ python/branches/py3k/Lib/test/test_builtin.py Mon Sep 13 23:16:29 2010 @@ -1028,6 +1028,60 @@ self.assertRaises(TypeError, range, 0.0, 0.0, 1) self.assertRaises(TypeError, range, 0.0, 0.0, 1.0) + self.assertEqual(range(3).count(-1), 0) + self.assertEqual(range(3).count(0), 1) + self.assertEqual(range(3).count(1), 1) + self.assertEqual(range(3).count(2), 1) + self.assertEqual(range(3).count(3), 0) + + self.assertEqual(range(10**20).count(1), 1) + self.assertEqual(range(10**20).count(10**20), 0) + self.assertEqual(range(3).index(1), 1) + self.assertEqual(range(1, 2**100, 2).count(2**87), 0) + self.assertEqual(range(1, 2**100, 2).count(2**87+1), 1) + + self.assertEqual(range(1, 10, 3).index(4), 1) + self.assertEqual(range(1, -10, -3).index(-5), 2) + + self.assertEqual(range(10**20).index(1), 1) + self.assertEqual(range(10**20).index(10**20 - 1), 10**20 - 1) + + self.assertRaises(ValueError, range(1, 2**100, 2).index, 2**87) + self.assertEqual(range(1, 2**100, 2).index(2**87+1), 2**86) + + class AlwaysEqual(object): + def __eq__(self, other): + return True + always_equal = AlwaysEqual() + self.assertEqual(range(10).count(always_equal), 10) + self.assertEqual(range(10).index(always_equal), 0) + + def test_range_index(self): + u = range(2) + self.assertEqual(u.index(0), 0) + self.assertEqual(u.index(1), 1) + self.assertRaises(ValueError, u.index, 2) + + u = range(-2, 3) + self.assertEqual(u.count(0), 1) + self.assertEqual(u.index(0), 2) + self.assertRaises(TypeError, u.index) + + class BadExc(Exception): + pass + + class BadCmp: + def __eq__(self, other): + if other == 2: + raise BadExc() + return False + + a = range(4) + self.assertRaises(BadExc, a.index, BadCmp()) + + a = range(-2, 3) + self.assertEqual(a.index(0), 2) + def test_input(self): self.write_testfile() fp = open(TESTFN, 'r') Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 13 23:16:29 2010 @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #9212: The range type_items now provides index() and count() + methods, to conform to the Sequence ABC. Patch by Daniel Urban and + Daniel Stutzbach. + - Issue #7994: Issue a PendingDeprecationWarning if object.__format__ is called with a non-empty format string. This is an effort to future-proof user code. If a derived class does not currently Modified: python/branches/py3k/Objects/rangeobject.c ============================================================================== --- python/branches/py3k/Objects/rangeobject.c (original) +++ python/branches/py3k/Objects/rangeobject.c Mon Sep 13 23:16:29 2010 @@ -273,60 +273,135 @@ r->start, r->stop, r->step); } +/* Assumes (PyLong_CheckExact(ob) || PyBool_Check(ob)) */ +static int +range_contains_long(rangeobject *r, PyObject *ob) +{ + int cmp1, cmp2, cmp3; + PyObject *tmp1 = NULL; + PyObject *tmp2 = NULL; + PyObject *zero = NULL; + int result = -1; + + zero = PyLong_FromLong(0); + if (zero == NULL) /* MemoryError in int(0) */ + goto end; + + /* Check if the value can possibly be in the range. */ + + cmp1 = PyObject_RichCompareBool(r->step, zero, Py_GT); + if (cmp1 == -1) + goto end; + if (cmp1 == 1) { /* positive steps: start <= ob < stop */ + cmp2 = PyObject_RichCompareBool(r->start, ob, Py_LE); + cmp3 = PyObject_RichCompareBool(ob, r->stop, Py_LT); + } + else { /* negative steps: stop < ob <= start */ + cmp2 = PyObject_RichCompareBool(ob, r->start, Py_LE); + cmp3 = PyObject_RichCompareBool(r->stop, ob, Py_LT); + } + + if (cmp2 == -1 || cmp3 == -1) /* TypeError */ + goto end; + if (cmp2 == 0 || cmp3 == 0) { /* ob outside of range */ + result = 0; + goto end; + } + + /* Check that the stride does not invalidate ob's membership. */ + tmp1 = PyNumber_Subtract(ob, r->start); + if (tmp1 == NULL) + goto end; + tmp2 = PyNumber_Remainder(tmp1, r->step); + if (tmp2 == NULL) + goto end; + /* result = (int(ob) - start % step) == 0 */ + result = PyObject_RichCompareBool(tmp2, zero, Py_EQ); + end: + Py_XDECREF(tmp1); + Py_XDECREF(tmp2); + Py_XDECREF(zero); + return result; +} + static int range_contains(rangeobject *r, PyObject *ob) { - if (PyLong_CheckExact(ob) || PyBool_Check(ob)) { - int cmp1, cmp2, cmp3; - PyObject *tmp1 = NULL; - PyObject *tmp2 = NULL; - PyObject *zero = NULL; - int result = -1; - - zero = PyLong_FromLong(0); - if (zero == NULL) /* MemoryError in int(0) */ - goto end; - - /* Check if the value can possibly be in the range. */ - - cmp1 = PyObject_RichCompareBool(r->step, zero, Py_GT); - if (cmp1 == -1) - goto end; - if (cmp1 == 1) { /* positive steps: start <= ob < stop */ - cmp2 = PyObject_RichCompareBool(r->start, ob, Py_LE); - cmp3 = PyObject_RichCompareBool(ob, r->stop, Py_LT); - } - else { /* negative steps: stop < ob <= start */ - cmp2 = PyObject_RichCompareBool(ob, r->start, Py_LE); - cmp3 = PyObject_RichCompareBool(r->stop, ob, Py_LT); - } - - if (cmp2 == -1 || cmp3 == -1) /* TypeError */ - goto end; - if (cmp2 == 0 || cmp3 == 0) { /* ob outside of range */ - result = 0; - goto end; - } - - /* Check that the stride does not invalidate ob's membership. */ - tmp1 = PyNumber_Subtract(ob, r->start); - if (tmp1 == NULL) - goto end; - tmp2 = PyNumber_Remainder(tmp1, r->step); - if (tmp2 == NULL) - goto end; - /* result = (int(ob) - start % step) == 0 */ - result = PyObject_RichCompareBool(tmp2, zero, Py_EQ); - end: - Py_XDECREF(tmp1); - Py_XDECREF(tmp2); - Py_XDECREF(zero); - return result; - } - /* Fall back to iterative search. */ + if (PyLong_CheckExact(ob) || PyBool_Check(ob)) + return range_contains_long(r, ob); + return (int)_PySequence_IterSearch((PyObject*)r, ob, PY_ITERSEARCH_CONTAINS); } +static PyObject * +range_count(rangeobject *r, PyObject *ob) +{ + if (PyLong_CheckExact(ob) || PyBool_Check(ob)) { + if (range_contains_long(r, ob)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } else { + Py_ssize_t count; + count = _PySequence_IterSearch((PyObject*)r, ob, PY_ITERSEARCH_COUNT); + if (count == -1) + return NULL; + return PyLong_FromSsize_t(count); + } +} + +static PyObject * +range_index(rangeobject *r, PyObject *ob) +{ + PyObject *idx, *tmp; + int contains; + PyObject *format_tuple, *err_string; + static PyObject *err_format = NULL; + + if (!PyLong_CheckExact(ob) && !PyBool_Check(ob)) { + Py_ssize_t index; + index = _PySequence_IterSearch((PyObject*)r, ob, PY_ITERSEARCH_INDEX); + if (index == -1) + return NULL; + return PyLong_FromSsize_t(index); + } + + contains = range_contains_long(r, ob); + if (contains == -1) + return NULL; + + if (!contains) + goto value_error; + + tmp = PyNumber_Subtract(ob, r->start); + if (tmp == NULL) + return NULL; + + /* idx = (ob - r.start) // r.step */ + idx = PyNumber_FloorDivide(tmp, r->step); + Py_DECREF(tmp); + return idx; + +value_error: + + /* object is not in the range */ + if (err_format == NULL) { + err_format = PyUnicode_FromString("%r is not in range"); + if (err_format == NULL) + return NULL; + } + format_tuple = PyTuple_Pack(1, ob); + if (format_tuple == NULL) + return NULL; + err_string = PyUnicode_Format(err_format, format_tuple); + Py_DECREF(format_tuple); + if (err_string == NULL) + return NULL; + PyErr_SetObject(PyExc_ValueError, err_string); + Py_DECREF(err_string); + return NULL; +} + static PySequenceMethods range_as_sequence = { (lenfunc)range_length, /* sq_length */ 0, /* sq_concat */ @@ -344,10 +419,18 @@ PyDoc_STRVAR(reverse_doc, "Returns a reverse iterator."); +PyDoc_STRVAR(count_doc, +"rangeobject.count(value) -> integer -- return number of occurrences of value"); + +PyDoc_STRVAR(index_doc, +"rangeobject.index(value, [start, [stop]]) -> integer -- return index of value.\n" +"Raises ValueError if the value is not present."); + static PyMethodDef range_methods[] = { - {"__reversed__", (PyCFunction)range_reverse, METH_NOARGS, - reverse_doc}, - {"__reduce__", (PyCFunction)range_reduce, METH_VARARGS}, + {"__reversed__", (PyCFunction)range_reverse, METH_NOARGS, reverse_doc}, + {"__reduce__", (PyCFunction)range_reduce, METH_VARARGS}, + {"count", (PyCFunction)range_count, METH_O, count_doc}, + {"index", (PyCFunction)range_index, METH_O, index_doc}, {NULL, NULL} /* sentinel */ }; From python-checkins at python.org Mon Sep 13 23:36:00 2010 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 13 Sep 2010 23:36:00 +0200 (CEST) Subject: [Python-checkins] r84792 - in python/branches/py3k: Doc/library/reprlib.rst Lib/collections.py Lib/reprlib.py Lib/test/test_reprlib.py Misc/NEWS Message-ID: <20100913213600.4FC73DD9A@mail.python.org> Author: raymond.hettinger Date: Mon Sep 13 23:36:00 2010 New Revision: 84792 Log: Issue 9840: Add reprlib.recursive_repr(), a decorator for handling recursive calls to __repr__ methods. Modified: python/branches/py3k/Doc/library/reprlib.rst python/branches/py3k/Lib/collections.py python/branches/py3k/Lib/reprlib.py python/branches/py3k/Lib/test/test_reprlib.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/reprlib.rst ============================================================================== --- python/branches/py3k/Doc/library/reprlib.rst (original) +++ python/branches/py3k/Doc/library/reprlib.rst Mon Sep 13 23:36:00 2010 @@ -34,6 +34,29 @@ similar to that returned by the built-in function of the same name, but with limits on most sizes. +In addition to size-limiting tools, the module also provides a decorator for +detecting recursive calls to :meth:`__repr__` and substituting a placeholder +string instead. + +.. decorator:: recursive_repr(fillvalue="...") + + Decorator for :meth:`__repr__` methods to detect recursive calls within the + same thread. If a recursive call is made, the *fillvalue* is returned, + otherwise, the usual :meth:`__repr__` call is made. For example: + + >>> class MyList(list): + ... @recursive_repr() + ... def __repr__(self): + ... return '<' + '|'.join(map(repr, self)) + '>' + ... + >>> m = MyList('abc') + >>> m.append(m) + >>> m.append('x') + >>> print(m) + <'a'|'b'|'c'|...|'x'> + + .. versionadded:: 3.2 + .. _repr-objects: Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Mon Sep 13 23:36:00 2010 @@ -13,6 +13,7 @@ import heapq as _heapq from weakref import proxy as _proxy from itertools import repeat as _repeat, chain as _chain, starmap as _starmap +from reprlib import recursive_repr as _recursive_repr ################################################################################ ### OrderedDict @@ -43,7 +44,6 @@ ''' if len(args) > 1: raise TypeError('expected at most 1 arguments, got %d' % len(args)) - self.__in_repr = False # detects recursive repr try: self.__root except AttributeError: @@ -97,10 +97,10 @@ def __reduce__(self): 'Return state information for pickling' items = [[k, self[k]] for k in self] - tmp = self.__map, self.__root, self.__in_repr - del self.__map, self.__root, self.__in_repr + tmp = self.__map, self.__root + del self.__map, self.__root inst_dict = vars(self).copy() - self.__map, self.__root, self.__in_repr = tmp + self.__map, self.__root = tmp if inst_dict: return (self.__class__, (items,), inst_dict) return self.__class__, (items,) @@ -167,18 +167,12 @@ items = MutableMapping.items __ne__ = MutableMapping.__ne__ + @_recursive_repr() def __repr__(self): 'od.__repr__() <==> repr(od)' if not self: return '%s()' % (self.__class__.__name__,) - if self.__in_repr: - return '...' - self.__in_repr = True - try: - result = '%s(%r)' % (self.__class__.__name__, list(self.items())) - finally: - self.__in_repr = False - return result + return '%s(%r)' % (self.__class__.__name__, list(self.items())) def copy(self): 'od.copy() -> a shallow copy of od' Modified: python/branches/py3k/Lib/reprlib.py ============================================================================== --- python/branches/py3k/Lib/reprlib.py (original) +++ python/branches/py3k/Lib/reprlib.py Mon Sep 13 23:36:00 2010 @@ -1,9 +1,38 @@ """Redo the builtin repr() (representation) but with limits on most sizes.""" -__all__ = ["Repr","repr"] +__all__ = ["Repr", "repr", "recursive_repr"] import builtins from itertools import islice +try: + from _thread import get_ident +except AttributeError: + from _dummy_thread import get_ident + +def recursive_repr(fillvalue='...'): + 'Decorator to make a repr function return fillvalue for a recursive call' + + def decorating_function(user_function): + repr_running = set() + + def wrapper(self): + key = id(self), get_ident() + if key in repr_running: + return fillvalue + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + + # Can't use functools.wraps() here because of bootstrap issues + wrapper.__module__ = getattr(user_function, '__module__') + wrapper.__doc__ = getattr(user_function, '__doc__') + wrapper.__name__ = getattr(user_function, '__name__') + return wrapper + + return decorating_function class Repr: Modified: python/branches/py3k/Lib/test/test_reprlib.py ============================================================================== --- python/branches/py3k/Lib/test/test_reprlib.py (original) +++ python/branches/py3k/Lib/test/test_reprlib.py Mon Sep 13 23:36:00 2010 @@ -11,6 +11,7 @@ from test.support import run_unittest from reprlib import repr as r # Don't shadow builtin repr from reprlib import Repr +from reprlib import recursive_repr def nestedTuple(nesting): @@ -301,10 +302,38 @@ def __repr__(self): raise Exception("This should be caught by Repr.repr_instance") +class MyContainer: + 'Helper class for TestRecursiveRepr' + def __init__(self, values): + self.values = list(values) + def append(self, value): + self.values.append(value) + @recursive_repr() + def __repr__(self): + return '<' + ', '.join(map(str, self.values)) + '>' + +class MyContainer2(MyContainer): + @recursive_repr('+++') + def __repr__(self): + return '<' + ', '.join(map(str, self.values)) + '>' + +class TestRecursiveRepr(unittest.TestCase): + def test_recursive_repr(self): + m = MyContainer(list('abcde')) + m.append(m) + m.append('x') + m.append(m) + self.assertEqual(repr(m), '') + m = MyContainer2(list('abcde')) + m.append(m) + m.append('x') + m.append(m) + self.assertEqual(repr(m), '') def test_main(): run_unittest(ReprTests) run_unittest(LongReprTest) + run_unittest(TestRecursiveRepr) if __name__ == "__main__": Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Sep 13 23:36:00 2010 @@ -52,6 +52,9 @@ Library ------- +- Issue 9840: Added a decorator to reprlib for wrapping __repr__ methods + to make them handle recursive calls within the same thread. + - logging: Enhanced HTTPHandler with secure and credentials initializers. - Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X From ncoghlan at gmail.com Mon Sep 13 23:50:34 2010 From: ncoghlan at gmail.com (Nick Coghlan) Date: Tue, 14 Sep 2010 07:50:34 +1000 Subject: [Python-checkins] r84792 - in python/branches/py3k: Doc/library/reprlib.rst Lib/collections.py Lib/reprlib.py Lib/test/test_reprlib.py Misc/NEWS In-Reply-To: <20100913213600.4FC73DD9A@mail.python.org> References: <20100913213600.4FC73DD9A@mail.python.org> Message-ID: On Tue, Sep 14, 2010 at 7:36 AM, raymond.hettinger wrote: > + ? ? ? ?# Can't use functools.wraps() here because of bootstrap issues > + ? ? ? ?wrapper.__module__ = getattr(user_function, '__module__') > + ? ? ? ?wrapper.__doc__ = getattr(user_function, '__doc__') > + ? ? ? ?wrapper.__name__ = getattr(user_function, '__name__') > + ? ? ? ?return wrapper Perhaps add __wrapped__ as well? (I assume that, similar to _collections before it was made a builtin, the bootstrap issue is that _functools is an extension module rather than builtin, but reprlib is needed when building the extension modules?) Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From python-checkins at python.org Tue Sep 14 00:14:37 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 14 Sep 2010 00:14:37 +0200 (CEST) Subject: [Python-checkins] r84793 - in python/branches/release27-maint: Lib/collections.py Lib/test/test_collections.py Misc/NEWS Message-ID: <20100913221437.13C67FBB1@mail.python.org> Author: raymond.hettinger Date: Tue Sep 14 00:14:36 2010 New Revision: 84793 Log: Issue 9826: OrderedDict.__repr__ did not play well with self-referencing dicts. Modified: python/branches/release27-maint/Lib/collections.py python/branches/release27-maint/Lib/test/test_collections.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/collections.py ============================================================================== --- python/branches/release27-maint/Lib/collections.py (original) +++ python/branches/release27-maint/Lib/collections.py Tue Sep 14 00:14:36 2010 @@ -12,6 +12,32 @@ import heapq as _heapq from itertools import repeat as _repeat, chain as _chain, starmap as _starmap, \ ifilter as _ifilter, imap as _imap +try: + from thread import get_ident +except AttributeError: + from dummy_thread import get_ident + +def _recursive_repr(user_function): + 'Decorator to make a repr function return "..." for a recursive call' + repr_running = set() + + def wrapper(self): + key = id(self), get_ident() + if key in repr_running: + return '...' + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + + # Can't use functools.wraps() here because of bootstrap issues + wrapper.__module__ = getattr(user_function, '__module__') + wrapper.__doc__ = getattr(user_function, '__doc__') + wrapper.__name__ = getattr(user_function, '__name__') + return wrapper + ################################################################################ ### OrderedDict @@ -142,6 +168,7 @@ value = self.pop(key) return key, value + @_recursive_repr def __repr__(self): 'od.__repr__() <==> repr(od)' if not self: Modified: python/branches/release27-maint/Lib/test/test_collections.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_collections.py (original) +++ python/branches/release27-maint/Lib/test/test_collections.py Tue Sep 14 00:14:36 2010 @@ -926,6 +926,13 @@ self.assertEqual(eval(repr(od)), od) self.assertEqual(repr(OrderedDict()), "OrderedDict()") + def test_repr_recursive(self): + # See issue #9826 + od = OrderedDict.fromkeys('abc') + od['x'] = od + self.assertEqual(repr(od), + "OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])") + def test_setdefault(self): pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] shuffle(pairs) Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Sep 14 00:14:36 2010 @@ -43,6 +43,9 @@ Library ------- +- Issue #9826: OrderedDict.__repr__ can now handle self-referential + values: d['x'] = d. + - Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X (macpath module). From python-checkins at python.org Tue Sep 14 03:11:36 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Tue, 14 Sep 2010 03:11:36 +0200 (CEST) Subject: [Python-checkins] r84794 - in python/branches/py3k/Lib/test/tracedmodules: __init__.py testmod.py Message-ID: <20100914011136.19A38FCD4@mail.python.org> Author: alexander.belopolsky Date: Tue Sep 14 03:11:35 2010 New Revision: 84794 Log: Added files missed in r84780. Thanks, Florent. Added: python/branches/py3k/Lib/test/tracedmodules/ python/branches/py3k/Lib/test/tracedmodules/__init__.py python/branches/py3k/Lib/test/tracedmodules/testmod.py Added: python/branches/py3k/Lib/test/tracedmodules/__init__.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/test/tracedmodules/__init__.py Tue Sep 14 03:11:35 2010 @@ -0,0 +1,9 @@ +"""This package contains modules that help testing the trace.py module. Note +that the exact location of functions in these modules is important, as trace.py +takes the real line numbers into account. +""" +"""This directory contains modules that help testing the trace.py module. Note +that the exact location of functions in these modules is important, as trace.py +takes the real line numbers into account. + +""" Added: python/branches/py3k/Lib/test/tracedmodules/testmod.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/test/tracedmodules/testmod.py Tue Sep 14 03:11:35 2010 @@ -0,0 +1,3 @@ +def func(x): + b = x + 1 + return b + 2 From solipsis at pitrou.net Tue Sep 14 05:09:05 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 14 Sep 2010 05:09:05 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84794): sum=0 Message-ID: py3k results for svn r84794 (hg cset f8003795a4dd) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflognzZHBI', '-x'] From python-checkins at python.org Tue Sep 14 08:59:24 2010 From: python-checkins at python.org (vinay.sajip) Date: Tue, 14 Sep 2010 08:59:24 +0200 (CEST) Subject: [Python-checkins] r84795 - python/branches/py3k/Doc/library/logging.rst Message-ID: <20100914065924.DED4FFE0A@mail.python.org> Author: vinay.sajip Date: Tue Sep 14 08:59:24 2010 New Revision: 84795 Log: Tidied example script. Modified: python/branches/py3k/Doc/library/logging.rst Modified: python/branches/py3k/Doc/library/logging.rst ============================================================================== --- python/branches/py3k/Doc/library/logging.rst (original) +++ python/branches/py3k/Doc/library/logging.rst Tue Sep 14 08:59:24 2010 @@ -2660,6 +2660,8 @@ data = json.dumps(record.__dict__) self.queue.send(data) + def close(self): + self.queue.close() .. _formatter-objects: From python-checkins at python.org Tue Sep 14 11:34:09 2010 From: python-checkins at python.org (vinay.sajip) Date: Tue, 14 Sep 2010 11:34:09 +0200 (CEST) Subject: [Python-checkins] r84796 - python/branches/py3k/Lib/test/test_logging.py Message-ID: <20100914093409.65AF0EE992@mail.python.org> Author: vinay.sajip Date: Tue Sep 14 11:34:09 2010 New Revision: 84796 Log: Added test for QueueHandler. Modified: python/branches/py3k/Lib/test/test_logging.py Modified: python/branches/py3k/Lib/test/test_logging.py ============================================================================== --- python/branches/py3k/Lib/test/test_logging.py (original) +++ python/branches/py3k/Lib/test/test_logging.py Tue Sep 14 11:34:09 2010 @@ -31,6 +31,7 @@ import gc import json import os +import queue import re import select import socket @@ -1760,6 +1761,35 @@ self.assertTrue(c2 is c3) +class QueueHandlerTest(BaseTest): + # Do not bother with a logger name group. + expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$" + + def setUp(self): + BaseTest.setUp(self) + self.queue = queue.Queue(-1) + self.que_hdlr = logging.handlers.QueueHandler(self.queue) + self.que_logger = logging.getLogger('que') + self.que_logger.propagate = False + self.que_logger.setLevel(logging.WARNING) + self.que_logger.addHandler(self.que_hdlr) + + def tearDown(self): + self.que_hdlr.close() + BaseTest.tearDown(self) + + def test_queue_handler(self): + self.que_logger.debug(self.next_message()) + self.assertRaises(queue.Empty, self.queue.get_nowait) + self.que_logger.info(self.next_message()) + self.assertRaises(queue.Empty, self.queue.get_nowait) + msg = self.next_message() + self.que_logger.warning(msg) + data = self.queue.get_nowait() + self.assertTrue(isinstance(data, logging.LogRecord)) + self.assertEqual(data.name, self.que_logger.name) + self.assertEqual((data.msg, data.args), (msg, None)) + # Set the locale to the platform-dependent default. I have no idea # why the test does this, but in any case we save the current locale # first and restore it at the end. @@ -1769,7 +1799,7 @@ CustomLevelsAndFiltersTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest, MemoryTest, EncodingTest, WarningsTest, ConfigDictTest, ManagerTest, - ChildLoggerTest) + ChildLoggerTest, QueueHandlerTest) if __name__ == "__main__": test_main() From python-checkins at python.org Tue Sep 14 11:42:39 2010 From: python-checkins at python.org (vinay.sajip) Date: Tue, 14 Sep 2010 11:42:39 +0200 (CEST) Subject: [Python-checkins] r84797 - python/branches/py3k/Doc/library/logging.rst Message-ID: <20100914094239.85FE3EE99A@mail.python.org> Author: vinay.sajip Date: Tue Sep 14 11:42:39 2010 New Revision: 84797 Log: Tidied example script. Modified: python/branches/py3k/Doc/library/logging.rst Modified: python/branches/py3k/Doc/library/logging.rst ============================================================================== --- python/branches/py3k/Doc/library/logging.rst (original) +++ python/branches/py3k/Doc/library/logging.rst Tue Sep 14 11:42:39 2010 @@ -2647,6 +2647,9 @@ data = json.dumps(record.__dict__) self.queue.send(data) + handler = ZeroMQSocketHandler(sock) + + Of course there are other ways of organizing this, for example passing in the data needed by the handler to create the socket:: @@ -2654,7 +2657,7 @@ def __init__(self, uri, socktype=zmq.PUB, ctx=None): self.ctx = ctx or zmq.Context() socket = zmq.Socket(self.ctx, socktype) - super(ZeroMQSocketHandler, self).__init__(socket) + QueueHandler.__init__(self, socket) def enqueue(self, record): data = json.dumps(record.__dict__) From python-checkins at python.org Tue Sep 14 11:48:39 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 11:48:39 +0200 (CEST) Subject: [Python-checkins] r84798 - in python/branches/py3k: Lib/test/test_gc.py Modules/gcmodule.c Message-ID: <20100914094839.B2A69FD20@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 11:48:39 2010 New Revision: 84798 Log: Do not print additional shutdown message when gc.DEBUG_SAVEALL is set Modified: python/branches/py3k/Lib/test/test_gc.py python/branches/py3k/Modules/gcmodule.c Modified: python/branches/py3k/Lib/test/test_gc.py ============================================================================== --- python/branches/py3k/Lib/test/test_gc.py (original) +++ python/branches/py3k/Lib/test/test_gc.py Tue Sep 14 11:48:39 2010 @@ -482,8 +482,7 @@ x.x = x x.y = X('second') del x - if %d: - gc.set_debug(gc.DEBUG_UNCOLLECTABLE) + gc.set_debug(%s) """ def run_command(code): p = subprocess.Popen([sys.executable, "-c", code], @@ -494,13 +493,19 @@ self.assertEqual(stdout.strip(), b"") return strip_python_stderr(stderr) - stderr = run_command(code % 0) + stderr = run_command(code % "0") self.assertIn(b"gc: 2 uncollectable objects at shutdown", stderr) self.assertNotIn(b"[, ]", stderr) # With DEBUG_UNCOLLECTABLE, the garbage list gets printed - stderr = run_command(code % 1) + stderr = run_command(code % "gc.DEBUG_UNCOLLECTABLE") self.assertIn(b"gc: 2 uncollectable objects at shutdown", stderr) self.assertIn(b"[, ]", stderr) + # With DEBUG_SAVEALL, no additional message should get printed + # (because gc.garbage also contains normally reclaimable cyclic + # references, and its elements get printed at runtime anyway). + stderr = run_command(code % "gc.DEBUG_SAVEALL") + self.assertNotIn(b"uncollectable objects at shutdown", stderr) + class GCTogglingTests(unittest.TestCase): def setUp(self): Modified: python/branches/py3k/Modules/gcmodule.c ============================================================================== --- python/branches/py3k/Modules/gcmodule.c (original) +++ python/branches/py3k/Modules/gcmodule.c Tue Sep 14 11:48:39 2010 @@ -1366,7 +1366,8 @@ void _PyGC_Fini(void) { - if (garbage != NULL && PyList_GET_SIZE(garbage) > 0) { + if (!(debug & DEBUG_SAVEALL) + && garbage != NULL && PyList_GET_SIZE(garbage) > 0) { PySys_WriteStderr( "gc: " "%" PY_FORMAT_SIZE_T "d uncollectable objects at shutdown:\n", From python-checkins at python.org Tue Sep 14 12:08:08 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 12:08:08 +0200 (CEST) Subject: [Python-checkins] r84799 - python/branches/py3k/Modules/posixmodule.c Message-ID: <20100914100808.C2B22FAAC@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 12:08:08 2010 New Revision: 84799 Log: Remove C++-style comments Modified: python/branches/py3k/Modules/posixmodule.c Modified: python/branches/py3k/Modules/posixmodule.c ============================================================================== --- python/branches/py3k/Modules/posixmodule.c (original) +++ python/branches/py3k/Modules/posixmodule.c Tue Sep 14 12:08:08 2010 @@ -2330,7 +2330,7 @@ WIN32_FIND_DATAW wFileData; Py_UNICODE *wnamebuf, *po_wchars; - if (po == NULL) { // Default arg: "." + if (po == NULL) { /* Default arg: "." */ po_wchars = L"."; len = 1; } else { @@ -2577,7 +2577,7 @@ oname = NULL; if (!PyArg_ParseTuple(args, "|O&:listdir", PyUnicode_FSConverter, &oname)) return NULL; - if (oname == NULL) { // Default arg: "." + if (oname == NULL) { /* Default arg: "." */ oname = PyBytes_FromString("."); } name = PyBytes_AsString(oname); From python-checkins at python.org Tue Sep 14 14:54:08 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 14:54:08 +0200 (CEST) Subject: [Python-checkins] r84800 - python/branches/release27-maint/Lib/test/test_ssl.py Message-ID: <20100914125408.8378BFCD4@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 14:54:08 2010 New Revision: 84800 Log: Some tests didn't get executed (because of the merge in r83728) Modified: python/branches/release27-maint/Lib/test/test_ssl.py Modified: python/branches/release27-maint/Lib/test/test_ssl.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_ssl.py (original) +++ python/branches/release27-maint/Lib/test/test_ssl.py Tue Sep 14 14:54:08 2010 @@ -1285,7 +1285,7 @@ not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT)): raise test_support.TestFailed("Can't read certificate files!") - tests = [BasicTests] + tests = [BasicTests, BasicSocketTests] if test_support.is_resource_enabled('network'): tests.append(NetworkedTests) From python-checkins at python.org Tue Sep 14 15:03:39 2010 From: python-checkins at python.org (eric.smith) Date: Tue, 14 Sep 2010 15:03:39 +0200 (CEST) Subject: [Python-checkins] r84801 - python/branches/release31-maint Message-ID: <20100914130339.CBC41D8D8@mail.python.org> Author: eric.smith Date: Tue Sep 14 15:03:39 2010 New Revision: 84801 Log: Blocked revisions 84790 via svnmerge ........ r84790 | eric.smith | 2010-09-13 16:48:43 -0400 (Mon, 13 Sep 2010) | 8 lines Issue 7994: Make object.__format__() raise a PendingDeprecationWarning if the format string is not empty. Manually merge r79596 and r84772 from 2.x. Also, apparently test_format() from test_builtin never made it into 3.x. I've added it as well. It tests the basic format() infrastructure. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Tue Sep 14 15:03:57 2010 From: python-checkins at python.org (eric.smith) Date: Tue, 14 Sep 2010 15:03:57 +0200 (CEST) Subject: [Python-checkins] r84802 - python/branches/release27-maint Message-ID: <20100914130357.4305FD8D8@mail.python.org> Author: eric.smith Date: Tue Sep 14 15:03:57 2010 New Revision: 84802 Log: Blocked revisions 84790 via svnmerge ........ r84790 | eric.smith | 2010-09-13 16:48:43 -0400 (Mon, 13 Sep 2010) | 8 lines Issue 7994: Make object.__format__() raise a PendingDeprecationWarning if the format string is not empty. Manually merge r79596 and r84772 from 2.x. Also, apparently test_format() from test_builtin never made it into 3.x. I've added it as well. It tests the basic format() infrastructure. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Tue Sep 14 16:12:20 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Tue, 14 Sep 2010 16:12:20 +0200 (CEST) Subject: [Python-checkins] r84803 - python/branches/py3k/Makefile.pre.in Message-ID: <20100914141220.223BBF4F4@mail.python.org> Author: alexander.belopolsky Date: Tue Sep 14 16:12:19 2010 New Revision: 84803 Log: Issue 9315: Added test/tracedmodules to LIBSUBDIRS Modified: python/branches/py3k/Makefile.pre.in Modified: python/branches/py3k/Makefile.pre.in ============================================================================== --- python/branches/py3k/Makefile.pre.in (original) +++ python/branches/py3k/Makefile.pre.in Tue Sep 14 16:12:19 2010 @@ -870,6 +870,7 @@ LIBSUBDIRS= tkinter tkinter/test tkinter/test/test_tkinter \ tkinter/test/test_ttk site-packages test \ test/decimaltestdata test/xmltestdata \ + test/tracedmodules \ encodings \ email email/mime email/test email/test/data \ html json json/tests http dbm xmlrpc \ From python-checkins at python.org Tue Sep 14 16:21:18 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Tue, 14 Sep 2010 16:21:18 +0200 (CEST) Subject: [Python-checkins] r84804 - in python/branches/release31-maint: Makefile.pre.in Message-ID: <20100914142118.A43F1EE9AA@mail.python.org> Author: alexander.belopolsky Date: Tue Sep 14 16:21:12 2010 New Revision: 84804 Log: Merged revisions 84803 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84803 | alexander.belopolsky | 2010-09-14 10:12:19 -0400 (Tue, 14 Sep 2010) | 1 line Issue 9315: Added test/tracedmodules to LIBSUBDIRS ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Makefile.pre.in Modified: python/branches/release31-maint/Makefile.pre.in ============================================================================== --- python/branches/release31-maint/Makefile.pre.in (original) +++ python/branches/release31-maint/Makefile.pre.in Tue Sep 14 16:21:12 2010 @@ -837,6 +837,7 @@ LIBSUBDIRS= tkinter tkinter/test tkinter/test/test_tkinter \ tkinter/test/test_ttk site-packages test test/data \ test/decimaltestdata \ + test/tracedmodules \ encodings \ email email/mime email/test email/test/data \ html json json/tests http dbm xmlrpc \ From python-checkins at python.org Tue Sep 14 16:24:58 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Tue, 14 Sep 2010 16:24:58 +0200 (CEST) Subject: [Python-checkins] r84805 - in python/branches/release27-maint: Makefile.pre.in Message-ID: <20100914142458.4C3A3EE99A@mail.python.org> Author: alexander.belopolsky Date: Tue Sep 14 16:24:58 2010 New Revision: 84805 Log: Merged revisions 84803 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84803 | alexander.belopolsky | 2010-09-14 10:12:19 -0400 (Tue, 14 Sep 2010) | 1 line Issue 9315: Added test/tracedmodules to LIBSUBDIRS ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Makefile.pre.in Modified: python/branches/release27-maint/Makefile.pre.in ============================================================================== --- python/branches/release27-maint/Makefile.pre.in (original) +++ python/branches/release27-maint/Makefile.pre.in Tue Sep 14 16:24:58 2010 @@ -850,6 +850,7 @@ LIBSUBDIRS= lib-tk lib-tk/test lib-tk/test/test_tkinter \ lib-tk/test/test_ttk site-packages test test/data \ test/decimaltestdata test/xmltestdata \ + test/tracedmodules \ encodings compiler hotshot \ email email/mime email/test email/test/data \ json json/tests \ From python-checkins at python.org Tue Sep 14 16:37:18 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 16:37:18 +0200 (CEST) Subject: [Python-checkins] r84806 - in python/branches/release27-maint: Lib/ssl.py Lib/test/test_ssl.py Misc/NEWS Message-ID: <20100914143718.783B5FB1A@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 16:37:18 2010 New Revision: 84806 Log: Issue #9729: Fix the signature of SSLSocket.recvfrom() and SSLSocket.sendto() to match the corresponding socket methods. Also, fix various SSLSocket methods to raise socket.error rather than an unhelpful TypeError when called on an unconnected socket. Original patch by Andrew Bennetts. NOTE: obviously, these methods are untested and unused in the real world... Modified: python/branches/release27-maint/Lib/ssl.py python/branches/release27-maint/Lib/test/test_ssl.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/ssl.py ============================================================================== --- python/branches/release27-maint/Lib/ssl.py (original) +++ python/branches/release27-maint/Lib/ssl.py Tue Sep 14 16:37:18 2010 @@ -184,14 +184,16 @@ else: return v else: - return socket.send(self, data, flags) + return self._sock.send(data, flags) - def sendto(self, data, addr, flags=0): + def sendto(self, data, flags_or_addr, addr=None): if self._sslobj: raise ValueError("sendto not allowed on instances of %s" % self.__class__) + elif addr is None: + return self._sock.sendto(data, flags_or_addr) else: - return socket.sendto(self, data, addr, flags) + return self._sock.sendto(data, flags_or_addr, addr) def sendall(self, data, flags=0): if self._sslobj: @@ -216,7 +218,7 @@ self.__class__) return self.read(buflen) else: - return socket.recv(self, buflen, flags) + return self._sock.recv(buflen, flags) def recv_into(self, buffer, nbytes=None, flags=0): if buffer and (nbytes is None): @@ -233,21 +235,21 @@ buffer[:v] = tmp_buffer return v else: - return socket.recv_into(self, buffer, nbytes, flags) + return self._sock.recv_into(buffer, nbytes, flags) - def recvfrom(self, addr, buflen=1024, flags=0): + def recvfrom(self, buflen=1024, flags=0): if self._sslobj: raise ValueError("recvfrom not allowed on instances of %s" % self.__class__) else: - return socket.recvfrom(self, addr, buflen, flags) + return self._sock.recvfrom(buflen, flags) def recvfrom_into(self, buffer, nbytes=None, flags=0): if self._sslobj: raise ValueError("recvfrom_into not allowed on instances of %s" % self.__class__) else: - return socket.recvfrom_into(self, buffer, nbytes, flags) + return self._sock.recvfrom_into(buffer, nbytes, flags) def pending(self): if self._sslobj: Modified: python/branches/release27-maint/Lib/test/test_ssl.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_ssl.py (original) +++ python/branches/release27-maint/Lib/test/test_ssl.py Tue Sep 14 16:37:18 2010 @@ -179,6 +179,19 @@ del ss self.assertEqual(wr(), None) + def test_wrapped_unconnected(self): + # The _delegate_methods in socket.py are correctly delegated to by an + # unconnected SSLSocket, so they will raise a socket.error rather than + # something unexpected like TypeError. + s = socket.socket(socket.AF_INET) + ss = ssl.wrap_socket(s) + self.assertRaises(socket.error, ss.recv, 1) + self.assertRaises(socket.error, ss.recv_into, bytearray(b'x')) + self.assertRaises(socket.error, ss.recvfrom, 1) + self.assertRaises(socket.error, ss.recvfrom_into, bytearray(b'x'), 1) + self.assertRaises(socket.error, ss.send, b'x') + self.assertRaises(socket.error, ss.sendto, b'x', ('0.0.0.0', 0)) + class NetworkedTests(unittest.TestCase): Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Sep 14 16:37:18 2010 @@ -43,6 +43,12 @@ Library ------- +- Issue #9729: Fix the signature of SSLSocket.recvfrom() and + SSLSocket.sendto() to match the corresponding socket methods. Also, + fix various SSLSocket methods to raise socket.error rather than an + unhelpful TypeError when called on an unconnected socket. Original patch + by Andrew Bennetts. + - Issue #9826: OrderedDict.__repr__ can now handle self-referential values: d['x'] = d. From python-checkins at python.org Tue Sep 14 16:43:44 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 16:43:44 +0200 (CEST) Subject: [Python-checkins] r84807 - in python/branches/py3k: Lib/ssl.py Lib/test/test_ssl.py Misc/NEWS Message-ID: <20100914144344.D175DEE9CA@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 16:43:44 2010 New Revision: 84807 Log: Issue #9853: Fix the signature of SSLSocket.recvfrom() and SSLSocket.sendto() to match the corresponding socket methods. Modified: python/branches/py3k/Lib/ssl.py python/branches/py3k/Lib/test/test_ssl.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/ssl.py ============================================================================== --- python/branches/py3k/Lib/ssl.py (original) +++ python/branches/py3k/Lib/ssl.py Tue Sep 14 16:43:44 2010 @@ -258,13 +258,15 @@ else: return socket.send(self, data, flags) - def sendto(self, data, addr, flags=0): + def sendto(self, data, flags_or_addr, addr=None): self._checkClosed() if self._sslobj: raise ValueError("sendto not allowed on instances of %s" % self.__class__) + elif addr is None: + return socket.sendto(self, data, flags_or_addr) else: - return socket.sendto(self, data, addr, flags) + return socket.sendto(self, data, flags_or_addr, addr) def sendall(self, data, flags=0): self._checkClosed() @@ -308,13 +310,13 @@ else: return socket.recv_into(self, buffer, nbytes, flags) - def recvfrom(self, addr, buflen=1024, flags=0): + def recvfrom(self, buflen=1024, flags=0): self._checkClosed() if self._sslobj: raise ValueError("recvfrom not allowed on instances of %s" % self.__class__) else: - return socket.recvfrom(self, addr, buflen, flags) + return socket.recvfrom(self, buflen, flags) def recvfrom_into(self, buffer, nbytes=None, flags=0): self._checkClosed() Modified: python/branches/py3k/Lib/test/test_ssl.py ============================================================================== --- python/branches/py3k/Lib/test/test_ssl.py (original) +++ python/branches/py3k/Lib/test/test_ssl.py Tue Sep 14 16:43:44 2010 @@ -163,6 +163,18 @@ del ss self.assertEqual(wr(), None) + def test_wrapped_unconnected(self): + # Methods on an unconnected SSLSocket propagate the original + # socket.error raise by the underlying socket object. + s = socket.socket(socket.AF_INET) + ss = ssl.wrap_socket(s) + self.assertRaises(socket.error, ss.recv, 1) + self.assertRaises(socket.error, ss.recv_into, bytearray(b'x')) + self.assertRaises(socket.error, ss.recvfrom, 1) + self.assertRaises(socket.error, ss.recvfrom_into, bytearray(b'x'), 1) + self.assertRaises(socket.error, ss.send, b'x') + self.assertRaises(socket.error, ss.sendto, b'x', ('0.0.0.0', 0)) + def test_timeout(self): # Issue #8524: when creating an SSL socket, the timeout of the # original socket should be retained. Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 14 16:43:44 2010 @@ -52,6 +52,9 @@ Library ------- +- Issue #9853: Fix the signature of SSLSocket.recvfrom() and + SSLSocket.sendto() to match the corresponding socket methods. + - Issue 9840: Added a decorator to reprlib for wrapping __repr__ methods to make them handle recursive calls within the same thread. From python-checkins at python.org Tue Sep 14 16:44:56 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 16:44:56 +0200 (CEST) Subject: [Python-checkins] r84808 - python/branches/release27-maint Message-ID: <20100914144456.97439EE9A7@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 16:44:56 2010 New Revision: 84808 Log: Blocked revisions 84807 via svnmerge ........ r84807 | antoine.pitrou | 2010-09-14 16:43:44 +0200 (mar., 14 sept. 2010) | 4 lines Issue #9853: Fix the signature of SSLSocket.recvfrom() and SSLSocket.sendto() to match the corresponding socket methods. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Tue Sep 14 16:47:09 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 16:47:09 +0200 (CEST) Subject: [Python-checkins] r84809 - in python/branches/release31-maint: Lib/ssl.py Lib/test/test_ssl.py Misc/NEWS Message-ID: <20100914144709.16DBDEE989@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 16:47:08 2010 New Revision: 84809 Log: Merged revisions 84807 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84807 | antoine.pitrou | 2010-09-14 16:43:44 +0200 (mar., 14 sept. 2010) | 4 lines Issue #9853: Fix the signature of SSLSocket.recvfrom() and SSLSocket.sendto() to match the corresponding socket methods. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/ssl.py python/branches/release31-maint/Lib/test/test_ssl.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/ssl.py ============================================================================== --- python/branches/release31-maint/Lib/ssl.py (original) +++ python/branches/release31-maint/Lib/ssl.py Tue Sep 14 16:47:08 2010 @@ -221,13 +221,15 @@ else: return socket.send(self, data, flags) - def sendto(self, data, addr, flags=0): + def sendto(self, data, flags_or_addr, addr=None): self._checkClosed() if self._sslobj: raise ValueError("sendto not allowed on instances of %s" % self.__class__) + elif addr is None: + return socket.sendto(self, data, flags_or_addr) else: - return socket.sendto(self, data, addr, flags) + return socket.sendto(self, data, flags_or_addr, addr) def sendall(self, data, flags=0): self._checkClosed() @@ -267,13 +269,13 @@ else: return socket.recv_into(self, buffer, nbytes, flags) - def recvfrom(self, addr, buflen=1024, flags=0): + def recvfrom(self, buflen=1024, flags=0): self._checkClosed() if self._sslobj: raise ValueError("recvfrom not allowed on instances of %s" % self.__class__) else: - return socket.recvfrom(self, addr, buflen, flags) + return socket.recvfrom(self, buflen, flags) def recvfrom_into(self, buffer, nbytes=None, flags=0): self._checkClosed() Modified: python/branches/release31-maint/Lib/test/test_ssl.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_ssl.py (original) +++ python/branches/release31-maint/Lib/test/test_ssl.py Tue Sep 14 16:47:08 2010 @@ -92,6 +92,18 @@ del ss self.assertEqual(wr(), None) + def test_wrapped_unconnected(self): + # Methods on an unconnected SSLSocket propagate the original + # socket.error raise by the underlying socket object. + s = socket.socket(socket.AF_INET) + ss = ssl.wrap_socket(s) + self.assertRaises(socket.error, ss.recv, 1) + self.assertRaises(socket.error, ss.recv_into, bytearray(b'x')) + self.assertRaises(socket.error, ss.recvfrom, 1) + self.assertRaises(socket.error, ss.recvfrom_into, bytearray(b'x'), 1) + self.assertRaises(socket.error, ss.send, b'x') + self.assertRaises(socket.error, ss.sendto, b'x', ('0.0.0.0', 0)) + def test_timeout(self): # Issue #8524: when creating an SSL socket, the timeout of the # original socket should be retained. Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Sep 14 16:47:08 2010 @@ -117,6 +117,9 @@ Library ------- +- Issue #9853: Fix the signature of SSLSocket.recvfrom() and + SSLSocket.sendto() to match the corresponding socket methods. + - Issue #9792: In case of connection failure, socket.create_connection() would swallow the exception and raise a new one, making it impossible to fetch the original errno, or to filter timeout errors. Now the From python-checkins at python.org Tue Sep 14 18:02:01 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Tue, 14 Sep 2010 18:02:01 +0200 (CEST) Subject: [Python-checkins] r84810 - in python/branches/py3k: Doc/extending/windows.rst Include/pyport.h Message-ID: <20100914160201.9CB72EE987@mail.python.org> Author: daniel.stutzbach Date: Tue Sep 14 18:02:01 2010 New Revision: 84810 Log: Remove pointers to a FAQ entry that no longer exists. Incorporate some text from the old FAQ into the docs Modified: python/branches/py3k/Doc/extending/windows.rst python/branches/py3k/Include/pyport.h Modified: python/branches/py3k/Doc/extending/windows.rst ============================================================================== --- python/branches/py3k/Doc/extending/windows.rst (original) +++ python/branches/py3k/Doc/extending/windows.rst Tue Sep 14 18:02:01 2010 @@ -171,7 +171,9 @@ PyVarObject_HEAD_INIT(&PyType_Type, 0) -Change it to:: +Static type object initializers in extension modules may cause +compiles to fail with an error message like "initializer not a +constant". This shows up when building DLL under MSVC. Change it to:: PyVarObject_HEAD_INIT(NULL, 0) @@ -179,8 +181,6 @@ MyObject_Type.ob_type = &PyType_Type; -Refer to section 3 of the `Python FAQ `_ for -details on why you must do this. .. _dynamic-linking: Modified: python/branches/py3k/Include/pyport.h ============================================================================== --- python/branches/py3k/Include/pyport.h (original) +++ python/branches/py3k/Include/pyport.h Tue Sep 14 18:02:01 2010 @@ -695,23 +695,24 @@ # ifdef Py_BUILD_CORE # define PyAPI_FUNC(RTYPE) __declspec(dllexport) RTYPE # define PyAPI_DATA(RTYPE) extern __declspec(dllexport) RTYPE - /* module init functions inside the core need no external linkage */ - /* except for Cygwin to handle embedding */ + /* module init functions inside the core need no external linkage */ + /* except for Cygwin to handle embedding */ # if defined(__CYGWIN__) # define PyMODINIT_FUNC __declspec(dllexport) PyObject* # else /* __CYGWIN__ */ # define PyMODINIT_FUNC PyObject* # endif /* __CYGWIN__ */ # else /* Py_BUILD_CORE */ - /* Building an extension module, or an embedded situation */ - /* public Python functions and data are imported */ - /* Under Cygwin, auto-import functions to prevent compilation */ - /* failures similar to http://python.org/doc/FAQ.html#3.24 */ + /* Building an extension module, or an embedded situation */ + /* public Python functions and data are imported */ + /* Under Cygwin, auto-import functions to prevent compilation */ + /* failures similar to those described at the bottom of 4.1: */ + /* http://docs.python.org/extending/windows.html#a-cookbook-approach */ # if !defined(__CYGWIN__) # define PyAPI_FUNC(RTYPE) __declspec(dllimport) RTYPE # endif /* !__CYGWIN__ */ # define PyAPI_DATA(RTYPE) extern __declspec(dllimport) RTYPE - /* module init functions outside the core must be exported */ + /* module init functions outside the core must be exported */ # if defined(__cplusplus) # define PyMODINIT_FUNC extern "C" __declspec(dllexport) PyObject* # else /* __cplusplus */ From python-checkins at python.org Tue Sep 14 18:07:54 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Tue, 14 Sep 2010 18:07:54 +0200 (CEST) Subject: [Python-checkins] r84811 - in python/branches/release31-maint: Doc/extending/windows.rst Include/pyport.h Message-ID: <20100914160754.C6799FE0A@mail.python.org> Author: daniel.stutzbach Date: Tue Sep 14 18:07:54 2010 New Revision: 84811 Log: Merged revisions 84810 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84810 | daniel.stutzbach | 2010-09-14 11:02:01 -0500 (Tue, 14 Sep 2010) | 1 line Remove pointers to a FAQ entry that no longer exists. Incorporate some text from the old FAQ into the docs ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/extending/windows.rst python/branches/release31-maint/Include/pyport.h Modified: python/branches/release31-maint/Doc/extending/windows.rst ============================================================================== --- python/branches/release31-maint/Doc/extending/windows.rst (original) +++ python/branches/release31-maint/Doc/extending/windows.rst Tue Sep 14 18:07:54 2010 @@ -171,7 +171,9 @@ PyVarObject_HEAD_INIT(&PyType_Type, 0) -Change it to:: +Static type object initializers in extension modules may cause +compiles to fail with an error message like "initializer not a +constant". This shows up when building DLL under MSVC. Change it to:: PyVarObject_HEAD_INIT(NULL, 0) @@ -179,8 +181,6 @@ MyObject_Type.ob_type = &PyType_Type; -Refer to section 3 of the `Python FAQ `_ for -details on why you must do this. .. _dynamic-linking: Modified: python/branches/release31-maint/Include/pyport.h ============================================================================== --- python/branches/release31-maint/Include/pyport.h (original) +++ python/branches/release31-maint/Include/pyport.h Tue Sep 14 18:07:54 2010 @@ -663,23 +663,24 @@ # ifdef Py_BUILD_CORE # define PyAPI_FUNC(RTYPE) __declspec(dllexport) RTYPE # define PyAPI_DATA(RTYPE) extern __declspec(dllexport) RTYPE - /* module init functions inside the core need no external linkage */ - /* except for Cygwin to handle embedding */ + /* module init functions inside the core need no external linkage */ + /* except for Cygwin to handle embedding */ # if defined(__CYGWIN__) # define PyMODINIT_FUNC __declspec(dllexport) PyObject* # else /* __CYGWIN__ */ # define PyMODINIT_FUNC PyObject* # endif /* __CYGWIN__ */ # else /* Py_BUILD_CORE */ - /* Building an extension module, or an embedded situation */ - /* public Python functions and data are imported */ - /* Under Cygwin, auto-import functions to prevent compilation */ - /* failures similar to http://python.org/doc/FAQ.html#3.24 */ + /* Building an extension module, or an embedded situation */ + /* public Python functions and data are imported */ + /* Under Cygwin, auto-import functions to prevent compilation */ + /* failures similar to those described at the bottom of 4.1: */ + /* http://docs.python.org/extending/windows.html#a-cookbook-approach */ # if !defined(__CYGWIN__) # define PyAPI_FUNC(RTYPE) __declspec(dllimport) RTYPE # endif /* !__CYGWIN__ */ # define PyAPI_DATA(RTYPE) extern __declspec(dllimport) RTYPE - /* module init functions outside the core must be exported */ + /* module init functions outside the core must be exported */ # if defined(__cplusplus) # define PyMODINIT_FUNC extern "C" __declspec(dllexport) PyObject* # else /* __cplusplus */ From python-checkins at python.org Tue Sep 14 18:10:22 2010 From: python-checkins at python.org (daniel.stutzbach) Date: Tue, 14 Sep 2010 18:10:22 +0200 (CEST) Subject: [Python-checkins] r84812 - in python/branches/release27-maint: Doc/extending/windows.rst Include/pyport.h Message-ID: <20100914161022.95467FE0A@mail.python.org> Author: daniel.stutzbach Date: Tue Sep 14 18:10:22 2010 New Revision: 84812 Log: Merged revisions 84810 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84810 | daniel.stutzbach | 2010-09-14 11:02:01 -0500 (Tue, 14 Sep 2010) | 1 line Remove pointers to a FAQ entry that no longer exists. Incorporate some text from the old FAQ into the docs ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/extending/windows.rst python/branches/release27-maint/Include/pyport.h Modified: python/branches/release27-maint/Doc/extending/windows.rst ============================================================================== --- python/branches/release27-maint/Doc/extending/windows.rst (original) +++ python/branches/release27-maint/Doc/extending/windows.rst Tue Sep 14 18:10:22 2010 @@ -175,7 +175,9 @@ PyObject_HEAD_INIT(&PyType_Type) -Change it to:: +Static type object initializers in extension modules may cause +compiles to fail with an error message like "initializer not a +constant". This shows up when building DLL under MSVC. Change it to:: PyObject_HEAD_INIT(NULL) @@ -183,8 +185,6 @@ MyObject_Type.ob_type = &PyType_Type; -Refer to section 3 of the `Python FAQ `_ for -details on why you must do this. .. _dynamic-linking: Modified: python/branches/release27-maint/Include/pyport.h ============================================================================== --- python/branches/release27-maint/Include/pyport.h (original) +++ python/branches/release27-maint/Include/pyport.h Tue Sep 14 18:10:22 2010 @@ -724,23 +724,24 @@ # ifdef Py_BUILD_CORE # define PyAPI_FUNC(RTYPE) __declspec(dllexport) RTYPE # define PyAPI_DATA(RTYPE) extern __declspec(dllexport) RTYPE - /* module init functions inside the core need no external linkage */ - /* except for Cygwin to handle embedding (FIXME: BeOS too?) */ + /* module init functions inside the core need no external linkage */ + /* except for Cygwin to handle embedding (FIXME: BeOS too?) */ # if defined(__CYGWIN__) # define PyMODINIT_FUNC __declspec(dllexport) void # else /* __CYGWIN__ */ # define PyMODINIT_FUNC void # endif /* __CYGWIN__ */ # else /* Py_BUILD_CORE */ - /* Building an extension module, or an embedded situation */ - /* public Python functions and data are imported */ - /* Under Cygwin, auto-import functions to prevent compilation */ - /* failures similar to http://python.org/doc/FAQ.html#3.24 */ + /* Building an extension module, or an embedded situation */ + /* public Python functions and data are imported */ + /* Under Cygwin, auto-import functions to prevent compilation */ + /* failures similar to those described at the bottom of 4.1: */ + /* http://docs.python.org/extending/windows.html#a-cookbook-approach */ # if !defined(__CYGWIN__) # define PyAPI_FUNC(RTYPE) __declspec(dllimport) RTYPE # endif /* !__CYGWIN__ */ # define PyAPI_DATA(RTYPE) extern __declspec(dllimport) RTYPE - /* module init functions outside the core must be exported */ + /* module init functions outside the core must be exported */ # if defined(__cplusplus) # define PyMODINIT_FUNC extern "C" __declspec(dllexport) void # else /* __cplusplus */ From python-checkins at python.org Tue Sep 14 20:00:03 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 20:00:03 +0200 (CEST) Subject: [Python-checkins] r84813 - in python/branches/py3k: Doc/library/socket.rst Lib/socket.py Lib/test/test_socket.py Misc/NEWS Message-ID: <20100914180003.40634FCD4@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 20:00:02 2010 New Revision: 84813 Log: Issue #1552: socket.socketpair() now returns regular socket.socket objects supporting the whole socket API (rather than the "raw" _socket.socket objects). Modified: python/branches/py3k/Doc/library/socket.rst python/branches/py3k/Lib/socket.py python/branches/py3k/Lib/test/test_socket.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/socket.rst ============================================================================== --- python/branches/py3k/Doc/library/socket.rst (original) +++ python/branches/py3k/Doc/library/socket.rst Tue Sep 14 20:00:02 2010 @@ -364,6 +364,10 @@ if defined on the platform; otherwise, the default is :const:`AF_INET`. Availability: Unix. + .. versionchanged:: 3.2 + The returned socket objects now support the whole socket API, rather + than a subset. + .. function:: fromfd(fd, family, type[, proto]) Modified: python/branches/py3k/Lib/socket.py ============================================================================== --- python/branches/py3k/Lib/socket.py (original) +++ python/branches/py3k/Lib/socket.py Tue Sep 14 20:00:02 2010 @@ -199,6 +199,27 @@ return socket(family, type, proto, nfd) +if hasattr(_socket, "socketpair"): + + def socketpair(family=None, type=SOCK_STREAM, proto=0): + """socketpair([family[, type[, proto]]]) -> (socket object, socket object) + + Create a pair of socket objects from the sockets returned by the platform + socketpair() function. + The arguments are the same as for socket() except the default family is + AF_UNIX if defined on the platform; otherwise, the default is AF_INET. + """ + if family is None: + try: + family = AF_UNIX + except NameError: + family = AF_INET + a, b = _socket.socketpair(family, type, proto) + a = socket(family, type, proto, a.detach()) + b = socket(family, type, proto, b.detach()) + return a, b + + class SocketIO(io.RawIOBase): """Raw I/O implementation for stream sockets. Modified: python/branches/py3k/Lib/test/test_socket.py ============================================================================== --- python/branches/py3k/Lib/test/test_socket.py (original) +++ python/branches/py3k/Lib/test/test_socket.py Tue Sep 14 20:00:02 2010 @@ -714,6 +714,7 @@ # Testing fromfd() fd = self.cli_conn.fileno() sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) + self.assertIsInstance(sock, socket.socket) msg = sock.recv(1024) self.assertEqual(msg, MSG) @@ -814,6 +815,23 @@ def __init__(self, methodName='runTest'): SocketPairTest.__init__(self, methodName=methodName) + def _testDefaults(self): + pass + + def testDefaults(self): + self.assertIsInstance(self.cli, socket.socket) + self.assertIsInstance(self.serv, socket.socket) + if hasattr(socket, 'AF_UNIX'): + self.assertEqual(self.cli.family, socket.AF_UNIX) + self.assertEqual(self.serv.family, socket.AF_UNIX) + else: + self.assertEqual(self.cli.family, socket.AF_INET) + self.assertEqual(self.serv.family, socket.AF_INET) + self.assertEqual(self.cli.type, socket.SOCK_STREAM) + self.assertEqual(self.serv.type, socket.SOCK_STREAM) + self.assertEqual(self.cli.proto, 0) + self.assertEqual(self.serv.proto, 0) + def testRecv(self): msg = self.serv.recv(1024) self.assertEqual(msg, MSG) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 14 20:00:02 2010 @@ -52,6 +52,10 @@ Library ------- +- Issue #1552: socket.socketpair() now returns regular socket.socket + objects supporting the whole socket API (rather than the "raw" + _socket.socket objects). + - Issue #9853: Fix the signature of SSLSocket.recvfrom() and SSLSocket.sendto() to match the corresponding socket methods. From python-checkins at python.org Tue Sep 14 20:37:24 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 20:37:24 +0200 (CEST) Subject: [Python-checkins] r84814 - in python/branches/py3k: Doc/library/io.rst Lib/_pyio.py Lib/test/test_io.py Misc/NEWS Modules/_io/iobase.c Message-ID: <20100914183724.5F99FEE9B4@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 20:37:24 2010 New Revision: 84814 Log: Issue #9854: The default read() implementation in io.RawIOBase now handles non-blocking readinto() returning None correctly. Modified: python/branches/py3k/Doc/library/io.rst python/branches/py3k/Lib/_pyio.py python/branches/py3k/Lib/test/test_io.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/_io/iobase.c Modified: python/branches/py3k/Doc/library/io.rst ============================================================================== --- python/branches/py3k/Doc/library/io.rst (original) +++ python/branches/py3k/Doc/library/io.rst Tue Sep 14 20:37:24 2010 @@ -361,8 +361,9 @@ .. method:: readinto(b) - Read up to len(b) bytes into bytearray *b* and return the number of bytes - read. + Read up to len(b) bytes into bytearray *b* and return the number ofbytes + read. If the object is in non-blocking mode and no bytes are available, + ``None`` is returned. .. method:: write(b) Modified: python/branches/py3k/Lib/_pyio.py ============================================================================== --- python/branches/py3k/Lib/_pyio.py (original) +++ python/branches/py3k/Lib/_pyio.py Tue Sep 14 20:37:24 2010 @@ -544,6 +544,8 @@ return self.readall() b = bytearray(n.__index__()) n = self.readinto(b) + if n is None: + return None del b[n:] return bytes(b) @@ -561,7 +563,7 @@ """Read up to len(b) bytes into b. Returns number of bytes read (0 for EOF), or None if the object - is set not to block as has no data to read. + is set not to block and has no data to read. """ self._unsupported("readinto") Modified: python/branches/py3k/Lib/test/test_io.py ============================================================================== --- python/branches/py3k/Lib/test/test_io.py (original) +++ python/branches/py3k/Lib/test/test_io.py Tue Sep 14 20:37:24 2010 @@ -48,7 +48,9 @@ return f._CHUNK_SIZE -class MockRawIO: +class MockRawIOWithoutRead: + """A RawIO implementation without read(), so as to exercise the default + RawIO.read() which calls readinto().""" def __init__(self, read_stack=()): self._read_stack = list(read_stack) @@ -56,14 +58,6 @@ self._reads = 0 self._extraneous_reads = 0 - def read(self, n=None): - self._reads += 1 - try: - return self._read_stack.pop(0) - except: - self._extraneous_reads += 1 - return b"" - def write(self, b): self._write_stack.append(bytes(b)) return len(b) @@ -110,6 +104,23 @@ def truncate(self, pos=None): return pos +class CMockRawIOWithoutRead(MockRawIOWithoutRead, io.RawIOBase): + pass + +class PyMockRawIOWithoutRead(MockRawIOWithoutRead, pyio.RawIOBase): + pass + + +class MockRawIO(MockRawIOWithoutRead): + + def read(self, n=None): + self._reads += 1 + try: + return self._read_stack.pop(0) + except: + self._extraneous_reads += 1 + return b"" + class CMockRawIO(MockRawIO, io.RawIOBase): pass @@ -582,6 +593,19 @@ f.close() self.assertRaises(ValueError, f.flush) + def test_RawIOBase_read(self): + # Exercise the default RawIOBase.read() implementation (which calls + # readinto() internally). + rawio = self.MockRawIOWithoutRead((b"abc", b"d", None, b"efg", None)) + self.assertEqual(rawio.read(2), b"ab") + self.assertEqual(rawio.read(2), b"c") + self.assertEqual(rawio.read(2), b"d") + self.assertEqual(rawio.read(2), None) + self.assertEqual(rawio.read(2), b"ef") + self.assertEqual(rawio.read(2), b"g") + self.assertEqual(rawio.read(2), None) + self.assertEqual(rawio.read(2), b"") + class CIOTest(IOTest): pass @@ -2590,7 +2614,7 @@ # Put the namespaces of the IO module we are testing and some useful mock # classes in the __dict__ of each test. mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO, - MockNonBlockWriterIO, MockUnseekableIO) + MockNonBlockWriterIO, MockUnseekableIO, MockRawIOWithoutRead) all_members = io.__all__ + ["IncrementalNewlineDecoder"] c_io_ns = {name : getattr(io, name) for name in all_members} py_io_ns = {name : getattr(pyio, name) for name in all_members} Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 14 20:37:24 2010 @@ -52,6 +52,9 @@ Library ------- +- Issue #9854: The default read() implementation in io.RawIOBase now + handles non-blocking readinto() returning None correctly. + - Issue #1552: socket.socketpair() now returns regular socket.socket objects supporting the whole socket API (rather than the "raw" _socket.socket objects). Modified: python/branches/py3k/Modules/_io/iobase.c ============================================================================== --- python/branches/py3k/Modules/_io/iobase.c (original) +++ python/branches/py3k/Modules/_io/iobase.c Tue Sep 14 20:37:24 2010 @@ -777,9 +777,9 @@ return NULL; res = PyObject_CallMethodObjArgs(self, _PyIO_str_readinto, b, NULL); - if (res == NULL) { + if (res == NULL || res == Py_None) { Py_DECREF(b); - return NULL; + return res; } n = PyNumber_AsSsize_t(res, PyExc_ValueError); From python-checkins at python.org Tue Sep 14 20:39:20 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 20:39:20 +0200 (CEST) Subject: [Python-checkins] r84815 - python/branches/release31-maint Message-ID: <20100914183920.5B0A5EE9C2@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 20:39:20 2010 New Revision: 84815 Log: Blocked revisions 84813 via svnmerge ........ r84813 | antoine.pitrou | 2010-09-14 20:00:02 +0200 (mar., 14 sept. 2010) | 5 lines Issue #1552: socket.socketpair() now returns regular socket.socket objects supporting the whole socket API (rather than the "raw" _socket.socket objects). ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Tue Sep 14 20:48:19 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 20:48:19 +0200 (CEST) Subject: [Python-checkins] r84816 - in python/branches/release27-maint: Doc/library/io.rst Lib/_pyio.py Lib/test/test_io.py Misc/NEWS Modules/_io/iobase.c Message-ID: <20100914184819.99DF7EE987@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 20:48:19 2010 New Revision: 84816 Log: Merged revisions 84814 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84814 | antoine.pitrou | 2010-09-14 20:37:24 +0200 (mar., 14 sept. 2010) | 4 lines Issue #9854: The default read() implementation in io.RawIOBase now handles non-blocking readinto() returning None correctly. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/io.rst python/branches/release27-maint/Lib/_pyio.py python/branches/release27-maint/Lib/test/test_io.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/_io/iobase.c Modified: python/branches/release27-maint/Doc/library/io.rst ============================================================================== --- python/branches/release27-maint/Doc/library/io.rst (original) +++ python/branches/release27-maint/Doc/library/io.rst Tue Sep 14 20:48:19 2010 @@ -371,8 +371,9 @@ .. method:: readinto(b) - Read up to len(b) bytes into bytearray *b* and return the number of bytes - read. + Read up to len(b) bytes into bytearray *b* and return the number ofbytes + read. If the object is in non-blocking mode and no bytes are available, + ``None`` is returned. .. method:: write(b) Modified: python/branches/release27-maint/Lib/_pyio.py ============================================================================== --- python/branches/release27-maint/Lib/_pyio.py (original) +++ python/branches/release27-maint/Lib/_pyio.py Tue Sep 14 20:48:19 2010 @@ -546,6 +546,8 @@ return self.readall() b = bytearray(n.__index__()) n = self.readinto(b) + if n is None: + return None del b[n:] return bytes(b) @@ -563,7 +565,7 @@ """Read up to len(b) bytes into b. Returns number of bytes read (0 for EOF), or None if the object - is set not to block as has no data to read. + is set not to block and has no data to read. """ self._unsupported("readinto") Modified: python/branches/release27-maint/Lib/test/test_io.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_io.py (original) +++ python/branches/release27-maint/Lib/test/test_io.py Tue Sep 14 20:48:19 2010 @@ -53,7 +53,9 @@ return f._CHUNK_SIZE -class MockRawIO: +class MockRawIOWithoutRead: + """A RawIO implementation without read(), so as to exercise the default + RawIO.read() which calls readinto().""" def __init__(self, read_stack=()): self._read_stack = list(read_stack) @@ -61,14 +63,6 @@ self._reads = 0 self._extraneous_reads = 0 - def read(self, n=None): - self._reads += 1 - try: - return self._read_stack.pop(0) - except: - self._extraneous_reads += 1 - return b"" - def write(self, b): self._write_stack.append(bytes(b)) return len(b) @@ -115,6 +109,23 @@ def truncate(self, pos=None): return pos +class CMockRawIOWithoutRead(MockRawIOWithoutRead, io.RawIOBase): + pass + +class PyMockRawIOWithoutRead(MockRawIOWithoutRead, pyio.RawIOBase): + pass + + +class MockRawIO(MockRawIOWithoutRead): + + def read(self, n=None): + self._reads += 1 + try: + return self._read_stack.pop(0) + except: + self._extraneous_reads += 1 + return b"" + class CMockRawIO(MockRawIO, io.RawIOBase): pass @@ -560,6 +571,19 @@ f.close() self.assertRaises(ValueError, f.flush) + def test_RawIOBase_read(self): + # Exercise the default RawIOBase.read() implementation (which calls + # readinto() internally). + rawio = self.MockRawIOWithoutRead((b"abc", b"d", None, b"efg", None)) + self.assertEqual(rawio.read(2), b"ab") + self.assertEqual(rawio.read(2), b"c") + self.assertEqual(rawio.read(2), b"d") + self.assertEqual(rawio.read(2), None) + self.assertEqual(rawio.read(2), b"ef") + self.assertEqual(rawio.read(2), b"g") + self.assertEqual(rawio.read(2), None) + self.assertEqual(rawio.read(2), b"") + class CIOTest(IOTest): pass @@ -2558,7 +2582,7 @@ # Put the namespaces of the IO module we are testing and some useful mock # classes in the __dict__ of each test. mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO, - MockNonBlockWriterIO) + MockNonBlockWriterIO, MockRawIOWithoutRead) all_members = io.__all__ + ["IncrementalNewlineDecoder"] c_io_ns = dict((name, getattr(io, name)) for name in all_members) py_io_ns = dict((name, getattr(pyio, name)) for name in all_members) Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Sep 14 20:48:19 2010 @@ -43,6 +43,9 @@ Library ------- +- Issue #9854: The default read() implementation in io.RawIOBase now + handles non-blocking readinto() returning None correctly. + - Issue #9729: Fix the signature of SSLSocket.recvfrom() and SSLSocket.sendto() to match the corresponding socket methods. Also, fix various SSLSocket methods to raise socket.error rather than an Modified: python/branches/release27-maint/Modules/_io/iobase.c ============================================================================== --- python/branches/release27-maint/Modules/_io/iobase.c (original) +++ python/branches/release27-maint/Modules/_io/iobase.c Tue Sep 14 20:48:19 2010 @@ -776,9 +776,9 @@ return NULL; res = PyObject_CallMethodObjArgs(self, _PyIO_str_readinto, b, NULL); - if (res == NULL) { + if (res == NULL || res == Py_None) { Py_DECREF(b); - return NULL; + return res; } n = PyNumber_AsSsize_t(res, PyExc_ValueError); From python-checkins at python.org Tue Sep 14 20:53:08 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 20:53:08 +0200 (CEST) Subject: [Python-checkins] r84817 - in python/branches/release31-maint: Doc/library/io.rst Lib/_pyio.py Lib/test/test_io.py Misc/NEWS Modules/_io/iobase.c Message-ID: <20100914185308.24358EE987@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 20:53:07 2010 New Revision: 84817 Log: Merged revisions 84814 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84814 | antoine.pitrou | 2010-09-14 20:37:24 +0200 (mar., 14 sept. 2010) | 4 lines Issue #9854: The default read() implementation in io.RawIOBase now handles non-blocking readinto() returning None correctly. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/io.rst python/branches/release31-maint/Lib/_pyio.py python/branches/release31-maint/Lib/test/test_io.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/_io/iobase.c Modified: python/branches/release31-maint/Doc/library/io.rst ============================================================================== --- python/branches/release31-maint/Doc/library/io.rst (original) +++ python/branches/release31-maint/Doc/library/io.rst Tue Sep 14 20:53:07 2010 @@ -361,8 +361,9 @@ .. method:: readinto(b) - Read up to len(b) bytes into bytearray *b* and return the number of bytes - read. + Read up to len(b) bytes into bytearray *b* and return the number ofbytes + read. If the object is in non-blocking mode and no bytes are available, + ``None`` is returned. .. method:: write(b) Modified: python/branches/release31-maint/Lib/_pyio.py ============================================================================== --- python/branches/release31-maint/Lib/_pyio.py (original) +++ python/branches/release31-maint/Lib/_pyio.py Tue Sep 14 20:53:07 2010 @@ -542,6 +542,8 @@ return self.readall() b = bytearray(n.__index__()) n = self.readinto(b) + if n is None: + return None del b[n:] return bytes(b) @@ -559,7 +561,7 @@ """Read up to len(b) bytes into b. Returns number of bytes read (0 for EOF), or None if the object - is set not to block as has no data to read. + is set not to block and has no data to read. """ self._unsupported("readinto") Modified: python/branches/release31-maint/Lib/test/test_io.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_io.py (original) +++ python/branches/release31-maint/Lib/test/test_io.py Tue Sep 14 20:53:07 2010 @@ -47,7 +47,9 @@ return f._CHUNK_SIZE -class MockRawIO: +class MockRawIOWithoutRead: + """A RawIO implementation without read(), so as to exercise the default + RawIO.read() which calls readinto().""" def __init__(self, read_stack=()): self._read_stack = list(read_stack) @@ -55,14 +57,6 @@ self._reads = 0 self._extraneous_reads = 0 - def read(self, n=None): - self._reads += 1 - try: - return self._read_stack.pop(0) - except: - self._extraneous_reads += 1 - return b"" - def write(self, b): self._write_stack.append(bytes(b)) return len(b) @@ -109,6 +103,23 @@ def truncate(self, pos=None): return pos +class CMockRawIOWithoutRead(MockRawIOWithoutRead, io.RawIOBase): + pass + +class PyMockRawIOWithoutRead(MockRawIOWithoutRead, pyio.RawIOBase): + pass + + +class MockRawIO(MockRawIOWithoutRead): + + def read(self, n=None): + self._reads += 1 + try: + return self._read_stack.pop(0) + except: + self._extraneous_reads += 1 + return b"" + class CMockRawIO(MockRawIO, io.RawIOBase): pass @@ -554,6 +565,19 @@ f.close() self.assertRaises(ValueError, f.flush) + def test_RawIOBase_read(self): + # Exercise the default RawIOBase.read() implementation (which calls + # readinto() internally). + rawio = self.MockRawIOWithoutRead((b"abc", b"d", None, b"efg", None)) + self.assertEqual(rawio.read(2), b"ab") + self.assertEqual(rawio.read(2), b"c") + self.assertEqual(rawio.read(2), b"d") + self.assertEqual(rawio.read(2), None) + self.assertEqual(rawio.read(2), b"ef") + self.assertEqual(rawio.read(2), b"g") + self.assertEqual(rawio.read(2), None) + self.assertEqual(rawio.read(2), b"") + class CIOTest(IOTest): pass @@ -2557,7 +2581,7 @@ # Put the namespaces of the IO module we are testing and some useful mock # classes in the __dict__ of each test. mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO, - MockNonBlockWriterIO) + MockNonBlockWriterIO, MockRawIOWithoutRead) all_members = io.__all__ + ["IncrementalNewlineDecoder"] c_io_ns = {name : getattr(io, name) for name in all_members} py_io_ns = {name : getattr(pyio, name) for name in all_members} Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Sep 14 20:53:07 2010 @@ -117,6 +117,9 @@ Library ------- +- Issue #9854: The default read() implementation in io.RawIOBase now + handles non-blocking readinto() returning None correctly. + - Issue #9853: Fix the signature of SSLSocket.recvfrom() and SSLSocket.sendto() to match the corresponding socket methods. Modified: python/branches/release31-maint/Modules/_io/iobase.c ============================================================================== --- python/branches/release31-maint/Modules/_io/iobase.c (original) +++ python/branches/release31-maint/Modules/_io/iobase.c Tue Sep 14 20:53:07 2010 @@ -776,9 +776,9 @@ return NULL; res = PyObject_CallMethodObjArgs(self, _PyIO_str_readinto, b, NULL); - if (res == NULL) { + if (res == NULL || res == Py_None) { Py_DECREF(b); - return NULL; + return res; } n = PyNumber_AsSsize_t(res, PyExc_ValueError); From nnorwitz at gmail.com Tue Sep 14 13:11:25 2010 From: nnorwitz at gmail.com (Neal Norwitz) Date: Tue, 14 Sep 2010 07:11:25 -0400 Subject: [Python-checkins] Python Regression Test Failures refleak (1) Message-ID: <20100914111125.GA32453@kbk-i386-bb.dyndns.org> More important issues: ---------------------- test_bz2 leaked [-69, 0, 0] references, sum=-69 Less important issues: ---------------------- From python-checkins at python.org Tue Sep 14 21:40:15 2010 From: python-checkins at python.org (raymond.hettinger) Date: Tue, 14 Sep 2010 21:40:15 +0200 (CEST) Subject: [Python-checkins] r84818 - python/branches/py3k/Lib/collections.py Message-ID: <20100914194015.5AE4EEE9B6@mail.python.org> Author: raymond.hettinger Date: Tue Sep 14 21:40:15 2010 New Revision: 84818 Log: Improve iteration speed by only proxying back links. The forward links are hard references. The sentinel element is also a weakref proxy (to break a forward cylce wrapping around the sentinel). Modified: python/branches/py3k/Lib/collections.py Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Tue Sep 14 21:40:15 2010 @@ -32,6 +32,7 @@ # The internal self.__map dictionary maps keys to links in a doubly linked list. # The circular doubly linked list starts and ends with a sentinel element. # The sentinel element never gets deleted (this simplifies the algorithm). + # The sentinel is stored in self.__hardroot with a weakref proxy in self.__root. # The prev/next links are weakref proxies (to prevent circular references). # Individual links are kept alive by the hard reference in self.__map. # Those hard references disappear when a key is deleted from an OrderedDict. @@ -47,7 +48,8 @@ try: self.__root except AttributeError: - self.__root = root = _Link() # sentinel node for the doubly linked list + self.__hardroot = _Link() + self.__root = root = _proxy(self.__hardroot) root.prev = root.next = root self.__map = {} self.update(*args, **kwds) @@ -62,8 +64,9 @@ root = self.__root last = root.prev link.prev, link.next, link.key = last, root, key - last.next = root.prev = proxy(link) - dict.__setitem__(self, key, value) + last.next = link + root.prev = proxy(link) + dict_setitem(self, key, value) def __delitem__(self, key, dict_delitem=dict.__delitem__): 'od.__delitem__(y) <==> del od[y]' @@ -97,10 +100,10 @@ def __reduce__(self): 'Return state information for pickling' items = [[k, self[k]] for k in self] - tmp = self.__map, self.__root - del self.__map, self.__root + tmp = self.__map, self.__root, self.__hardroot + del self.__map, self.__root, self.__hardroot inst_dict = vars(self).copy() - self.__map, self.__root = tmp + self.__map, self.__root, self.__hardroot = tmp if inst_dict: return (self.__class__, (items,), inst_dict) return self.__class__, (items,) From python-checkins at python.org Tue Sep 14 21:41:23 2010 From: python-checkins at python.org (brett.cannon) Date: Tue, 14 Sep 2010 21:41:23 +0200 (CEST) Subject: [Python-checkins] r84819 - in python/branches/py3k: Misc/NEWS setup.py Message-ID: <20100914194123.5A95AFAAC@mail.python.org> Author: brett.cannon Date: Tue Sep 14 21:41:23 2010 New Revision: 84819 Log: setup.py was trying to build _weakref which is redundant as it's a built-in module. Closes issue #9848. Thanks to Arfrever Frehtes Taifersar Arahesis for the bug report. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/setup.py Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Sep 14 21:41:23 2010 @@ -183,6 +183,9 @@ Build ----- +- Issue #9848: Stopping trying to build _weakref in setup.py as it is a + built-in module. + - Issue #9806: python-config now has an ``--extension-suffix`` option that outputs the suffix for dynamic libraries including the ABI version name defined by PEP 3149. Modified: python/branches/py3k/setup.py ============================================================================== --- python/branches/py3k/setup.py (original) +++ python/branches/py3k/setup.py Tue Sep 14 21:41:23 2010 @@ -456,9 +456,6 @@ # on pretty much any POSIXish platform. # - # Some modules that are normally always on: - exts.append( Extension('_weakref', ['_weakref.c']) ) - # array objects exts.append( Extension('array', ['arraymodule.c']) ) # complex math library functions From python-checkins at python.org Tue Sep 14 23:24:25 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 14 Sep 2010 23:24:25 +0200 (CEST) Subject: [Python-checkins] r84820 - python/branches/py3k/Lib/test/test_socket.py Message-ID: <20100914212425.D1709EE9EC@mail.python.org> Author: antoine.pitrou Date: Tue Sep 14 23:24:25 2010 New Revision: 84820 Log: Make testDefaults in test.test_socket.BasicSocketPairTest more reliable. Modified: python/branches/py3k/Lib/test/test_socket.py Modified: python/branches/py3k/Lib/test/test_socket.py ============================================================================== --- python/branches/py3k/Lib/test/test_socket.py (original) +++ python/branches/py3k/Lib/test/test_socket.py Tue Sep 14 23:24:25 2010 @@ -815,22 +815,20 @@ def __init__(self, methodName='runTest'): SocketPairTest.__init__(self, methodName=methodName) + def _check_defaults(self, sock): + self.assertIsInstance(sock, socket.socket) + if hasattr(socket, 'AF_UNIX'): + self.assertEqual(sock.family, socket.AF_UNIX) + else: + self.assertEqual(sock.family, socket.AF_INET) + self.assertEqual(sock.type, socket.SOCK_STREAM) + self.assertEqual(sock.proto, 0) + def _testDefaults(self): - pass + self._check_defaults(self.cli) def testDefaults(self): - self.assertIsInstance(self.cli, socket.socket) - self.assertIsInstance(self.serv, socket.socket) - if hasattr(socket, 'AF_UNIX'): - self.assertEqual(self.cli.family, socket.AF_UNIX) - self.assertEqual(self.serv.family, socket.AF_UNIX) - else: - self.assertEqual(self.cli.family, socket.AF_INET) - self.assertEqual(self.serv.family, socket.AF_INET) - self.assertEqual(self.cli.type, socket.SOCK_STREAM) - self.assertEqual(self.serv.type, socket.SOCK_STREAM) - self.assertEqual(self.cli.proto, 0) - self.assertEqual(self.serv.proto, 0) + self._check_defaults(self.serv) def testRecv(self): msg = self.serv.recv(1024) From python-checkins at python.org Wed Sep 15 00:55:13 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 15 Sep 2010 00:55:13 +0200 (CEST) Subject: [Python-checkins] r84821 - python/branches/py3k/Lib/functools.py Message-ID: <20100914225513.F38C1EE982@mail.python.org> Author: raymond.hettinger Date: Wed Sep 15 00:55:13 2010 New Revision: 84821 Log: Future proof total_ordering against changes in methods defined on object. Modified: python/branches/py3k/Lib/functools.py Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Wed Sep 15 00:55:13 2010 @@ -82,7 +82,7 @@ ('__lt__', lambda self, other: not self >= other)] } # Find comparisons not inherited from object. - roots = [op for op in convert if getattr(cls, op) is not getattr(object, op)] + roots = [op for op in convert if getattr(cls, op, None) is not getattr(object, op, None)] if not roots: raise ValueError('must define at least one ordering operation: < > <= >=') root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ From python-checkins at python.org Wed Sep 15 01:13:42 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 15 Sep 2010 01:13:42 +0200 (CEST) Subject: [Python-checkins] r84822 - python/branches/py3k/Doc/library/functions.rst Message-ID: <20100914231342.4B430EE98D@mail.python.org> Author: raymond.hettinger Date: Wed Sep 15 01:13:42 2010 New Revision: 84822 Log: Issue 9802: Document min()/max() sort stability Modified: python/branches/py3k/Doc/library/functions.rst Modified: python/branches/py3k/Doc/library/functions.rst ============================================================================== --- python/branches/py3k/Doc/library/functions.rst (original) +++ python/branches/py3k/Doc/library/functions.rst Wed Sep 15 01:13:42 2010 @@ -634,6 +634,10 @@ The optional keyword-only *key* argument specifies a one-argument ordering function like that used for :meth:`list.sort`. + If multiple items are maximal, the function returns the first one encountered. + This is consistent with other sort-stability preserving tools such as + ``sorted(iterable, key=keyfunc, reverse=True)[0]` and + ``heapq.nlargest(1, iterable, key=keyfunc)``. .. function:: memoryview(obj) :noindex: @@ -651,6 +655,10 @@ The optional keyword-only *key* argument specifies a one-argument ordering function like that used for :meth:`list.sort`. + If multiple items are minimal, the function returns the first one encountered. + This is consistent with other sort-stability preserving tools such as + ``sorted(iterable, key=keyfunc)[0]` and + ``heapq.nsmallest(1, iterable, key=keyfunc)``. .. function:: next(iterator[, default]) From python-checkins at python.org Wed Sep 15 02:09:26 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 15 Sep 2010 02:09:26 +0200 (CEST) Subject: [Python-checkins] r84823 - python/branches/py3k/Doc/reference/expressions.rst Message-ID: <20100915000926.8AFF3EE9B1@mail.python.org> Author: raymond.hettinger Date: Wed Sep 15 02:09:26 2010 New Revision: 84823 Log: Clarify where support for negative indices fall in the language hierarchy. Modified: python/branches/py3k/Doc/reference/expressions.rst Modified: python/branches/py3k/Doc/reference/expressions.rst ============================================================================== --- python/branches/py3k/Doc/reference/expressions.rst (original) +++ python/branches/py3k/Doc/reference/expressions.rst Wed Sep 15 02:09:26 2010 @@ -518,11 +518,18 @@ value in the mapping that corresponds to that key. (The expression list is a tuple except if it has exactly one item.) -If the primary is a sequence, the expression (list) must evaluate to an integer. -If this value is negative, the length of the sequence is added to it (so that, -e.g., ``x[-1]`` selects the last item of ``x``.) The resulting value must be a -nonnegative integer less than the number of items in the sequence, and the -subscription selects the item whose index is that value (counting from zero). +If the primary is a sequence, the expression (list) must evaluate to an integer +or a slice (as discussed in the following section). + +The formal syntax makes no special provision for negative indices in +sequences; however, built-in sequences all provide a :meth:`__getitem__` +method that interprets negative indices by adding the length of the sequence +to the index (so that ``x[-1]`` selects the last item of ``x``). The +resulting value must be a nonnegative integer less than the number of items in +the sequence, and the subscription selects the item whose index is that value +(counting from zero). Since the support for negative indices and slicing +occurs in the object's :meth:`__getitem__` method, subclasses overriding +this method will need to explicitly add that support. .. index:: single: character From python-checkins at python.org Wed Sep 15 02:12:05 2010 From: python-checkins at python.org (raymond.hettinger) Date: Wed, 15 Sep 2010 02:12:05 +0200 (CEST) Subject: [Python-checkins] r84824 - python/branches/release31-maint/Doc/reference/expressions.rst Message-ID: <20100915001205.5510AF89C@mail.python.org> Author: raymond.hettinger Date: Wed Sep 15 02:12:05 2010 New Revision: 84824 Log: Clarify where support for negative indices fall in the language hierarchy. Modified: python/branches/release31-maint/Doc/reference/expressions.rst Modified: python/branches/release31-maint/Doc/reference/expressions.rst ============================================================================== --- python/branches/release31-maint/Doc/reference/expressions.rst (original) +++ python/branches/release31-maint/Doc/reference/expressions.rst Wed Sep 15 02:12:05 2010 @@ -518,11 +518,18 @@ value in the mapping that corresponds to that key. (The expression list is a tuple except if it has exactly one item.) -If the primary is a sequence, the expression (list) must evaluate to an integer. -If this value is negative, the length of the sequence is added to it (so that, -e.g., ``x[-1]`` selects the last item of ``x``.) The resulting value must be a -nonnegative integer less than the number of items in the sequence, and the -subscription selects the item whose index is that value (counting from zero). +If the primary is a sequence, the expression (list) must evaluate to an integer +or a slice (as discussed in the following section). + +The formal syntax makes no special provision for negative indices in +sequences; however, built-in sequences all provide a :meth:`__getitem__` +method that interprets negative indices by adding the length of the sequence +to the index (so that ``x[-1]`` selects the last item of ``x``). The +resulting value must be a nonnegative integer less than the number of items in +the sequence, and the subscription selects the item whose index is that value +(counting from zero). Since the support for negative indices and slicing +occurs in the object's :meth:`__getitem__` method, subclasses overriding +this method will need to explicitly add that support. .. index:: single: character From solipsis at pitrou.net Wed Sep 15 05:09:27 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 15 Sep 2010 05:09:27 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84823): sum=0 Message-ID: py3k results for svn r84823 (hg cset 175a1d16210b) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogL6YDRg', '-x'] From python-checkins at python.org Wed Sep 15 10:39:25 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 10:39:25 +0200 (CEST) Subject: [Python-checkins] r84825 - python/branches/py3k/Lib/socket.py Message-ID: <20100915083925.3B3E7EE989@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 10:39:25 2010 New Revision: 84825 Log: Add a comment explaining why SocketIO is needed Modified: python/branches/py3k/Lib/socket.py Modified: python/branches/py3k/Lib/socket.py ============================================================================== --- python/branches/py3k/Lib/socket.py (original) +++ python/branches/py3k/Lib/socket.py Wed Sep 15 10:39:25 2010 @@ -228,6 +228,13 @@ the raw I/O interface on top of a socket object. """ + # One might wonder why not let FileIO do the job instead. There are two + # main reasons why FileIO is not adapted: + # - it wouldn't work under Windows (where you can't used read() and + # write() on a socket handle) + # - it wouldn't work with socket timeouts (FileIO would ignore the + # timeout and consider the socket non-blocking) + # XXX More docs def __init__(self, sock, mode): From python-checkins at python.org Wed Sep 15 11:32:45 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 11:32:45 +0200 (CEST) Subject: [Python-checkins] r84826 - in python/branches/py3k: Doc/library/socket.rst Lib/socket.py Message-ID: <20100915093245.76BECEE98A@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 11:32:45 2010 New Revision: 84826 Log: Improve docs for socket.makefile() and SocketIO Modified: python/branches/py3k/Doc/library/socket.rst python/branches/py3k/Lib/socket.py Modified: python/branches/py3k/Doc/library/socket.rst ============================================================================== --- python/branches/py3k/Doc/library/socket.rst (original) +++ python/branches/py3k/Doc/library/socket.rst Wed Sep 15 11:32:45 2010 @@ -622,10 +622,9 @@ arguments are interpreted the same way as by the built-in :func:`open` function. - The returned file object references a :cfunc:`dup`\ ped version of the - socket file descriptor, so the file object and socket object may be - closed or garbage-collected independently. The socket must be in - blocking mode (it can not have a timeout). + Closing the file object won't close the socket unless there are no + remaining references to the socket. The socket must be in blocking mode + (it can not have a timeout). .. method:: socket.recv(bufsize[, flags]) Modified: python/branches/py3k/Lib/socket.py ============================================================================== --- python/branches/py3k/Lib/socket.py (original) +++ python/branches/py3k/Lib/socket.py Wed Sep 15 11:32:45 2010 @@ -54,6 +54,8 @@ errno = None EBADF = getattr(errno, 'EBADF', 9) EINTR = getattr(errno, 'EINTR', 4) +EAGAIN = getattr(errno, 'EAGAIN', 11) +EWOULDBLOCK = getattr(errno, 'EWOULDBLOCK', 11) __all__ = ["getfqdn", "create_connection"] __all__.extend(os._get_exports_list(_socket)) @@ -249,6 +251,13 @@ self._writing = "w" in mode def readinto(self, b): + """Read up to len(b) bytes into the writable buffer *b* and return + the number of bytes read. If the socket is non-blocking and no bytes + are available, None is returned. + + If *b* is non-empty, a 0 return value indicates that the connection + was shutdown at the other end. + """ self._checkClosed() self._checkReadable() while True: @@ -260,17 +269,28 @@ raise def write(self, b): + """Write the given bytes or bytearray object *b* to the socket + and return the number of bytes written. This can be less than + len(b) if not all data could be written. If the socket is + non-blocking and no bytes could be written None is returned. + """ self._checkClosed() self._checkWritable() return self._sock.send(b) def readable(self): + """True if the SocketIO is open for reading. + """ return self._reading and not self.closed def writable(self): + """True if the SocketIO is open for writing. + """ return self._writing and not self.closed def fileno(self): + """Return the file descriptor of the underlying socket. + """ self._checkClosed() return self._sock.fileno() @@ -283,6 +303,9 @@ return self._mode def close(self): + """Close the SocketIO object. This doesn't close the underlying + socket, except if all references to it have disappeared. + """ if self.closed: return io.RawIOBase.close(self) From python-checkins at python.org Wed Sep 15 11:58:26 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 11:58:26 +0200 (CEST) Subject: [Python-checkins] r84827 - in python/branches/py3k/Doc: glossary.rst library/socket.rst reference/datamodel.rst Message-ID: <20100915095827.00B3AEE989@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 11:58:26 2010 New Revision: 84827 Log: Add a glossary entry for file objects. Modified: python/branches/py3k/Doc/glossary.rst python/branches/py3k/Doc/library/socket.rst python/branches/py3k/Doc/reference/datamodel.rst Modified: python/branches/py3k/Doc/glossary.rst ============================================================================== --- python/branches/py3k/Doc/glossary.rst (original) +++ python/branches/py3k/Doc/glossary.rst Wed Sep 15 11:58:26 2010 @@ -184,6 +184,23 @@ A module written in C or C++, using Python's C API to interact with the core and with user code. + file object + An object exposing a file-oriented API (with methods such as + :meth:`read()` or :meth:`write()`) to an underlying resource. + Depending on the way it was created, a file object can mediate access + to a real on-disk file or to another other type of storage or + communication device (for example standard input/output, in-memory + buffers, sockets, pipes, etc.). File objects are also called + :dfn:`file-like objects` or :dfn:`streams`. + + There are actually three categories of file objects: raw binary + files, buffered binary files and text files. Their interfaces are + defined in the :mod:`io` module. The canonical way to create a + file object is by using the :func:`open` function. + + file-like object + A synonym for :term:`file object`. + finder An object that tries to find the :term:`loader` for a module. It must implement a method named :meth:`find_module`. See :pep:`302` for Modified: python/branches/py3k/Doc/library/socket.rst ============================================================================== --- python/branches/py3k/Doc/library/socket.rst (original) +++ python/branches/py3k/Doc/library/socket.rst Wed Sep 15 11:58:26 2010 @@ -617,7 +617,7 @@ .. index:: single: I/O control; buffering - Return a :dfn:`file object` associated with the socket. The exact + Return a :term:`file object` associated with the socket. The exact returned type depends on the arguments given to :meth:`makefile`. These arguments are interpreted the same way as by the built-in :func:`open` function. Modified: python/branches/py3k/Doc/reference/datamodel.rst ============================================================================== --- python/branches/py3k/Doc/reference/datamodel.rst (original) +++ python/branches/py3k/Doc/reference/datamodel.rst Wed Sep 15 11:58:26 2010 @@ -781,9 +781,9 @@ single: stdout (in module sys) single: stderr (in module sys) - A file object represents an open file. Various shortcuts are available - to create file objects: the :func:`open` built-in function, and also - :func:`os.popen`, :func:`os.fdopen`, and the :meth:`makefile` method + A :term:`file object` represents an open file. Various shortcuts are + available to create file objects: the :func:`open` built-in function, and + also :func:`os.popen`, :func:`os.fdopen`, and the :meth:`makefile` method of socket objects (and perhaps by other functions or methods provided by extension modules). From python-checkins at python.org Wed Sep 15 12:08:31 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 12:08:31 +0200 (CEST) Subject: [Python-checkins] r84828 - python/branches/py3k/Doc/faq/library.rst Message-ID: <20100915100831.AE327EE98A@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 12:08:31 2010 New Revision: 84828 Log: Update file-related information in the FAQ. Modified: python/branches/py3k/Doc/faq/library.rst Modified: python/branches/py3k/Doc/faq/library.rst ============================================================================== --- python/branches/py3k/Doc/faq/library.rst (original) +++ python/branches/py3k/Doc/faq/library.rst Wed Sep 15 12:08:31 2010 @@ -458,7 +458,7 @@ To rename a file, use ``os.rename(old_path, new_path)``. -To truncate a file, open it using ``f = open(filename, "r+")``, and use +To truncate a file, open it using ``f = open(filename, "rb+")``, and use ``f.truncate(offset)``; offset defaults to the current seek position. There's also ```os.ftruncate(fd, offset)`` for files opened with :func:`os.open`, where ``fd`` is the file descriptor (a small integer). @@ -487,9 +487,9 @@ import struct - f = open(filename, "rb") # Open in binary mode for portability - s = f.read(8) - x, y, z = struct.unpack(">hhl", s) + with open(filename, "rb") as f: + s = f.read(8) + x, y, z = struct.unpack(">hhl", s) The '>' in the format string forces big-endian data; the letter 'h' reads one "short integer" (2 bytes), and 'l' reads one "long integer" (4 bytes) from the @@ -498,6 +498,13 @@ For data that is more regular (e.g. a homogeneous list of ints or thefloats), you can also use the :mod:`array` module. + .. note:: + To read and write binary data, it is mandatory to open the file in + binary mode (here, passing ``"rb"`` to :func:`open`). If you use + ``"r"`` instead (the default), the file will be open in text mode + and ``f.read()`` will return :class:`str` objects rather than + :class:`bytes` objects. + I can't seem to use os.read() on a pipe created with os.popen(); why? --------------------------------------------------------------------- @@ -603,28 +610,29 @@ Why doesn't closing sys.stdout (stdin, stderr) really close it? --------------------------------------------------------------- -Python file objects are a high-level layer of abstraction on top of C streams, -which in turn are a medium-level layer of abstraction on top of (among other -things) low-level C file descriptors. - -For most file objects you create in Python via the built-in ``open`` -constructor, ``f.close()`` marks the Python file object as being closed from -Python's point of view, and also arranges to close the underlying C stream. -This also happens automatically in ``f``'s destructor, when ``f`` becomes -garbage. +Python :term:`file objects ` are a high-level layer of +abstraction on low-level C file descriptors. + +For most file objects you create in Python via the built-in :func:`open` +function, ``f.close()`` marks the Python file object as being closed from +Python's point of view, and also arranges to close the underlying C file +descriptor. This also happens automatically in ``f``'s destructor, when +``f`` becomes garbage. But stdin, stdout and stderr are treated specially by Python, because of the special status also given to them by C. Running ``sys.stdout.close()`` marks the Python-level file object as being closed, but does *not* close the -associated C stream. +associated C file descriptor. + +To close the underlying C file descriptor for one of these three, you should +first be sure that's what you really want to do (e.g., you may confuse +extension modules trying to do I/O). If it is, use :func:`os.close`:: + + os.close(stdin.fileno()) + os.close(stdout.fileno()) + os.close(stderr.fileno()) -To close the underlying C stream for one of these three, you should first be -sure that's what you really want to do (e.g., you may confuse extension modules -trying to do I/O). If it is, use os.close:: - - os.close(0) # close C's stdin stream - os.close(1) # close C's stdout stream - os.close(2) # close C's stderr stream +Or you can use the numeric constants 0, 1 and 2, respectively. Network/Internet Programming From python-checkins at python.org Wed Sep 15 13:11:28 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 13:11:28 +0200 (CEST) Subject: [Python-checkins] r84829 - in python/branches/py3k/Doc: faq/design.rst library/aifc.rst library/array.rst library/asyncore.rst library/base64.rst library/bz2.rst library/configparser.rst library/csv.rst library/email.generator.rst library/email.parser.rst library/exceptions.rst library/filesys.rst library/formatter.rst library/ftplib.rst library/gettext.rst library/gzip.rst library/http.client.rst library/imp.rst library/mmap.rst library/nntplib.rst library/os.rst library/pickle.rst library/quopri.rst library/select.rst library/stdtypes.rst library/subprocess.rst library/sys.rst library/tarfile.rst library/tempfile.rst library/termios.rst library/weakref.rst library/xml.etree.elementtree.rst tutorial/inputoutput.rst Message-ID: <20100915111128.CF78EEE989@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 13:11:28 2010 New Revision: 84829 Log: Add cross-references to the glossary entry for file objects. Modified: python/branches/py3k/Doc/faq/design.rst python/branches/py3k/Doc/library/aifc.rst python/branches/py3k/Doc/library/array.rst python/branches/py3k/Doc/library/asyncore.rst python/branches/py3k/Doc/library/base64.rst python/branches/py3k/Doc/library/bz2.rst python/branches/py3k/Doc/library/configparser.rst python/branches/py3k/Doc/library/csv.rst python/branches/py3k/Doc/library/email.generator.rst python/branches/py3k/Doc/library/email.parser.rst python/branches/py3k/Doc/library/exceptions.rst python/branches/py3k/Doc/library/filesys.rst python/branches/py3k/Doc/library/formatter.rst python/branches/py3k/Doc/library/ftplib.rst python/branches/py3k/Doc/library/gettext.rst python/branches/py3k/Doc/library/gzip.rst python/branches/py3k/Doc/library/http.client.rst python/branches/py3k/Doc/library/imp.rst python/branches/py3k/Doc/library/mmap.rst python/branches/py3k/Doc/library/nntplib.rst python/branches/py3k/Doc/library/os.rst python/branches/py3k/Doc/library/pickle.rst python/branches/py3k/Doc/library/quopri.rst python/branches/py3k/Doc/library/select.rst python/branches/py3k/Doc/library/stdtypes.rst python/branches/py3k/Doc/library/subprocess.rst python/branches/py3k/Doc/library/sys.rst python/branches/py3k/Doc/library/tarfile.rst python/branches/py3k/Doc/library/tempfile.rst python/branches/py3k/Doc/library/termios.rst python/branches/py3k/Doc/library/weakref.rst python/branches/py3k/Doc/library/xml.etree.elementtree.rst python/branches/py3k/Doc/tutorial/inputoutput.rst Modified: python/branches/py3k/Doc/faq/design.rst ============================================================================== --- python/branches/py3k/Doc/faq/design.rst (original) +++ python/branches/py3k/Doc/faq/design.rst Wed Sep 15 13:11:28 2010 @@ -210,8 +210,8 @@ is hidden at the bottom of the loop. The best approach is to use iterators, making it possible to loop through -objects using the ``for`` statement. For example, in the current version of -Python file objects support the iterator protocol, so you can now write simply:: +objects using the ``for`` statement. For example, :term:`file objects +` support the iterator protocol, so you can write simply:: for line in f: ... # do something with line... Modified: python/branches/py3k/Doc/library/aifc.rst ============================================================================== --- python/branches/py3k/Doc/library/aifc.rst (original) +++ python/branches/py3k/Doc/library/aifc.rst Wed Sep 15 13:11:28 2010 @@ -40,10 +40,10 @@ .. function:: open(file, mode=None) Open an AIFF or AIFF-C file and return an object instance with methods that are - described below. The argument *file* is either a string naming a file or a file - object. *mode* must be ``'r'`` or ``'rb'`` when the file must be opened for - reading, or ``'w'`` or ``'wb'`` when the file must be opened for writing. If - omitted, ``file.mode`` is used if it exists, otherwise ``'rb'`` is used. When + described below. The argument *file* is either a string naming a file or a + :term:`file object`. *mode* must be ``'r'`` or ``'rb'`` when the file must be + opened for reading, or ``'w'`` or ``'wb'`` when the file must be opened for writing. + If omitted, ``file.mode`` is used if it exists, otherwise ``'rb'`` is used. When used for writing, the file object should be seekable, unless you know ahead of time how many samples you are going to write in total and use :meth:`writeframesraw` and :meth:`setnframes`. Modified: python/branches/py3k/Doc/library/array.rst ============================================================================== --- python/branches/py3k/Doc/library/array.rst (original) +++ python/branches/py3k/Doc/library/array.rst Wed Sep 15 13:11:28 2010 @@ -147,11 +147,11 @@ .. method:: array.fromfile(f, n) - Read *n* items (as machine values) from the file object *f* and append them to - the end of the array. If less than *n* items are available, :exc:`EOFError` is - raised, but the items that were available are still inserted into the array. - *f* must be a real built-in file object; something else with a :meth:`read` - method won't do. + Read *n* items (as machine values) from the :term:`file object` *f* and append + them to the end of the array. If less than *n* items are available, + :exc:`EOFError` is raised, but the items that were available are still + inserted into the array. *f* must be a real built-in file object; something + else with a :meth:`read` method won't do. .. method:: array.fromlist(list) @@ -214,7 +214,7 @@ .. method:: array.tofile(f) - Write all items (as machine values) to the file object *f*. + Write all items (as machine values) to the :term:`file object` *f*. .. method:: array.tolist() Modified: python/branches/py3k/Doc/library/asyncore.rst ============================================================================== --- python/branches/py3k/Doc/library/asyncore.rst (original) +++ python/branches/py3k/Doc/library/asyncore.rst Wed Sep 15 13:11:28 2010 @@ -225,9 +225,9 @@ .. class:: file_dispatcher() - A file_dispatcher takes a file descriptor or file object along with an - optional map argument and wraps it for use with the :cfunc:`poll` or - :cfunc:`loop` functions. If provided a file object or anything with a + A file_dispatcher takes a file descriptor or :term:`file object` along + with an optional map argument and wraps it for use with the :cfunc:`poll` + or :cfunc:`loop` functions. If provided a file object or anything with a :cfunc:`fileno` method, that method will be called and passed to the :class:`file_wrapper` constructor. Availability: UNIX. Modified: python/branches/py3k/Doc/library/base64.rst ============================================================================== --- python/branches/py3k/Doc/library/base64.rst (original) +++ python/branches/py3k/Doc/library/base64.rst Wed Sep 15 13:11:28 2010 @@ -122,9 +122,9 @@ .. function:: decode(input, output) Decode the contents of the binary *input* file and write the resulting binary - data to the *output* file. *input* and *output* must either be file objects - or objects that mimic the file object interface working with bytes - objects. *input* will be read until ``input.read()`` returns an empty string. + data to the *output* file. *input* and *output* must be :term:`file objects + `. *input* will be read until ``input.read()`` returns an empty + bytes object. .. function:: decodebytes(s) @@ -138,11 +138,10 @@ .. function:: encode(input, output) Encode the contents of the binary *input* file and write the resulting base64 - encoded data to the *output* file. *input* and *output* must either be file - objects or objects that mimic the file object interface working with bytes - objects. *input* will be read until ``input.read()`` returns an empty string. - :func:`encode` returns the encoded data plus a trailing newline character - (``b'\n'``). + encoded data to the *output* file. *input* and *output* must be :term:`file + objects `. *input* will be read until ``input.read()`` returns + an empty bytes object. :func:`encode` returns the encoded data plus a trailing + newline character (``b'\n'``). .. function:: encodebytes(s) Modified: python/branches/py3k/Doc/library/bz2.rst ============================================================================== --- python/branches/py3k/Doc/library/bz2.rst (original) +++ python/branches/py3k/Doc/library/bz2.rst Wed Sep 15 13:11:28 2010 @@ -25,8 +25,8 @@ * :class:`BZ2File` class implements universal newline support; -* :class:`BZ2File` class offers an optimized line iteration using the readahead - algorithm borrowed from file objects; +* :class:`BZ2File` class offers an optimized line iteration using a readahead + algorithm; * Sequential (de)compression supported by :class:`BZ2Compressor` and :class:`BZ2Decompressor` classes; Modified: python/branches/py3k/Doc/library/configparser.rst ============================================================================== --- python/branches/py3k/Doc/library/configparser.rst (original) +++ python/branches/py3k/Doc/library/configparser.rst Wed Sep 15 13:11:28 2010 @@ -422,7 +422,7 @@ .. method:: RawConfigParser.write(fileobject, space_around_delimiters=True) - Write a representation of the configuration to the specified file object, + Write a representation of the configuration to the specified :term:`file object`, which must be opened in text mode (accepting strings). This representation can be parsed by a future :meth:`read` call. If ``space_around_delimiters`` is ``True`` (the default), delimiters between keys and values are surrounded Modified: python/branches/py3k/Doc/library/csv.rst ============================================================================== --- python/branches/py3k/Doc/library/csv.rst (original) +++ python/branches/py3k/Doc/library/csv.rst Wed Sep 15 13:11:28 2010 @@ -50,9 +50,9 @@ Return a reader object which will iterate over lines in the given *csvfile*. *csvfile* can be any object which supports the :term:`iterator` protocol and returns a - string each time its :meth:`!next` method is called --- file objects and list - objects are both suitable. If *csvfile* is a file object, it should be opened - with ``newline=''``. [#]_ An optional + string each time its :meth:`!next` method is called --- :term:`file objects + ` and list objects are both suitable. If *csvfile* is a file object, + it should be opened with ``newline=''``. [#]_ An optional *dialect* parameter can be given which is used to define a set of parameters specific to a particular CSV dialect. It may be an instance of a subclass of the :class:`Dialect` class or one of the strings returned by the Modified: python/branches/py3k/Doc/library/email.generator.rst ============================================================================== --- python/branches/py3k/Doc/library/email.generator.rst (original) +++ python/branches/py3k/Doc/library/email.generator.rst Wed Sep 15 13:11:28 2010 @@ -28,9 +28,9 @@ .. class:: Generator(outfp, mangle_from_=True, maxheaderlen=78) - The constructor for the :class:`Generator` class takes a file-like object called - *outfp* for an argument. *outfp* must support the :meth:`write` method and be - usable as the output file for the :func:`print` function. + The constructor for the :class:`Generator` class takes a :term:`file-like object` + called *outfp* for an argument. *outfp* must support the :meth:`write` method + and be usable as the output file for the :func:`print` function. Optional *mangle_from_* is a flag that, when ``True``, puts a ``>`` character in front of any line in the body that starts exactly as ``From``, i.e. ``From`` Modified: python/branches/py3k/Doc/library/email.parser.rst ============================================================================== --- python/branches/py3k/Doc/library/email.parser.rst (original) +++ python/branches/py3k/Doc/library/email.parser.rst Wed Sep 15 13:11:28 2010 @@ -154,9 +154,9 @@ .. function:: message_from_file(fp[, _class][, strict]) - Return a message object structure tree from an open file object. This is - exactly equivalent to ``Parser().parse(fp)``. Optional *_class* and *strict* - are interpreted as with the :class:`Parser` class constructor. + Return a message object structure tree from an open :term:`file object`. + This is exactly equivalent to ``Parser().parse(fp)``. Optional *_class* + and *strict* are interpreted as with the :class:`Parser` class constructor. Here's an example of how you might use this at an interactive Python prompt:: Modified: python/branches/py3k/Doc/library/exceptions.rst ============================================================================== --- python/branches/py3k/Doc/library/exceptions.rst (original) +++ python/branches/py3k/Doc/library/exceptions.rst Wed Sep 15 13:11:28 2010 @@ -134,8 +134,8 @@ .. exception:: IOError Raised when an I/O operation (such as the built-in :func:`print` or - :func:`open` functions or a method of a file object) fails for an I/O-related - reason, e.g., "file not found" or "disk full". + :func:`open` functions or a method of a :term:`file object`) fails for an + I/O-related reason, e.g., "file not found" or "disk full". This class is derived from :exc:`EnvironmentError`. See the discussion above for more information on exception instance attributes. Modified: python/branches/py3k/Doc/library/filesys.rst ============================================================================== --- python/branches/py3k/Doc/library/filesys.rst (original) +++ python/branches/py3k/Doc/library/filesys.rst Wed Sep 15 13:11:28 2010 @@ -27,8 +27,8 @@ .. seealso:: Module :mod:`os` - Operating system interfaces, including functions to work with files at a lower - level than the built-in file object. + Operating system interfaces, including functions to work with files at a + lower level than Python :term:`file objects `. Module :mod:`io` Python's built-in I/O library, including both abstract classes and Modified: python/branches/py3k/Doc/library/formatter.rst ============================================================================== --- python/branches/py3k/Doc/library/formatter.rst (original) +++ python/branches/py3k/Doc/library/formatter.rst Wed Sep 15 13:11:28 2010 @@ -339,8 +339,8 @@ .. class:: DumbWriter(file=None, maxcol=72) - Simple writer class which writes output on the file object passed in as *file* - or, if *file* is omitted, on standard output. The output is simply word-wrapped - to the number of columns specified by *maxcol*. This class is suitable for - reflowing a sequence of paragraphs. + Simple writer class which writes output on the :term:`file object` passed + in as *file* or, if *file* is omitted, on standard output. The output is + simply word-wrapped to the number of columns specified by *maxcol*. This + class is suitable for reflowing a sequence of paragraphs. Modified: python/branches/py3k/Doc/library/ftplib.rst ============================================================================== --- python/branches/py3k/Doc/library/ftplib.rst (original) +++ python/branches/py3k/Doc/library/ftplib.rst Wed Sep 15 13:11:28 2010 @@ -245,12 +245,12 @@ .. method:: FTP.storbinary(cmd, file, blocksize=8192, callback=None, rest=None) Store a file in binary transfer mode. *cmd* should be an appropriate - ``STOR`` command: ``"STOR filename"``. *file* is an open file object which is - read until EOF using its :meth:`read` method in blocks of size *blocksize* to - provide the data to be stored. The *blocksize* argument defaults to 8192. - *callback* is an optional single parameter callable that is called - on each block of data after it is sent. *rest* means the same thing as in - the :meth:`transfercmd` method. + ``STOR`` command: ``"STOR filename"``. *file* is an open :term:`file object` + which is read until EOF using its :meth:`read` method in blocks of size + *blocksize* to provide the data to be stored. The *blocksize* argument + defaults to 8192. *callback* is an optional single parameter callable that + is called on each block of data after it is sent. *rest* means the same thing + as in the :meth:`transfercmd` method. .. versionchanged:: 3.2 *rest* parameter added. @@ -260,8 +260,8 @@ Store a file in ASCII transfer mode. *cmd* should be an appropriate ``STOR`` command (see :meth:`storbinary`). Lines are read until EOF from the - open file object *file* using its :meth:`readline` method to provide the data to - be stored. *callback* is an optional single parameter callable + open :term:`file object` *file* using its :meth:`readline` method to provide + the data to be stored. *callback* is an optional single parameter callable that is called on each line after it is sent. Modified: python/branches/py3k/Doc/library/gettext.rst ============================================================================== --- python/branches/py3k/Doc/library/gettext.rst (original) +++ python/branches/py3k/Doc/library/gettext.rst Wed Sep 15 13:11:28 2010 @@ -173,8 +173,8 @@ associated :file:`.mo` file paths. Instances with identical :file:`.mo` file names are cached. The actual class instantiated is either *class_* if provided, otherwise :class:`GNUTranslations`. The class's constructor must - take a single file object argument. If provided, *codeset* will change the - charset used to encode translated strings in the :meth:`lgettext` and + take a single :term:`file object` argument. If provided, *codeset* will change + the charset used to encode translated strings in the :meth:`lgettext` and :meth:`lngettext` methods. If multiple files are found, later files are used as fallbacks for earlier ones. @@ -219,7 +219,7 @@ .. class:: NullTranslations(fp=None) - Takes an optional file object *fp*, which is ignored by the base class. + Takes an optional :term:`file object` *fp*, which is ignored by the base class. Initializes "protected" instance variables *_info* and *_charset* which are set by derived classes, as well as *_fallback*, which is set through :meth:`add_fallback`. It then calls ``self._parse(fp)`` if *fp* is not Modified: python/branches/py3k/Doc/library/gzip.rst ============================================================================== --- python/branches/py3k/Doc/library/gzip.rst (original) +++ python/branches/py3k/Doc/library/gzip.rst Wed Sep 15 13:11:28 2010 @@ -9,10 +9,9 @@ The data compression is provided by the :mod:`zlib` module. -The :mod:`gzip` module provides the :class:`GzipFile` class which is modeled -after Python's File Object. The :class:`GzipFile` class reads and writes -:program:`gzip`\ -format files, automatically compressing or decompressing the -data so that it looks like an ordinary file object. +The :mod:`gzip` module provides the :class:`GzipFile` class. The :class:`GzipFile` +class reads and writes :program:`gzip`\ -format files, automatically compressing +or decompressing the data so that it looks like an ordinary :term:`file object`. Note that additional file formats which can be decompressed by the :program:`gzip` and :program:`gunzip` programs, such as those produced by @@ -27,7 +26,7 @@ .. class:: GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None) Constructor for the :class:`GzipFile` class, which simulates most of the methods - of a file object, with the exception of the :meth:`readinto` and + of a :term:`file object`, with the exception of the :meth:`readinto` and :meth:`truncate` methods. At least one of *fileobj* and *filename* must be given a non-trivial value. Modified: python/branches/py3k/Doc/library/http.client.rst ============================================================================== --- python/branches/py3k/Doc/library/http.client.rst (original) +++ python/branches/py3k/Doc/library/http.client.rst Wed Sep 15 13:11:28 2010 @@ -367,7 +367,7 @@ object. The Content-Length header is set to the length of the string. - The *body* may also be an open file object, in which case the + The *body* may also be an open :term:`file object`, in which case the contents of the file is sent; this file object should support ``fileno()`` and ``read()`` methods. The header Content-Length is automatically set to the length of the file as reported by Modified: python/branches/py3k/Doc/library/imp.rst ============================================================================== --- python/branches/py3k/Doc/library/imp.rst (original) +++ python/branches/py3k/Doc/library/imp.rst Wed Sep 15 13:11:28 2010 @@ -48,8 +48,8 @@ If search is successful, the return value is a 3-element tuple ``(file, pathname, description)``: - *file* is an open file object positioned at the beginning, *pathname* is the - pathname of the file found, and *description* is a 3-element tuple as + *file* is an open :term:`file object` positioned at the beginning, *pathname* + is the pathname of the file found, and *description* is a 3-element tuple as contained in the list returned by :func:`get_suffixes` describing the kind of module found. Modified: python/branches/py3k/Doc/library/mmap.rst ============================================================================== --- python/branches/py3k/Doc/library/mmap.rst (original) +++ python/branches/py3k/Doc/library/mmap.rst Wed Sep 15 13:11:28 2010 @@ -5,14 +5,13 @@ :synopsis: Interface to memory-mapped files for Unix and Windows. -Memory-mapped file objects behave like both :class:`bytes` and like file -objects. Unlike normal :class:`bytes` objects, however, these are mutable. -You can use mmap objects in most places where :class:`bytes` are expected; for -example, you can use the :mod:`re` module to search through a memory-mapped file. -Since they're mutable, you can change a single byte by doing ``obj[index] = 97``, -or change a subsequence by assigning to a slice: ``obj[i1:i2] = b'...'``. -You can also read and write data starting at the current file position, and -:meth:`seek` through the file to different positions. +Memory-mapped file objects behave like both :class:`bytearray` and like +:term:`file objects `. You can use mmap objects in most places +where :class:`bytearray` are expected; for example, you can use the :mod:`re` +module to search through a memory-mapped file. You can also change a single +byte by doing ``obj[index] = 97``, or change a subsequence by assigning to a +slice: ``obj[i1:i2] = b'...'``. You can also read and write data starting at +the current file position, and :meth:`seek` through the file to different positions. A memory-mapped file is created by the :class:`mmap` constructor, which is different on Unix and on Windows. In either case you must provide a file Modified: python/branches/py3k/Doc/library/nntplib.rst ============================================================================== --- python/branches/py3k/Doc/library/nntplib.rst (original) +++ python/branches/py3k/Doc/library/nntplib.rst Wed Sep 15 13:11:28 2010 @@ -143,9 +143,9 @@ *groups* is a list of group names that are new since the given date and time. If the *file* parameter is supplied, then the output of the ``NEWGROUPS`` command is stored in a file. If *file* is a string, then the method will open a file - object with that name, write to it then close it. If *file* is a file object, - then it will start calling :meth:`write` on it to store the lines of the command - output. If *file* is supplied, then the returned *list* is an empty list. + object with that name, write to it then close it. If *file* is a :term:`file + object`, then it will start calling :meth:`write` on it to store the lines of + the command output. If *file* is supplied, then the returned *list* is an empty list. .. method:: NNTP.newnews(group, date, time, [file]) @@ -155,9 +155,9 @@ ``(response, articles)`` where *articles* is a list of message ids. If the *file* parameter is supplied, then the output of the ``NEWNEWS`` command is stored in a file. If *file* is a string, then the method will open a file - object with that name, write to it then close it. If *file* is a file object, - then it will start calling :meth:`write` on it to store the lines of the command - output. If *file* is supplied, then the returned *list* is an empty list. + object with that name, write to it then close it. If *file* is a :term:`file + object`, then it will start calling :meth:`write` on it to store the lines of the + command output. If *file* is supplied, then the returned *list* is an empty list. .. method:: NNTP.list([file]) @@ -169,9 +169,9 @@ not, and ``'m'`` if the newsgroup is moderated. (Note the ordering: *last*, *first*.) If the *file* parameter is supplied, then the output of the ``LIST`` command is stored in a file. If *file* is a string, then the method will open - a file object with that name, write to it then close it. If *file* is a file - object, then it will start calling :meth:`write` on it to store the lines of the - command output. If *file* is supplied, then the returned *list* is an empty + a file with that name, write to it then close it. If *file* is a :term:`file + object`, then it will start calling :meth:`write` on it to store the lines of + the command output. If *file* is supplied, then the returned *list* is an empty list. @@ -207,8 +207,8 @@ Send a ``HELP`` command. Return a pair ``(response, list)`` where *list* is a list of help strings. If the *file* parameter is supplied, then the output of the ``HELP`` command is stored in a file. If *file* is a string, then the - method will open a file object with that name, write to it then close it. If - *file* is a file object, then it will start calling :meth:`write` on it to store + method will open a file with that name, write to it then close it. If *file* + is a :term:`file object`, then it will start calling :meth:`write` on it to store the lines of the command output. If *file* is supplied, then the returned *list* is an empty list. @@ -243,8 +243,8 @@ Send a ``BODY`` command, where *id* has the same meaning as for :meth:`stat`. If the *file* parameter is supplied, then the body is stored in a file. If - *file* is a string, then the method will open a file object with that name, - write to it then close it. If *file* is a file object, then it will start + *file* is a string, then the method will open a file with that name, write + to it then close it. If *file* is a :term:`file object`, then it will start calling :meth:`write` on it to store the lines of the body. Return as for :meth:`head`. If *file* is supplied, then the returned *list* is an empty list. @@ -270,9 +270,9 @@ text)``, where *id* is an article number (as a string) and *text* is the text of the requested header for that article. If the *file* parameter is supplied, then the output of the ``XHDR`` command is stored in a file. If *file* is a string, - then the method will open a file object with that name, write to it then close - it. If *file* is a file object, then it will start calling :meth:`write` on it - to store the lines of the command output. If *file* is supplied, then the + then the method will open a file with that name, write to it then close it. + If *file* is a :term:`file object`, then it will start calling :meth:`write` on + it to store the lines of the command output. If *file* is supplied, then the returned *list* is an empty list. @@ -303,8 +303,8 @@ Process an ``XGTITLE`` command, returning a pair ``(response, list)``, where *list* is a list of tuples containing ``(name, title)``. If the *file* parameter is supplied, then the output of the ``XGTITLE`` command is stored in a file. - If *file* is a string, then the method will open a file object with that name, - write to it then close it. If *file* is a file object, then it will start + If *file* is a string, then the method will open a file with that name, write + to it then close it. If *file* is a :term:`file object`, then it will start calling :meth:`write` on it to store the lines of the command output. If *file* is supplied, then the returned *list* is an empty list. This is an optional NNTP extension, and may not be supported by all servers. @@ -320,8 +320,8 @@ tuple is of the form ``(article number, subject, poster, date, id, references, size, lines)``. If the *file* parameter is supplied, then the output of the ``XOVER`` command is stored in a file. If *file* is a string, then the method - will open a file object with that name, write to it then close it. If *file* - is a file object, then it will start calling :meth:`write` on it to store the + will open a file with that name, write to it then close it. If *file* is a + :term:`file object`, then it will start calling :meth:`write` on it to store the lines of the command output. If *file* is supplied, then the returned *list* is an empty list. This is an optional NNTP extension, and may not be supported by all servers. Modified: python/branches/py3k/Doc/library/os.rst ============================================================================== --- python/branches/py3k/Doc/library/os.rst (original) +++ python/branches/py3k/Doc/library/os.rst Wed Sep 15 13:11:28 2010 @@ -526,7 +526,7 @@ File Object Creation -------------------- -These functions create new file objects. (See also :func:`open`.) +These functions create new :term:`file objects `. (See also :func:`open`.) .. function:: fdopen(fd[, mode[, bufsize]]) @@ -562,7 +562,7 @@ by file descriptors. The :meth:`~file.fileno` method can be used to obtain the file descriptor -associated with a file object when required. Note that using the file +associated with a :term:`file object` when required. Note that using the file descriptor directly will bypass the file object methods, ignoring aspects such as internal buffering of data. @@ -679,9 +679,9 @@ Force write of file with filedescriptor *fd* to disk. On Unix, this calls the native :cfunc:`fsync` function; on Windows, the MS :cfunc:`_commit` function. - If you're starting with a Python file object *f*, first do ``f.flush()``, and - then do ``os.fsync(f.fileno())``, to ensure that all internal buffers associated - with *f* are written to disk. + If you're starting with a buffered Python :term:`file object` *f*, first do + ``f.flush()``, and then do ``os.fsync(f.fileno())``, to ensure that all internal + buffers associated with *f* are written to disk. Availability: Unix, and Windows. @@ -738,9 +738,9 @@ .. note:: This function is intended for low-level I/O. For normal usage, use the - built-in function :func:`open`, which returns a "file object" with + built-in function :func:`open`, which returns a :term:`file object` with :meth:`~file.read` and :meth:`~file.write` methods (and many more). To - wrap a file descriptor in a "file object", use :func:`fdopen`. + wrap a file descriptor in a file object, use :func:`fdopen`. .. function:: openpty() Modified: python/branches/py3k/Doc/library/pickle.rst ============================================================================== --- python/branches/py3k/Doc/library/pickle.rst (original) +++ python/branches/py3k/Doc/library/pickle.rst Wed Sep 15 13:11:28 2010 @@ -143,8 +143,8 @@ .. function:: dump(obj, file, protocol=None, \*, fix_imports=True) - Write a pickled representation of *obj* to the open file object *file*. This - is equivalent to ``Pickler(file, protocol).dump(obj)``. + Write a pickled representation of *obj* to the open :term:`file object` *file*. + This is equivalent to ``Pickler(file, protocol).dump(obj)``. The optional *protocol* argument tells the pickler to use the given protocol; supported protocols are 0, 1, 2, 3. The default protocol is 3; a @@ -155,8 +155,9 @@ Python needed to read the pickle produced. The *file* argument must have a write() method that accepts a single bytes - argument. It can thus be a file object opened for binary writing, a - io.BytesIO instance, or any other custom object that meets this interface. + argument. It can thus be an on-disk file opened for binary writing, a + :class:`io.BytesIO` instance, or any other custom object that meets this + interface. If *fix_imports* is True and *protocol* is less than 3, pickle will try to map the new Python 3.x names to the old module names used in Python 2.x, @@ -181,8 +182,8 @@ .. function:: load(file, \*, fix_imports=True, encoding="ASCII", errors="strict") - Read a pickled object representation from the open file object *file* and - return the reconstituted object hierarchy specified therein. This is + Read a pickled object representation from the open :term:`file object` *file* + and return the reconstituted object hierarchy specified therein. This is equivalent to ``Unpickler(file).load()``. The protocol version of the pickle is detected automatically, so no protocol @@ -191,9 +192,9 @@ The argument *file* must have two methods, a read() method that takes an integer argument, and a readline() method that requires no arguments. Both - methods should return bytes. Thus *file* can be a binary file object opened - for reading, a BytesIO object, or any other custom object that meets this - interface. + methods should return bytes. Thus *file* can be an on-disk file opened + for binary reading, a :class:`io.BytesIO` object, or any other custom object + that meets this interface. Optional keyword arguments are *fix_imports*, *encoding* and *errors*, which are used to control compatiblity support for pickle stream generated @@ -260,8 +261,8 @@ Python needed to read the pickle produced. The *file* argument must have a write() method that accepts a single bytes - argument. It can thus be a file object opened for binary writing, a - io.BytesIO instance, or any other custom object that meets this interface. + argument. It can thus be an on-disk file opened for binary writing, a + :class:`io.BytesIO` instance, or any other custom object that meets this interface. If *fix_imports* is True and *protocol* is less than 3, pickle will try to map the new Python 3.x names to the old module names used in Python 2.x, @@ -304,9 +305,9 @@ The argument *file* must have two methods, a read() method that takes an integer argument, and a readline() method that requires no arguments. Both - methods should return bytes. Thus *file* can be a binary file object opened - for reading, a BytesIO object, or any other custom object that meets this - interface. + methods should return bytes. Thus *file* can be an on-disk file object opened + for binary reading, a :class:`io.BytesIO` object, or any other custom object + that meets this interface. Optional keyword arguments are *fix_imports*, *encoding* and *errors*, which are used to control compatiblity support for pickle stream generated Modified: python/branches/py3k/Doc/library/quopri.rst ============================================================================== --- python/branches/py3k/Doc/library/quopri.rst (original) +++ python/branches/py3k/Doc/library/quopri.rst Wed Sep 15 13:11:28 2010 @@ -21,25 +21,24 @@ .. function:: decode(input, output, header=False) Decode the contents of the *input* file and write the resulting decoded binary - data to the *output* file. *input* and *output* must either be file objects or - objects that mimic the file object interface. *input* will be read until - ``input.readline()`` returns an empty string. If the optional argument *header* - is present and true, underscore will be decoded as space. This is used to decode - "Q"-encoded headers as described in :rfc:`1522`: "MIME (Multipurpose Internet - Mail Extensions) Part Two: Message Header Extensions for Non-ASCII Text". + data to the *output* file. *input* and *output* must be :term:`file objects + `. *input* will be read until ``input.readline()`` returns an + empty string. If the optional argument *header* is present and true, underscore + will be decoded as space. This is used to decode "Q"-encoded headers as + described in :rfc:`1522`: "MIME (Multipurpose Internet Mail Extensions) + Part Two: Message Header Extensions for Non-ASCII Text". .. function:: encode(input, output, quotetabs, header=False) Encode the contents of the *input* file and write the resulting quoted-printable - data to the *output* file. *input* and *output* must either be file objects or - objects that mimic the file object interface. *input* will be read until - ``input.readline()`` returns an empty string. *quotetabs* is a flag which - controls whether to encode embedded spaces and tabs; when true it encodes such - embedded whitespace, and when false it leaves them unencoded. Note that spaces - and tabs appearing at the end of lines are always encoded, as per - :rfc:`1521`. *header* is a flag which controls if spaces are encoded as - underscores as per :rfc:`1522`. + data to the *output* file. *input* and *output* must be :term:`file objects + `. *input* will be read until ``input.readline()`` returns an + empty string. *quotetabs* is a flag which controls whether to encode embedded + spaces and tabs; when true it encodes such embedded whitespace, and when + false it leaves them unencoded. Note that spaces and tabs appearing at the + end of lines are always encoded, as per :rfc:`1521`. *header* is a flag + which controls if spaces are encoded as underscores as per :rfc:`1522`. .. function:: decodestring(s, header=False) Modified: python/branches/py3k/Doc/library/select.rst ============================================================================== --- python/branches/py3k/Doc/library/select.rst (original) +++ python/branches/py3k/Doc/library/select.rst Wed Sep 15 13:11:28 2010 @@ -78,11 +78,12 @@ single: socket() (in module socket) single: popen() (in module os) - Among the acceptable object types in the sequences are Python file objects (e.g. - ``sys.stdin``, or objects returned by :func:`open` or :func:`os.popen`), socket - objects returned by :func:`socket.socket`. You may also define a :dfn:`wrapper` - class yourself, as long as it has an appropriate :meth:`fileno` method (that - really returns a file descriptor, not just a random integer). + Among the acceptable object types in the sequences are Python :term:`file + objects ` (e.g. ``sys.stdin``, or objects returned by + :func:`open` or :func:`os.popen`), socket objects returned by + :func:`socket.socket`. You may also define a :dfn:`wrapper` class yourself, + as long as it has an appropriate :meth:`fileno` method (that really returns + a file descriptor, not just a random integer). .. note:: Modified: python/branches/py3k/Doc/library/stdtypes.rst ============================================================================== --- python/branches/py3k/Doc/library/stdtypes.rst (original) +++ python/branches/py3k/Doc/library/stdtypes.rst Wed Sep 15 13:11:28 2010 @@ -2441,9 +2441,9 @@ the identifier in the :keyword:`as` clause of :keyword:`with` statements using this context manager. - An example of a context manager that returns itself is a file object. File - objects return themselves from __enter__() to allow :func:`open` to be used as - the context expression in a :keyword:`with` statement. + An example of a context manager that returns itself is a :term:`file object`. + File objects return themselves from __enter__() to allow :func:`open` to be + used as the context expression in a :keyword:`with` statement. An example of a context manager that returns a related object is the one returned by :func:`decimal.localcontext`. These managers set the active Modified: python/branches/py3k/Doc/library/subprocess.rst ============================================================================== --- python/branches/py3k/Doc/library/subprocess.rst (original) +++ python/branches/py3k/Doc/library/subprocess.rst Wed Sep 15 13:11:28 2010 @@ -108,9 +108,9 @@ *stdin*, *stdout* and *stderr* specify the executed programs' standard input, standard output and standard error file handles, respectively. Valid values are :data:`PIPE`, an existing file descriptor (a positive integer), an - existing file object, and ``None``. :data:`PIPE` indicates that a new pipe - to the child should be created. With ``None``, no redirection will occur; - the child's file handles will be inherited from the parent. Additionally, + existing :term:`file object`, and ``None``. :data:`PIPE` indicates that a + new pipe to the child should be created. With ``None``, no redirection will + occur; the child's file handles will be inherited from the parent. Additionally, *stderr* can be :data:`STDOUT`, which indicates that the stderr data from the applications should be captured into the same file handle as for stdout. @@ -409,20 +409,20 @@ .. attribute:: Popen.stdin - If the *stdin* argument was :data:`PIPE`, this attribute is a file object - that provides input to the child process. Otherwise, it is ``None``. + If the *stdin* argument was :data:`PIPE`, this attribute is a :term:`file + object` that provides input to the child process. Otherwise, it is ``None``. .. attribute:: Popen.stdout - If the *stdout* argument was :data:`PIPE`, this attribute is a file object - that provides output from the child process. Otherwise, it is ``None``. + If the *stdout* argument was :data:`PIPE`, this attribute is a :term:`file + object` that provides output from the child process. Otherwise, it is ``None``. .. attribute:: Popen.stderr - If the *stderr* argument was :data:`PIPE`, this attribute is a file object - that provides error output from the child process. Otherwise, it is + If the *stderr* argument was :data:`PIPE`, this attribute is a :term:`file + object` that provides error output from the child process. Otherwise, it is ``None``. Modified: python/branches/py3k/Doc/library/sys.rst ============================================================================== --- python/branches/py3k/Doc/library/sys.rst (original) +++ python/branches/py3k/Doc/library/sys.rst Wed Sep 15 13:11:28 2010 @@ -853,10 +853,10 @@ stdout stderr - File objects corresponding to the interpreter's standard input, output and error - streams. ``stdin`` is used for all interpreter input except for scripts but - including calls to :func:`input`. ``stdout`` is used for - the output of :func:`print` and :term:`expression` statements and for the + :term:`File objects ` corresponding to the interpreter's standard + input, output and error streams. ``stdin`` is used for all interpreter input + except for scripts but including calls to :func:`input`. ``stdout`` is used + for the output of :func:`print` and :term:`expression` statements and for the prompts of :func:`input`. The interpreter's own prompts and (almost all of) its error messages go to ``stderr``. ``stdout`` and ``stderr`` needn't be built-in file objects: any object is acceptable as long Modified: python/branches/py3k/Doc/library/tarfile.rst ============================================================================== --- python/branches/py3k/Doc/library/tarfile.rst (original) +++ python/branches/py3k/Doc/library/tarfile.rst Wed Sep 15 13:11:28 2010 @@ -66,8 +66,8 @@ *mode* ``'r'`` to avoid this. If a compression method is not supported, :exc:`CompressionError` is raised. - If *fileobj* is specified, it is used as an alternative to a file object opened - for *name*. It is supposed to be at position 0. + If *fileobj* is specified, it is used as an alternative to a :term:`file object` + opened in binary mode for *name*. It is supposed to be at position 0. For special purposes, there is a second format for *mode*: ``'filemode|[compression]'``. :func:`tarfile.open` will return a :class:`TarFile` @@ -75,7 +75,7 @@ be done on the file. If given, *fileobj* may be any object that has a :meth:`read` or :meth:`write` method (depending on the *mode*). *bufsize* specifies the blocksize and defaults to ``20 * 512`` bytes. Use this variant - in combination with e.g. ``sys.stdin``, a socket file object or a tape + in combination with e.g. ``sys.stdin``, a socket :term:`file object` or a tape device. However, such a :class:`TarFile` object is limited in that it does not allow to be accessed randomly, see :ref:`tar-examples`. The currently possible modes: @@ -355,9 +355,9 @@ .. method:: TarFile.extractfile(member) Extract a member from the archive as a file object. *member* may be a filename - or a :class:`TarInfo` object. If *member* is a regular file, a file-like object - is returned. If *member* is a link, a file-like object is constructed from the - link's target. If *member* is none of the above, :const:`None` is returned. + or a :class:`TarInfo` object. If *member* is a regular file, a :term:`file-like + object` is returned. If *member* is a link, a file-like object is constructed from + the link's target. If *member* is none of the above, :const:`None` is returned. .. note:: @@ -402,9 +402,9 @@ .. method:: TarFile.gettarinfo(name=None, arcname=None, fileobj=None) - Create a :class:`TarInfo` object for either the file *name* or the file object - *fileobj* (using :func:`os.fstat` on its file descriptor). You can modify some - of the :class:`TarInfo`'s attributes before you add it using :meth:`addfile`. + Create a :class:`TarInfo` object for either the file *name* or the :term:`file + object` *fileobj* (using :func:`os.fstat` on its file descriptor). You can modify + some of the :class:`TarInfo`'s attributes before you add it using :meth:`addfile`. If given, *arcname* specifies an alternative name for the file in the archive. Modified: python/branches/py3k/Doc/library/tempfile.rst ============================================================================== --- python/branches/py3k/Doc/library/tempfile.rst (original) +++ python/branches/py3k/Doc/library/tempfile.rst Wed Sep 15 13:11:28 2010 @@ -29,7 +29,7 @@ .. function:: TemporaryFile(mode='w+b', buffering=None, encoding=None, newline=None, suffix='', prefix='tmp', dir=None) - Return a file-like object that can be used as a temporary storage area. + Return a :term:`file-like object` that can be used as a temporary storage area. The file is created using :func:`mkstemp`. It will be destroyed as soon as it is closed (including an implicit close when the object is garbage collected). Under Unix, the directory entry for the file is removed Modified: python/branches/py3k/Doc/library/termios.rst ============================================================================== --- python/branches/py3k/Doc/library/termios.rst (original) +++ python/branches/py3k/Doc/library/termios.rst Wed Sep 15 13:11:28 2010 @@ -17,7 +17,7 @@ All functions in this module take a file descriptor *fd* as their first argument. This can be an integer file descriptor, such as returned by -``sys.stdin.fileno()``, or a file object, such as ``sys.stdin`` itself. +``sys.stdin.fileno()``, or a :term:`file object`, such as ``sys.stdin`` itself. This module also defines all the constants needed to work with the functions provided here; these have the same name as their counterparts in C. Please Modified: python/branches/py3k/Doc/library/weakref.rst ============================================================================== --- python/branches/py3k/Doc/library/weakref.rst (original) +++ python/branches/py3k/Doc/library/weakref.rst Wed Sep 15 13:11:28 2010 @@ -58,8 +58,9 @@ Not all objects can be weakly referenced; those objects which can include class instances, functions written in Python (but not in C), instance methods, sets, -frozensets, file objects, :term:`generator`\s, type objects, sockets, arrays, -deques, regular expression pattern objects, and code objects. +frozensets, some :term:`file objects `, :term:`generator`\s, type +objects, sockets, arrays, deques, regular expression pattern objects, and code +objects. .. versionchanged:: 3.2 Added support for thread.lock, threading.Lock, and code objects. Modified: python/branches/py3k/Doc/library/xml.etree.elementtree.rst ============================================================================== --- python/branches/py3k/Doc/library/xml.etree.elementtree.rst (original) +++ python/branches/py3k/Doc/library/xml.etree.elementtree.rst Wed Sep 15 13:11:28 2010 @@ -92,8 +92,8 @@ .. function:: iterparse(source, events=None, parser=None) Parses an XML section into an element tree incrementally, and reports what's - going on to the user. *source* is a filename or file object containing XML - data. *events* is a list of events to report back. If omitted, only "end" + going on to the user. *source* is a filename or :term:`file object` containing + XML data. *events* is a list of events to report back. If omitted, only "end" events are reported. *parser* is an optional parser instance. If not given, the standard :class:`XMLParser` parser is used. Returns an :term:`iterator` providing ``(event, elem)`` pairs. @@ -455,15 +455,15 @@ .. method:: parse(source, parser=None) Loads an external XML section into this element tree. *source* is a file - name or file object. *parser* is an optional parser instance. If not - given, the standard XMLParser parser is used. Returns the section + name or :term:`file object`. *parser* is an optional parser instance. + If not given, the standard XMLParser parser is used. Returns the section root element. .. method:: write(file, encoding="us-ascii", xml_declaration=None, method="xml") Writes the element tree to a file, as XML. *file* is a file name, or a - file object opened for writing. *encoding* [1]_ is the output encoding + :term:`file object` opened for writing. *encoding* [1]_ is the output encoding (default is US-ASCII). Use ``encoding="unicode"`` to write a Unicode string. *xml_declaration* controls if an XML declaration should be added to the file. Use False for never, True for always, None Modified: python/branches/py3k/Doc/tutorial/inputoutput.rst ============================================================================== --- python/branches/py3k/Doc/tutorial/inputoutput.rst (original) +++ python/branches/py3k/Doc/tutorial/inputoutput.rst Wed Sep 15 13:11:28 2010 @@ -232,8 +232,8 @@ builtin: open object: file -:func:`open` returns a file object, and is most commonly used with two -arguments: ``open(filename, mode)``. +:func:`open` returns a :term:`file object`, and is most commonly used with +two arguments: ``open(filename, mode)``. :: From python-checkins at python.org Wed Sep 15 13:12:57 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 13:12:57 +0200 (CEST) Subject: [Python-checkins] r84830 - python/branches/py3k/Lib/socket.py Message-ID: <20100915111257.E4DF6FBB1@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 13:12:57 2010 New Revision: 84830 Log: Reverted unwanted change in r84826 Modified: python/branches/py3k/Lib/socket.py Modified: python/branches/py3k/Lib/socket.py ============================================================================== --- python/branches/py3k/Lib/socket.py (original) +++ python/branches/py3k/Lib/socket.py Wed Sep 15 13:12:57 2010 @@ -54,8 +54,6 @@ errno = None EBADF = getattr(errno, 'EBADF', 9) EINTR = getattr(errno, 'EINTR', 4) -EAGAIN = getattr(errno, 'EAGAIN', 11) -EWOULDBLOCK = getattr(errno, 'EWOULDBLOCK', 11) __all__ = ["getfqdn", "create_connection"] __all__.extend(os._get_exports_list(_socket)) From python-checkins at python.org Wed Sep 15 13:14:58 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 13:14:58 +0200 (CEST) Subject: [Python-checkins] r84831 - python/branches/release27-maint Message-ID: <20100915111458.E10E5EE989@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 13:14:58 2010 New Revision: 84831 Log: Blocked revisions 84825-84826,84830 via svnmerge ........ r84825 | antoine.pitrou | 2010-09-15 10:39:25 +0200 (mer., 15 sept. 2010) | 3 lines Add a comment explaining why SocketIO is needed ........ r84826 | antoine.pitrou | 2010-09-15 11:32:45 +0200 (mer., 15 sept. 2010) | 3 lines Improve docs for socket.makefile() and SocketIO ........ r84830 | antoine.pitrou | 2010-09-15 13:12:57 +0200 (mer., 15 sept. 2010) | 3 lines Reverted unwanted change in r84826 ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Wed Sep 15 13:16:39 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 13:16:39 +0200 (CEST) Subject: [Python-checkins] r84832 - in python/branches/release31-maint: Doc/library/socket.rst Lib/socket.py Message-ID: <20100915111639.C50BDEE989@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 13:16:39 2010 New Revision: 84832 Log: Merged revisions 84825-84826,84830 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84825 | antoine.pitrou | 2010-09-15 10:39:25 +0200 (mer., 15 sept. 2010) | 3 lines Add a comment explaining why SocketIO is needed ........ r84826 | antoine.pitrou | 2010-09-15 11:32:45 +0200 (mer., 15 sept. 2010) | 3 lines Improve docs for socket.makefile() and SocketIO ........ r84830 | antoine.pitrou | 2010-09-15 13:12:57 +0200 (mer., 15 sept. 2010) | 3 lines Reverted unwanted change in r84826 ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/socket.rst python/branches/release31-maint/Lib/socket.py Modified: python/branches/release31-maint/Doc/library/socket.rst ============================================================================== --- python/branches/release31-maint/Doc/library/socket.rst (original) +++ python/branches/release31-maint/Doc/library/socket.rst Wed Sep 15 13:16:39 2010 @@ -597,10 +597,9 @@ arguments are interpreted the same way as by the built-in :func:`open` function. - The returned file object references a :cfunc:`dup`\ ped version of the - socket file descriptor, so the file object and socket object may be - closed or garbage-collected independently. The socket must be in - blocking mode (it can not have a timeout). + Closing the file object won't close the socket unless there are no + remaining references to the socket. The socket must be in blocking mode + (it can not have a timeout). .. method:: socket.recv(bufsize[, flags]) Modified: python/branches/release31-maint/Lib/socket.py ============================================================================== --- python/branches/release31-maint/Lib/socket.py (original) +++ python/branches/release31-maint/Lib/socket.py Wed Sep 15 13:16:39 2010 @@ -195,6 +195,13 @@ the raw I/O interface on top of a socket object. """ + # One might wonder why not let FileIO do the job instead. There are two + # main reasons why FileIO is not adapted: + # - it wouldn't work under Windows (where you can't used read() and + # write() on a socket handle) + # - it wouldn't work with socket timeouts (FileIO would ignore the + # timeout and consider the socket non-blocking) + # XXX More docs def __init__(self, sock, mode): @@ -209,22 +216,40 @@ self._writing = "w" in mode def readinto(self, b): + """Read up to len(b) bytes into the writable buffer *b* and return + the number of bytes read. If the socket is non-blocking and no bytes + are available, None is returned. + + If *b* is non-empty, a 0 return value indicates that the connection + was shutdown at the other end. + """ self._checkClosed() self._checkReadable() return self._sock.recv_into(b) def write(self, b): + """Write the given bytes or bytearray object *b* to the socket + and return the number of bytes written. This can be less than + len(b) if not all data could be written. If the socket is + non-blocking and no bytes could be written None is returned. + """ self._checkClosed() self._checkWritable() return self._sock.send(b) def readable(self): + """True if the SocketIO is open for reading. + """ return self._reading and not self.closed def writable(self): + """True if the SocketIO is open for writing. + """ return self._writing and not self.closed def fileno(self): + """Return the file descriptor of the underlying socket. + """ self._checkClosed() return self._sock.fileno() @@ -237,6 +262,9 @@ return self._mode def close(self): + """Close the SocketIO object. This doesn't close the underlying + socket, except if all references to it have disappeared. + """ if self.closed: return io.RawIOBase.close(self) From python-checkins at python.org Wed Sep 15 13:25:12 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 13:25:12 +0200 (CEST) Subject: [Python-checkins] r84833 - in python/branches/release31-maint: Doc/faq/design.rst Doc/faq/library.rst Doc/glossary.rst Doc/library/aifc.rst Doc/library/array.rst Doc/library/asyncore.rst Doc/library/base64.rst Doc/library/bz2.rst Doc/library/configparser.rst Doc/library/csv.rst Doc/library/email.generator.rst Doc/library/email.parser.rst Doc/library/exceptions.rst Doc/library/filesys.rst Doc/library/formatter.rst Doc/library/ftplib.rst Doc/library/gettext.rst Doc/library/gzip.rst Doc/library/http.client.rst Doc/library/imp.rst Doc/library/mmap.rst Doc/library/nntplib.rst Doc/library/os.rst Doc/library/pickle.rst Doc/library/quopri.rst Doc/library/select.rst Doc/library/socket.rst Doc/library/stdtypes.rst Doc/library/subprocess.rst Doc/library/sys.rst Doc/library/tarfile.rst Doc/library/tempfile.rst Doc/library/termios.rst Doc/library/weakref.rst Doc/library/xml.etree.elementtree.rst Doc/reference/datamodel.rst Doc/tutorial/inputoutput.rst Message-ID: <20100915112512.538A4EE98C@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 13:25:11 2010 New Revision: 84833 Log: Merged revisions 84827-84829 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84827 | antoine.pitrou | 2010-09-15 11:58:26 +0200 (mer., 15 sept. 2010) | 3 lines Add a glossary entry for file objects. ........ r84828 | antoine.pitrou | 2010-09-15 12:08:31 +0200 (mer., 15 sept. 2010) | 3 lines Update file-related information in the FAQ. ........ r84829 | antoine.pitrou | 2010-09-15 13:11:28 +0200 (mer., 15 sept. 2010) | 3 lines Add cross-references to the glossary entry for file objects. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/faq/design.rst python/branches/release31-maint/Doc/faq/library.rst python/branches/release31-maint/Doc/glossary.rst python/branches/release31-maint/Doc/library/aifc.rst python/branches/release31-maint/Doc/library/array.rst python/branches/release31-maint/Doc/library/asyncore.rst python/branches/release31-maint/Doc/library/base64.rst python/branches/release31-maint/Doc/library/bz2.rst python/branches/release31-maint/Doc/library/configparser.rst python/branches/release31-maint/Doc/library/csv.rst python/branches/release31-maint/Doc/library/email.generator.rst python/branches/release31-maint/Doc/library/email.parser.rst python/branches/release31-maint/Doc/library/exceptions.rst python/branches/release31-maint/Doc/library/filesys.rst python/branches/release31-maint/Doc/library/formatter.rst python/branches/release31-maint/Doc/library/ftplib.rst python/branches/release31-maint/Doc/library/gettext.rst python/branches/release31-maint/Doc/library/gzip.rst python/branches/release31-maint/Doc/library/http.client.rst python/branches/release31-maint/Doc/library/imp.rst python/branches/release31-maint/Doc/library/mmap.rst python/branches/release31-maint/Doc/library/nntplib.rst python/branches/release31-maint/Doc/library/os.rst python/branches/release31-maint/Doc/library/pickle.rst python/branches/release31-maint/Doc/library/quopri.rst python/branches/release31-maint/Doc/library/select.rst python/branches/release31-maint/Doc/library/socket.rst python/branches/release31-maint/Doc/library/stdtypes.rst python/branches/release31-maint/Doc/library/subprocess.rst python/branches/release31-maint/Doc/library/sys.rst python/branches/release31-maint/Doc/library/tarfile.rst python/branches/release31-maint/Doc/library/tempfile.rst python/branches/release31-maint/Doc/library/termios.rst python/branches/release31-maint/Doc/library/weakref.rst python/branches/release31-maint/Doc/library/xml.etree.elementtree.rst python/branches/release31-maint/Doc/reference/datamodel.rst python/branches/release31-maint/Doc/tutorial/inputoutput.rst Modified: python/branches/release31-maint/Doc/faq/design.rst ============================================================================== --- python/branches/release31-maint/Doc/faq/design.rst (original) +++ python/branches/release31-maint/Doc/faq/design.rst Wed Sep 15 13:25:11 2010 @@ -209,8 +209,8 @@ is hidden at the bottom of the loop. The best approach is to use iterators, making it possible to loop through -objects using the ``for`` statement. For example, in the current version of -Python file objects support the iterator protocol, so you can now write simply:: +objects using the ``for`` statement. For example, :term:`file objects +` support the iterator protocol, so you can write simply:: for line in f: ... # do something with line... Modified: python/branches/release31-maint/Doc/faq/library.rst ============================================================================== --- python/branches/release31-maint/Doc/faq/library.rst (original) +++ python/branches/release31-maint/Doc/faq/library.rst Wed Sep 15 13:25:11 2010 @@ -454,7 +454,7 @@ To rename a file, use ``os.rename(old_path, new_path)``. -To truncate a file, open it using ``f = open(filename, "r+")``, and use +To truncate a file, open it using ``f = open(filename, "rb+")``, and use ``f.truncate(offset)``; offset defaults to the current seek position. There's also ```os.ftruncate(fd, offset)`` for files opened with :func:`os.open`, where ``fd`` is the file descriptor (a small integer). @@ -483,9 +483,9 @@ import struct - f = open(filename, "rb") # Open in binary mode for portability - s = f.read(8) - x, y, z = struct.unpack(">hhl", s) + with open(filename, "rb") as f: + s = f.read(8) + x, y, z = struct.unpack(">hhl", s) The '>' in the format string forces big-endian data; the letter 'h' reads one "short integer" (2 bytes), and 'l' reads one "long integer" (4 bytes) from the @@ -494,6 +494,13 @@ For data that is more regular (e.g. a homogeneous list of ints or thefloats), you can also use the :mod:`array` module. + .. note:: + To read and write binary data, it is mandatory to open the file in + binary mode (here, passing ``"rb"`` to :func:`open`). If you use + ``"r"`` instead (the default), the file will be open in text mode + and ``f.read()`` will return :class:`str` objects rather than + :class:`bytes` objects. + I can't seem to use os.read() on a pipe created with os.popen(); why? --------------------------------------------------------------------- @@ -597,27 +604,29 @@ Why doesn't closing sys.stdout (stdin, stderr) really close it? --------------------------------------------------------------- -Python file objects are a high-level layer of abstraction on top of C streams, -which in turn are a medium-level layer of abstraction on top of (among other -things) low-level C file descriptors. - -For most file objects you create in Python via the builtin ``file`` constructor, -``f.close()`` marks the Python file object as being closed from Python's point -of view, and also arranges to close the underlying C stream. This also happens -automatically in f's destructor, when f becomes garbage. +Python :term:`file objects ` are a high-level layer of +abstraction on low-level C file descriptors. + +For most file objects you create in Python via the built-in :func:`open` +function, ``f.close()`` marks the Python file object as being closed from +Python's point of view, and also arranges to close the underlying C file +descriptor. This also happens automatically in ``f``'s destructor, when +``f`` becomes garbage. But stdin, stdout and stderr are treated specially by Python, because of the special status also given to them by C. Running ``sys.stdout.close()`` marks the Python-level file object as being closed, but does *not* close the -associated C stream. +associated C file descriptor. + +To close the underlying C file descriptor for one of these three, you should +first be sure that's what you really want to do (e.g., you may confuse +extension modules trying to do I/O). If it is, use :func:`os.close`:: + + os.close(stdin.fileno()) + os.close(stdout.fileno()) + os.close(stderr.fileno()) -To close the underlying C stream for one of these three, you should first be -sure that's what you really want to do (e.g., you may confuse extension modules -trying to do I/O). If it is, use os.close:: - - os.close(0) # close C's stdin stream - os.close(1) # close C's stdout stream - os.close(2) # close C's stderr stream +Or you can use the numeric constants 0, 1 and 2, respectively. Network/Internet Programming Modified: python/branches/release31-maint/Doc/glossary.rst ============================================================================== --- python/branches/release31-maint/Doc/glossary.rst (original) +++ python/branches/release31-maint/Doc/glossary.rst Wed Sep 15 13:25:11 2010 @@ -178,6 +178,23 @@ A module written in C or C++, using Python's C API to interact with the core and with user code. + file object + An object exposing a file-oriented API (with methods such as + :meth:`read()` or :meth:`write()`) to an underlying resource. + Depending on the way it was created, a file object can mediate access + to a real on-disk file or to another other type of storage or + communication device (for example standard input/output, in-memory + buffers, sockets, pipes, etc.). File objects are also called + :dfn:`file-like objects` or :dfn:`streams`. + + There are actually three categories of file objects: raw binary + files, buffered binary files and text files. Their interfaces are + defined in the :mod:`io` module. The canonical way to create a + file object is by using the :func:`open` function. + + file-like object + A synonym for :term:`file object`. + finder An object that tries to find the :term:`loader` for a module. It must implement a method named :meth:`find_module`. See :pep:`302` for Modified: python/branches/release31-maint/Doc/library/aifc.rst ============================================================================== --- python/branches/release31-maint/Doc/library/aifc.rst (original) +++ python/branches/release31-maint/Doc/library/aifc.rst Wed Sep 15 13:25:11 2010 @@ -40,10 +40,10 @@ .. function:: open(file, mode=None) Open an AIFF or AIFF-C file and return an object instance with methods that are - described below. The argument *file* is either a string naming a file or a file - object. *mode* must be ``'r'`` or ``'rb'`` when the file must be opened for - reading, or ``'w'`` or ``'wb'`` when the file must be opened for writing. If - omitted, ``file.mode`` is used if it exists, otherwise ``'rb'`` is used. When + described below. The argument *file* is either a string naming a file or a + :term:`file object`. *mode* must be ``'r'`` or ``'rb'`` when the file must be + opened for reading, or ``'w'`` or ``'wb'`` when the file must be opened for writing. + If omitted, ``file.mode`` is used if it exists, otherwise ``'rb'`` is used. When used for writing, the file object should be seekable, unless you know ahead of time how many samples you are going to write in total and use :meth:`writeframesraw` and :meth:`setnframes`. Modified: python/branches/release31-maint/Doc/library/array.rst ============================================================================== --- python/branches/release31-maint/Doc/library/array.rst (original) +++ python/branches/release31-maint/Doc/library/array.rst Wed Sep 15 13:25:11 2010 @@ -138,11 +138,11 @@ .. method:: array.fromfile(f, n) - Read *n* items (as machine values) from the file object *f* and append them to - the end of the array. If less than *n* items are available, :exc:`EOFError` is - raised, but the items that were available are still inserted into the array. - *f* must be a real built-in file object; something else with a :meth:`read` - method won't do. + Read *n* items (as machine values) from the :term:`file object` *f* and append + them to the end of the array. If less than *n* items are available, + :exc:`EOFError` is raised, but the items that were available are still + inserted into the array. *f* must be a real built-in file object; something + else with a :meth:`read` method won't do. .. method:: array.fromlist(list) @@ -196,7 +196,7 @@ .. method:: array.tofile(f) - Write all items (as machine values) to the file object *f*. + Write all items (as machine values) to the :term:`file object` *f*. .. method:: array.tolist() Modified: python/branches/release31-maint/Doc/library/asyncore.rst ============================================================================== --- python/branches/release31-maint/Doc/library/asyncore.rst (original) +++ python/branches/release31-maint/Doc/library/asyncore.rst Wed Sep 15 13:25:11 2010 @@ -224,9 +224,9 @@ .. class:: file_dispatcher() - A file_dispatcher takes a file descriptor or file object along with an - optional map argument and wraps it for use with the :cfunc:`poll` or - :cfunc:`loop` functions. If provided a file object or anything with a + A file_dispatcher takes a file descriptor or :term:`file object` along + with an optional map argument and wraps it for use with the :cfunc:`poll` + or :cfunc:`loop` functions. If provided a file object or anything with a :cfunc:`fileno` method, that method will be called and passed to the :class:`file_wrapper` constructor. Availability: UNIX. Modified: python/branches/release31-maint/Doc/library/base64.rst ============================================================================== --- python/branches/release31-maint/Doc/library/base64.rst (original) +++ python/branches/release31-maint/Doc/library/base64.rst Wed Sep 15 13:25:11 2010 @@ -122,9 +122,9 @@ .. function:: decode(input, output) Decode the contents of the binary *input* file and write the resulting binary - data to the *output* file. *input* and *output* must either be file objects - or objects that mimic the file object interface working with bytes - objects. *input* will be read until ``input.read()`` returns an empty string. + data to the *output* file. *input* and *output* must be :term:`file objects + `. *input* will be read until ``input.read()`` returns an empty + bytes object. .. function:: decodebytes(s) @@ -138,11 +138,10 @@ .. function:: encode(input, output) Encode the contents of the binary *input* file and write the resulting base64 - encoded data to the *output* file. *input* and *output* must either be file - objects or objects that mimic the file object interface working with bytes - objects. *input* will be read until ``input.read()`` returns an empty string. - :func:`encode` returns the encoded data plus a trailing newline character - (``b'\n'``). + encoded data to the *output* file. *input* and *output* must be :term:`file + objects `. *input* will be read until ``input.read()`` returns + an empty bytes object. :func:`encode` returns the encoded data plus a trailing + newline character (``b'\n'``). .. function:: encodebytes(s) Modified: python/branches/release31-maint/Doc/library/bz2.rst ============================================================================== --- python/branches/release31-maint/Doc/library/bz2.rst (original) +++ python/branches/release31-maint/Doc/library/bz2.rst Wed Sep 15 13:25:11 2010 @@ -25,8 +25,8 @@ * :class:`BZ2File` class implements universal newline support; -* :class:`BZ2File` class offers an optimized line iteration using the readahead - algorithm borrowed from file objects; +* :class:`BZ2File` class offers an optimized line iteration using a readahead + algorithm; * Sequential (de)compression supported by :class:`BZ2Compressor` and :class:`BZ2Decompressor` classes; Modified: python/branches/release31-maint/Doc/library/configparser.rst ============================================================================== --- python/branches/release31-maint/Doc/library/configparser.rst (original) +++ python/branches/release31-maint/Doc/library/configparser.rst Wed Sep 15 13:25:11 2010 @@ -298,7 +298,7 @@ .. method:: RawConfigParser.write(fileobject) - Write a representation of the configuration to the specified file object, + Write a representation of the configuration to the specified :term:`file object`, which must be opened in text mode (accepting strings). This representation can be parsed by a future :meth:`read` call. Modified: python/branches/release31-maint/Doc/library/csv.rst ============================================================================== --- python/branches/release31-maint/Doc/library/csv.rst (original) +++ python/branches/release31-maint/Doc/library/csv.rst Wed Sep 15 13:25:11 2010 @@ -50,9 +50,9 @@ Return a reader object which will iterate over lines in the given *csvfile*. *csvfile* can be any object which supports the :term:`iterator` protocol and returns a - string each time its :meth:`!next` method is called --- file objects and list - objects are both suitable. If *csvfile* is a file object, it should be opened - with ``newline=''``. [#]_ An optional + string each time its :meth:`!next` method is called --- :term:`file objects + ` and list objects are both suitable. If *csvfile* is a file object, + it should be opened with ``newline=''``. [#]_ An optional *dialect* parameter can be given which is used to define a set of parameters specific to a particular CSV dialect. It may be an instance of a subclass of the :class:`Dialect` class or one of the strings returned by the Modified: python/branches/release31-maint/Doc/library/email.generator.rst ============================================================================== --- python/branches/release31-maint/Doc/library/email.generator.rst (original) +++ python/branches/release31-maint/Doc/library/email.generator.rst Wed Sep 15 13:25:11 2010 @@ -28,9 +28,9 @@ .. class:: Generator(outfp, mangle_from_=True, maxheaderlen=78) - The constructor for the :class:`Generator` class takes a file-like object called - *outfp* for an argument. *outfp* must support the :meth:`write` method and be - usable as the output file for the :func:`print` function. + The constructor for the :class:`Generator` class takes a :term:`file-like object` + called *outfp* for an argument. *outfp* must support the :meth:`write` method + and be usable as the output file for the :func:`print` function. Optional *mangle_from_* is a flag that, when ``True``, puts a ``>`` character in front of any line in the body that starts exactly as ``From``, i.e. ``From`` Modified: python/branches/release31-maint/Doc/library/email.parser.rst ============================================================================== --- python/branches/release31-maint/Doc/library/email.parser.rst (original) +++ python/branches/release31-maint/Doc/library/email.parser.rst Wed Sep 15 13:25:11 2010 @@ -154,9 +154,9 @@ .. function:: message_from_file(fp[, _class][, strict]) - Return a message object structure tree from an open file object. This is - exactly equivalent to ``Parser().parse(fp)``. Optional *_class* and *strict* - are interpreted as with the :class:`Parser` class constructor. + Return a message object structure tree from an open :term:`file object`. + This is exactly equivalent to ``Parser().parse(fp)``. Optional *_class* + and *strict* are interpreted as with the :class:`Parser` class constructor. Here's an example of how you might use this at an interactive Python prompt:: Modified: python/branches/release31-maint/Doc/library/exceptions.rst ============================================================================== --- python/branches/release31-maint/Doc/library/exceptions.rst (original) +++ python/branches/release31-maint/Doc/library/exceptions.rst Wed Sep 15 13:25:11 2010 @@ -142,8 +142,8 @@ .. exception:: IOError Raised when an I/O operation (such as the built-in :func:`print` or - :func:`open` functions or a method of a file object) fails for an I/O-related - reason, e.g., "file not found" or "disk full". + :func:`open` functions or a method of a :term:`file object`) fails for an + I/O-related reason, e.g., "file not found" or "disk full". This class is derived from :exc:`EnvironmentError`. See the discussion above for more information on exception instance attributes. Modified: python/branches/release31-maint/Doc/library/filesys.rst ============================================================================== --- python/branches/release31-maint/Doc/library/filesys.rst (original) +++ python/branches/release31-maint/Doc/library/filesys.rst Wed Sep 15 13:25:11 2010 @@ -27,8 +27,8 @@ .. seealso:: Module :mod:`os` - Operating system interfaces, including functions to work with files at a lower - level than the built-in file object. + Operating system interfaces, including functions to work with files at a + lower level than Python :term:`file objects `. Module :mod:`io` Python's built-in I/O library, including both abstract classes and Modified: python/branches/release31-maint/Doc/library/formatter.rst ============================================================================== --- python/branches/release31-maint/Doc/library/formatter.rst (original) +++ python/branches/release31-maint/Doc/library/formatter.rst Wed Sep 15 13:25:11 2010 @@ -339,8 +339,8 @@ .. class:: DumbWriter(file=None, maxcol=72) - Simple writer class which writes output on the file object passed in as *file* - or, if *file* is omitted, on standard output. The output is simply word-wrapped - to the number of columns specified by *maxcol*. This class is suitable for - reflowing a sequence of paragraphs. + Simple writer class which writes output on the :term:`file object` passed + in as *file* or, if *file* is omitted, on standard output. The output is + simply word-wrapped to the number of columns specified by *maxcol*. This + class is suitable for reflowing a sequence of paragraphs. Modified: python/branches/release31-maint/Doc/library/ftplib.rst ============================================================================== --- python/branches/release31-maint/Doc/library/ftplib.rst (original) +++ python/branches/release31-maint/Doc/library/ftplib.rst Wed Sep 15 13:25:11 2010 @@ -194,19 +194,19 @@ .. method:: FTP.storbinary(cmd, file, blocksize=8192, callback=None) Store a file in binary transfer mode. *cmd* should be an appropriate - ``STOR`` command: ``"STOR filename"``. *file* is an open file object which is - read until EOF using its :meth:`read` method in blocks of size *blocksize* to - provide the data to be stored. The *blocksize* argument defaults to 8192. - *callback* is an optional single parameter callable that is called - on each block of data after it is sent. + ``STOR`` command: ``"STOR filename"``. *file* is an open :term:`file object` + which is read until EOF using its :meth:`read` method in blocks of size + *blocksize* to provide the data to be stored. The *blocksize* argument + defaults to 8192. *callback* is an optional single parameter callable that + is called on each block of data after it is sent. .. method:: FTP.storlines(cmd, file, callback=None) Store a file in ASCII transfer mode. *cmd* should be an appropriate ``STOR`` command (see :meth:`storbinary`). Lines are read until EOF from the - open file object *file* using its :meth:`readline` method to provide the data to - be stored. *callback* is an optional single parameter callable + open :term:`file object` *file* using its :meth:`readline` method to provide + the data to be stored. *callback* is an optional single parameter callable that is called on each line after it is sent. Modified: python/branches/release31-maint/Doc/library/gettext.rst ============================================================================== --- python/branches/release31-maint/Doc/library/gettext.rst (original) +++ python/branches/release31-maint/Doc/library/gettext.rst Wed Sep 15 13:25:11 2010 @@ -173,8 +173,8 @@ associated :file:`.mo` file paths. Instances with identical :file:`.mo` file names are cached. The actual class instantiated is either *class_* if provided, otherwise :class:`GNUTranslations`. The class's constructor must - take a single file object argument. If provided, *codeset* will change the - charset used to encode translated strings in the :meth:`lgettext` and + take a single :term:`file object` argument. If provided, *codeset* will change + the charset used to encode translated strings in the :meth:`lgettext` and :meth:`lngettext` methods. If multiple files are found, later files are used as fallbacks for earlier ones. @@ -219,7 +219,7 @@ .. class:: NullTranslations(fp=None) - Takes an optional file object *fp*, which is ignored by the base class. + Takes an optional :term:`file object` *fp*, which is ignored by the base class. Initializes "protected" instance variables *_info* and *_charset* which are set by derived classes, as well as *_fallback*, which is set through :meth:`add_fallback`. It then calls ``self._parse(fp)`` if *fp* is not Modified: python/branches/release31-maint/Doc/library/gzip.rst ============================================================================== --- python/branches/release31-maint/Doc/library/gzip.rst (original) +++ python/branches/release31-maint/Doc/library/gzip.rst Wed Sep 15 13:25:11 2010 @@ -9,10 +9,9 @@ The data compression is provided by the :mod:`zlib` module. -The :mod:`gzip` module provides the :class:`GzipFile` class which is modeled -after Python's File Object. The :class:`GzipFile` class reads and writes -:program:`gzip`\ -format files, automatically compressing or decompressing the -data so that it looks like an ordinary file object. +The :mod:`gzip` module provides the :class:`GzipFile` class. The :class:`GzipFile` +class reads and writes :program:`gzip`\ -format files, automatically compressing +or decompressing the data so that it looks like an ordinary :term:`file object`. Note that additional file formats which can be decompressed by the :program:`gzip` and :program:`gunzip` programs, such as those produced by @@ -27,7 +26,7 @@ .. class:: GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None) Constructor for the :class:`GzipFile` class, which simulates most of the methods - of a file object, with the exception of the :meth:`readinto` and + of a :term:`file object`, with the exception of the :meth:`readinto` and :meth:`truncate` methods. At least one of *fileobj* and *filename* must be given a non-trivial value. Modified: python/branches/release31-maint/Doc/library/http.client.rst ============================================================================== --- python/branches/release31-maint/Doc/library/http.client.rst (original) +++ python/branches/release31-maint/Doc/library/http.client.rst Wed Sep 15 13:25:11 2010 @@ -359,7 +359,7 @@ object. The Content-Length header is set to the length of the string. - The *body* may also be an open file object, in which case the + The *body* may also be an open :term:`file object`, in which case the contents of the file is sent; this file object should support ``fileno()`` and ``read()`` methods. The header Content-Length is automatically set to the length of the file as reported by Modified: python/branches/release31-maint/Doc/library/imp.rst ============================================================================== --- python/branches/release31-maint/Doc/library/imp.rst (original) +++ python/branches/release31-maint/Doc/library/imp.rst Wed Sep 15 13:25:11 2010 @@ -48,8 +48,8 @@ If search is successful, the return value is a 3-element tuple ``(file, pathname, description)``: - *file* is an open file object positioned at the beginning, *pathname* is the - pathname of the file found, and *description* is a 3-element tuple as + *file* is an open :term:`file object` positioned at the beginning, *pathname* + is the pathname of the file found, and *description* is a 3-element tuple as contained in the list returned by :func:`get_suffixes` describing the kind of module found. Modified: python/branches/release31-maint/Doc/library/mmap.rst ============================================================================== --- python/branches/release31-maint/Doc/library/mmap.rst (original) +++ python/branches/release31-maint/Doc/library/mmap.rst Wed Sep 15 13:25:11 2010 @@ -5,14 +5,13 @@ :synopsis: Interface to memory-mapped files for Unix and Windows. -Memory-mapped file objects behave like both :class:`bytes` and like file -objects. Unlike normal :class:`bytes` objects, however, these are mutable. -You can use mmap objects in most places where :class:`bytes` are expected; for -example, you can use the :mod:`re` module to search through a memory-mapped file. -Since they're mutable, you can change a single byte by doing ``obj[index] = 97``, -or change a subsequence by assigning to a slice: ``obj[i1:i2] = b'...'``. -You can also read and write data starting at the current file position, and -:meth:`seek` through the file to different positions. +Memory-mapped file objects behave like both :class:`bytearray` and like +:term:`file objects `. You can use mmap objects in most places +where :class:`bytearray` are expected; for example, you can use the :mod:`re` +module to search through a memory-mapped file. You can also change a single +byte by doing ``obj[index] = 97``, or change a subsequence by assigning to a +slice: ``obj[i1:i2] = b'...'``. You can also read and write data starting at +the current file position, and :meth:`seek` through the file to different positions. A memory-mapped file is created by the :class:`mmap` constructor, which is different on Unix and on Windows. In either case you must provide a file Modified: python/branches/release31-maint/Doc/library/nntplib.rst ============================================================================== --- python/branches/release31-maint/Doc/library/nntplib.rst (original) +++ python/branches/release31-maint/Doc/library/nntplib.rst Wed Sep 15 13:25:11 2010 @@ -143,9 +143,9 @@ *groups* is a list of group names that are new since the given date and time. If the *file* parameter is supplied, then the output of the ``NEWGROUPS`` command is stored in a file. If *file* is a string, then the method will open a file - object with that name, write to it then close it. If *file* is a file object, - then it will start calling :meth:`write` on it to store the lines of the command - output. If *file* is supplied, then the returned *list* is an empty list. + object with that name, write to it then close it. If *file* is a :term:`file + object`, then it will start calling :meth:`write` on it to store the lines of + the command output. If *file* is supplied, then the returned *list* is an empty list. .. method:: NNTP.newnews(group, date, time, [file]) @@ -155,9 +155,9 @@ ``(response, articles)`` where *articles* is a list of message ids. If the *file* parameter is supplied, then the output of the ``NEWNEWS`` command is stored in a file. If *file* is a string, then the method will open a file - object with that name, write to it then close it. If *file* is a file object, - then it will start calling :meth:`write` on it to store the lines of the command - output. If *file* is supplied, then the returned *list* is an empty list. + object with that name, write to it then close it. If *file* is a :term:`file + object`, then it will start calling :meth:`write` on it to store the lines of the + command output. If *file* is supplied, then the returned *list* is an empty list. .. method:: NNTP.list([file]) @@ -169,9 +169,9 @@ not, and ``'m'`` if the newsgroup is moderated. (Note the ordering: *last*, *first*.) If the *file* parameter is supplied, then the output of the ``LIST`` command is stored in a file. If *file* is a string, then the method will open - a file object with that name, write to it then close it. If *file* is a file - object, then it will start calling :meth:`write` on it to store the lines of the - command output. If *file* is supplied, then the returned *list* is an empty + a file with that name, write to it then close it. If *file* is a :term:`file + object`, then it will start calling :meth:`write` on it to store the lines of + the command output. If *file* is supplied, then the returned *list* is an empty list. @@ -207,8 +207,8 @@ Send a ``HELP`` command. Return a pair ``(response, list)`` where *list* is a list of help strings. If the *file* parameter is supplied, then the output of the ``HELP`` command is stored in a file. If *file* is a string, then the - method will open a file object with that name, write to it then close it. If - *file* is a file object, then it will start calling :meth:`write` on it to store + method will open a file with that name, write to it then close it. If *file* + is a :term:`file object`, then it will start calling :meth:`write` on it to store the lines of the command output. If *file* is supplied, then the returned *list* is an empty list. @@ -243,8 +243,8 @@ Send a ``BODY`` command, where *id* has the same meaning as for :meth:`stat`. If the *file* parameter is supplied, then the body is stored in a file. If - *file* is a string, then the method will open a file object with that name, - write to it then close it. If *file* is a file object, then it will start + *file* is a string, then the method will open a file with that name, write + to it then close it. If *file* is a :term:`file object`, then it will start calling :meth:`write` on it to store the lines of the body. Return as for :meth:`head`. If *file* is supplied, then the returned *list* is an empty list. @@ -270,9 +270,9 @@ text)``, where *id* is an article number (as a string) and *text* is the text of the requested header for that article. If the *file* parameter is supplied, then the output of the ``XHDR`` command is stored in a file. If *file* is a string, - then the method will open a file object with that name, write to it then close - it. If *file* is a file object, then it will start calling :meth:`write` on it - to store the lines of the command output. If *file* is supplied, then the + then the method will open a file with that name, write to it then close it. + If *file* is a :term:`file object`, then it will start calling :meth:`write` on + it to store the lines of the command output. If *file* is supplied, then the returned *list* is an empty list. @@ -303,8 +303,8 @@ Process an ``XGTITLE`` command, returning a pair ``(response, list)``, where *list* is a list of tuples containing ``(name, title)``. If the *file* parameter is supplied, then the output of the ``XGTITLE`` command is stored in a file. - If *file* is a string, then the method will open a file object with that name, - write to it then close it. If *file* is a file object, then it will start + If *file* is a string, then the method will open a file with that name, write + to it then close it. If *file* is a :term:`file object`, then it will start calling :meth:`write` on it to store the lines of the command output. If *file* is supplied, then the returned *list* is an empty list. This is an optional NNTP extension, and may not be supported by all servers. @@ -320,8 +320,8 @@ tuple is of the form ``(article number, subject, poster, date, id, references, size, lines)``. If the *file* parameter is supplied, then the output of the ``XOVER`` command is stored in a file. If *file* is a string, then the method - will open a file object with that name, write to it then close it. If *file* - is a file object, then it will start calling :meth:`write` on it to store the + will open a file with that name, write to it then close it. If *file* is a + :term:`file object`, then it will start calling :meth:`write` on it to store the lines of the command output. If *file* is supplied, then the returned *list* is an empty list. This is an optional NNTP extension, and may not be supported by all servers. Modified: python/branches/release31-maint/Doc/library/os.rst ============================================================================== --- python/branches/release31-maint/Doc/library/os.rst (original) +++ python/branches/release31-maint/Doc/library/os.rst Wed Sep 15 13:25:11 2010 @@ -401,7 +401,7 @@ File Object Creation -------------------- -These functions create new file objects. (See also :func:`open`.) +These functions create new :term:`file objects `. (See also :func:`open`.) .. function:: fdopen(fd[, mode[, bufsize]]) @@ -436,6 +436,10 @@ is slightly deceptive; on Unix platforms, sockets and pipes are also referenced by file descriptors. +The :meth:`~file.fileno` method can be used to obtain the file descriptor +associated with a :term:`file object` when required. Note that using the file +descriptor directly will bypass the file object methods, ignoring aspects such +as internal buffering of data. .. function:: close(fd) @@ -550,9 +554,9 @@ Force write of file with filedescriptor *fd* to disk. On Unix, this calls the native :cfunc:`fsync` function; on Windows, the MS :cfunc:`_commit` function. - If you're starting with a Python file object *f*, first do ``f.flush()``, and - then do ``os.fsync(f.fileno())``, to ensure that all internal buffers associated - with *f* are written to disk. + If you're starting with a buffered Python :term:`file object` *f*, first do + ``f.flush()``, and then do ``os.fsync(f.fileno())``, to ensure that all internal + buffers associated with *f* are written to disk. Availability: Unix, and Windows. @@ -609,9 +613,9 @@ .. note:: This function is intended for low-level I/O. For normal usage, use the - built-in function :func:`open`, which returns a "file object" with + built-in function :func:`open`, which returns a :term:`file object` with :meth:`~file.read` and :meth:`~file.wprite` methods (and many more). To - wrap a file descriptor in a "file object", use :func:`fdopen`. + wrap a file descriptor in a file object, use :func:`fdopen`. .. function:: openpty() Modified: python/branches/release31-maint/Doc/library/pickle.rst ============================================================================== --- python/branches/release31-maint/Doc/library/pickle.rst (original) +++ python/branches/release31-maint/Doc/library/pickle.rst Wed Sep 15 13:25:11 2010 @@ -143,8 +143,8 @@ .. function:: dump(obj, file[, protocol, \*, fix_imports=True]) - Write a pickled representation of *obj* to the open file object *file*. This - is equivalent to ``Pickler(file, protocol).dump(obj)``. + Write a pickled representation of *obj* to the open :term:`file object` *file*. + This is equivalent to ``Pickler(file, protocol).dump(obj)``. The optional *protocol* argument tells the pickler to use the given protocol; supported protocols are 0, 1, 2, 3. The default protocol is 3; a @@ -155,8 +155,9 @@ Python needed to read the pickle produced. The *file* argument must have a write() method that accepts a single bytes - argument. It can thus be a file object opened for binary writing, a - io.BytesIO instance, or any other custom object that meets this interface. + argument. It can thus be an on-disk file opened for binary writing, a + :class:`io.BytesIO` instance, or any other custom object that meets this + interface. If *fix_imports* is True and *protocol* is less than 3, pickle will try to map the new Python 3.x names to the old module names used in Python 2.x, @@ -181,8 +182,8 @@ .. function:: load(file, [\*, fix_imports=True, encoding="ASCII", errors="strict"]) - Read a pickled object representation from the open file object *file* and - return the reconstituted object hierarchy specified therein. This is + Read a pickled object representation from the open :term:`file object` *file* + and return the reconstituted object hierarchy specified therein. This is equivalent to ``Unpickler(file).load()``. The protocol version of the pickle is detected automatically, so no protocol @@ -191,9 +192,9 @@ The argument *file* must have two methods, a read() method that takes an integer argument, and a readline() method that requires no arguments. Both - methods should return bytes. Thus *file* can be a binary file object opened - for reading, a BytesIO object, or any other custom object that meets this - interface. + methods should return bytes. Thus *file* can be an on-disk file opened + for binary reading, a :class:`io.BytesIO` object, or any other custom object + that meets this interface. Optional keyword arguments are *fix_imports*, *encoding* and *errors*, which are used to control compatiblity support for pickle stream generated @@ -260,8 +261,8 @@ Python needed to read the pickle produced. The *file* argument must have a write() method that accepts a single bytes - argument. It can thus be a file object opened for binary writing, a - io.BytesIO instance, or any other custom object that meets this interface. + argument. It can thus be an on-disk file opened for binary writing, a + :class:`io.BytesIO` instance, or any other custom object that meets this interface. If *fix_imports* is True and *protocol* is less than 3, pickle will try to map the new Python 3.x names to the old module names used in Python 2.x, @@ -304,9 +305,9 @@ The argument *file* must have two methods, a read() method that takes an integer argument, and a readline() method that requires no arguments. Both - methods should return bytes. Thus *file* can be a binary file object opened - for reading, a BytesIO object, or any other custom object that meets this - interface. + methods should return bytes. Thus *file* can be an on-disk file object opened + for binary reading, a :class:`io.BytesIO` object, or any other custom object + that meets this interface. Optional keyword arguments are *fix_imports*, *encoding* and *errors*, which are used to control compatiblity support for pickle stream generated Modified: python/branches/release31-maint/Doc/library/quopri.rst ============================================================================== --- python/branches/release31-maint/Doc/library/quopri.rst (original) +++ python/branches/release31-maint/Doc/library/quopri.rst Wed Sep 15 13:25:11 2010 @@ -22,23 +22,23 @@ .. function:: decode(input, output[,header]) Decode the contents of the *input* file and write the resulting decoded binary - data to the *output* file. *input* and *output* must either be file objects or - objects that mimic the file object interface. *input* will be read until - ``input.readline()`` returns an empty string. If the optional argument *header* - is present and true, underscore will be decoded as space. This is used to decode - "Q"-encoded headers as described in :rfc:`1522`: "MIME (Multipurpose Internet - Mail Extensions) Part Two: Message Header Extensions for Non-ASCII Text". + data to the *output* file. *input* and *output* must be :term:`file objects + `. *input* will be read until ``input.readline()`` returns an + empty string. If the optional argument *header* is present and true, underscore + will be decoded as space. This is used to decode "Q"-encoded headers as + described in :rfc:`1522`: "MIME (Multipurpose Internet Mail Extensions) + Part Two: Message Header Extensions for Non-ASCII Text". .. function:: encode(input, output, quotetabs) Encode the contents of the *input* file and write the resulting quoted-printable - data to the *output* file. *input* and *output* must either be file objects or - objects that mimic the file object interface. *input* will be read until - ``input.readline()`` returns an empty string. *quotetabs* is a flag which - controls whether to encode embedded spaces and tabs; when true it encodes such - embedded whitespace, and when false it leaves them unencoded. Note that spaces - and tabs appearing at the end of lines are always encoded, as per :rfc:`1521`. + data to the *output* file. *input* and *output* must be :term:`file objects + `. *input* will be read until ``input.readline()`` returns an + empty string. *quotetabs* is a flag which controls whether to encode embedded + spaces and tabs; when true it encodes such embedded whitespace, and when + false it leaves them unencoded. Note that spaces and tabs appearing at the + end of lines are always encoded, as per :rfc:`1521`. .. function:: decodestring(s[,header]) Modified: python/branches/release31-maint/Doc/library/select.rst ============================================================================== --- python/branches/release31-maint/Doc/library/select.rst (original) +++ python/branches/release31-maint/Doc/library/select.rst Wed Sep 15 13:25:11 2010 @@ -79,11 +79,12 @@ single: socket() (in module socket) single: popen() (in module os) - Among the acceptable object types in the sequences are Python file objects (e.g. - ``sys.stdin``, or objects returned by :func:`open` or :func:`os.popen`), socket - objects returned by :func:`socket.socket`. You may also define a :dfn:`wrapper` - class yourself, as long as it has an appropriate :meth:`fileno` method (that - really returns a file descriptor, not just a random integer). + Among the acceptable object types in the sequences are Python :term:`file + objects ` (e.g. ``sys.stdin``, or objects returned by + :func:`open` or :func:`os.popen`), socket objects returned by + :func:`socket.socket`. You may also define a :dfn:`wrapper` class yourself, + as long as it has an appropriate :meth:`fileno` method (that really returns + a file descriptor, not just a random integer). .. note:: Modified: python/branches/release31-maint/Doc/library/socket.rst ============================================================================== --- python/branches/release31-maint/Doc/library/socket.rst (original) +++ python/branches/release31-maint/Doc/library/socket.rst Wed Sep 15 13:25:11 2010 @@ -592,7 +592,7 @@ .. index:: single: I/O control; buffering - Return a :dfn:`file object` associated with the socket. The exact + Return a :term:`file object` associated with the socket. The exact returned type depends on the arguments given to :meth:`makefile`. These arguments are interpreted the same way as by the built-in :func:`open` function. Modified: python/branches/release31-maint/Doc/library/stdtypes.rst ============================================================================== --- python/branches/release31-maint/Doc/library/stdtypes.rst (original) +++ python/branches/release31-maint/Doc/library/stdtypes.rst Wed Sep 15 13:25:11 2010 @@ -2210,9 +2210,9 @@ the identifier in the :keyword:`as` clause of :keyword:`with` statements using this context manager. - An example of a context manager that returns itself is a file object. File - objects return themselves from __enter__() to allow :func:`open` to be used as - the context expression in a :keyword:`with` statement. + An example of a context manager that returns itself is a :term:`file object`. + File objects return themselves from __enter__() to allow :func:`open` to be + used as the context expression in a :keyword:`with` statement. An example of a context manager that returns a related object is the one returned by :func:`decimal.localcontext`. These managers set the active Modified: python/branches/release31-maint/Doc/library/subprocess.rst ============================================================================== --- python/branches/release31-maint/Doc/library/subprocess.rst (original) +++ python/branches/release31-maint/Doc/library/subprocess.rst Wed Sep 15 13:25:11 2010 @@ -107,9 +107,9 @@ *stdin*, *stdout* and *stderr* specify the executed programs' standard input, standard output and standard error file handles, respectively. Valid values are :data:`PIPE`, an existing file descriptor (a positive integer), an - existing file object, and ``None``. :data:`PIPE` indicates that a new pipe - to the child should be created. With ``None``, no redirection will occur; - the child's file handles will be inherited from the parent. Additionally, + existing :term:`file object`, and ``None``. :data:`PIPE` indicates that a + new pipe to the child should be created. With ``None``, no redirection will + occur; the child's file handles will be inherited from the parent. Additionally, *stderr* can be :data:`STDOUT`, which indicates that the stderr data from the applications should be captured into the same file handle as for stdout. @@ -377,20 +377,20 @@ .. attribute:: Popen.stdin - If the *stdin* argument was :data:`PIPE`, this attribute is a file object - that provides input to the child process. Otherwise, it is ``None``. + If the *stdin* argument was :data:`PIPE`, this attribute is a :term:`file + object` that provides input to the child process. Otherwise, it is ``None``. .. attribute:: Popen.stdout - If the *stdout* argument was :data:`PIPE`, this attribute is a file object - that provides output from the child process. Otherwise, it is ``None``. + If the *stdout* argument was :data:`PIPE`, this attribute is a :term:`file + object` that provides output from the child process. Otherwise, it is ``None``. .. attribute:: Popen.stderr - If the *stderr* argument was :data:`PIPE`, this attribute is a file object - that provides error output from the child process. Otherwise, it is + If the *stderr* argument was :data:`PIPE`, this attribute is a :term:`file + object` that provides error output from the child process. Otherwise, it is ``None``. Modified: python/branches/release31-maint/Doc/library/sys.rst ============================================================================== --- python/branches/release31-maint/Doc/library/sys.rst (original) +++ python/branches/release31-maint/Doc/library/sys.rst Wed Sep 15 13:25:11 2010 @@ -792,10 +792,10 @@ stdout stderr - File objects corresponding to the interpreter's standard input, output and error - streams. ``stdin`` is used for all interpreter input except for scripts but - including calls to :func:`input`. ``stdout`` is used for - the output of :func:`print` and :term:`expression` statements and for the + :term:`File objects ` corresponding to the interpreter's standard + input, output and error streams. ``stdin`` is used for all interpreter input + except for scripts but including calls to :func:`input`. ``stdout`` is used + for the output of :func:`print` and :term:`expression` statements and for the prompts of :func:`input`. The interpreter's own prompts and (almost all of) its error messages go to ``stderr``. ``stdout`` and ``stderr`` needn't be built-in file objects: any object is acceptable as long Modified: python/branches/release31-maint/Doc/library/tarfile.rst ============================================================================== --- python/branches/release31-maint/Doc/library/tarfile.rst (original) +++ python/branches/release31-maint/Doc/library/tarfile.rst Wed Sep 15 13:25:11 2010 @@ -66,8 +66,8 @@ *mode* ``'r'`` to avoid this. If a compression method is not supported, :exc:`CompressionError` is raised. - If *fileobj* is specified, it is used as an alternative to a file object opened - for *name*. It is supposed to be at position 0. + If *fileobj* is specified, it is used as an alternative to a :term:`file object` + opened in binary mode for *name*. It is supposed to be at position 0. For special purposes, there is a second format for *mode*: ``'filemode|[compression]'``. :func:`tarfile.open` will return a :class:`TarFile` @@ -75,7 +75,7 @@ be done on the file. If given, *fileobj* may be any object that has a :meth:`read` or :meth:`write` method (depending on the *mode*). *bufsize* specifies the blocksize and defaults to ``20 * 512`` bytes. Use this variant - in combination with e.g. ``sys.stdin``, a socket file object or a tape + in combination with e.g. ``sys.stdin``, a socket :term:`file object` or a tape device. However, such a :class:`TarFile` object is limited in that it does not allow to be accessed randomly, see :ref:`tar-examples`. The currently possible modes: @@ -344,9 +344,9 @@ .. method:: TarFile.extractfile(member) Extract a member from the archive as a file object. *member* may be a filename - or a :class:`TarInfo` object. If *member* is a regular file, a file-like object - is returned. If *member* is a link, a file-like object is constructed from the - link's target. If *member* is none of the above, :const:`None` is returned. + or a :class:`TarInfo` object. If *member* is a regular file, a :term:`file-like + object` is returned. If *member* is a link, a file-like object is constructed from + the link's target. If *member* is none of the above, :const:`None` is returned. .. note:: @@ -380,9 +380,9 @@ .. method:: TarFile.gettarinfo(name=None, arcname=None, fileobj=None) - Create a :class:`TarInfo` object for either the file *name* or the file object - *fileobj* (using :func:`os.fstat` on its file descriptor). You can modify some - of the :class:`TarInfo`'s attributes before you add it using :meth:`addfile`. + Create a :class:`TarInfo` object for either the file *name* or the :term:`file + object` *fileobj* (using :func:`os.fstat` on its file descriptor). You can modify + some of the :class:`TarInfo`'s attributes before you add it using :meth:`addfile`. If given, *arcname* specifies an alternative name for the file in the archive. Modified: python/branches/release31-maint/Doc/library/tempfile.rst ============================================================================== --- python/branches/release31-maint/Doc/library/tempfile.rst (original) +++ python/branches/release31-maint/Doc/library/tempfile.rst Wed Sep 15 13:25:11 2010 @@ -29,7 +29,7 @@ .. function:: TemporaryFile(mode='w+b', buffering=None, encoding=None, newline=None, suffix='', prefix='tmp', dir=None) - Return a file-like object that can be used as a temporary storage area. + Return a :term:`file-like object` that can be used as a temporary storage area. The file is created using :func:`mkstemp`. It will be destroyed as soon as it is closed (including an implicit close when the object is garbage collected). Under Unix, the directory entry for the file is removed Modified: python/branches/release31-maint/Doc/library/termios.rst ============================================================================== --- python/branches/release31-maint/Doc/library/termios.rst (original) +++ python/branches/release31-maint/Doc/library/termios.rst Wed Sep 15 13:25:11 2010 @@ -17,7 +17,7 @@ All functions in this module take a file descriptor *fd* as their first argument. This can be an integer file descriptor, such as returned by -``sys.stdin.fileno()``, or a file object, such as ``sys.stdin`` itself. +``sys.stdin.fileno()``, or a :term:`file object`, such as ``sys.stdin`` itself. This module also defines all the constants needed to work with the functions provided here; these have the same name as their counterparts in C. Please Modified: python/branches/release31-maint/Doc/library/weakref.rst ============================================================================== --- python/branches/release31-maint/Doc/library/weakref.rst (original) +++ python/branches/release31-maint/Doc/library/weakref.rst Wed Sep 15 13:25:11 2010 @@ -58,8 +58,9 @@ Not all objects can be weakly referenced; those objects which can include class instances, functions written in Python (but not in C), instance methods, sets, -frozensets, file objects, :term:`generator`\s, type objects, sockets, arrays, -deques, and regular expression pattern objects. +frozensets, some :term:`file objects `, :term:`generator`\s, type +objects, sockets, arrays, deques and regular expression pattern objects. + Several built-in types such as :class:`list` and :class:`dict` do not directly support weak references but can add support through subclassing:: Modified: python/branches/release31-maint/Doc/library/xml.etree.elementtree.rst ============================================================================== --- python/branches/release31-maint/Doc/library/xml.etree.elementtree.rst (original) +++ python/branches/release31-maint/Doc/library/xml.etree.elementtree.rst Wed Sep 15 13:25:11 2010 @@ -89,9 +89,10 @@ .. function:: iterparse(source, events=None) Parses an XML section into an element tree incrementally, and reports what's - going on to the user. *source* is a filename or file object containing XML data. - *events* is a list of events to report back. If omitted, only "end" events are - reported. Returns an :term:`iterator` providing ``(event, elem)`` pairs. + going on to the user. *source* is a filename or :term:`file object` containing + XML data. *events* is a list of events to report back. If omitted, only "end" + events are reported. Returns an :term:`iterator` providing ``(event, elem)`` + pairs. .. note:: @@ -359,16 +360,16 @@ .. method:: parse(source, parser=None) - Loads an external XML section into this element tree. *source* is a file - name or file object. *parser* is an optional parser instance. If not - given, the standard XMLTreeBuilder parser is used. Returns the section + Loads an external XML section into this element tree. *source* is a file + name or :term:`file object`. *parser* is an optional parser instance. + If not given, the standard XMLTreeBuilder parser is used. Returns the section root element. .. method:: write(file, encoding=None) - Writes the element tree to a file, as XML. *file* is a file name, or a - file object opened for writing. *encoding* [1]_ is the output encoding + Writes the element tree to a file, as XML. *file* is a file name, or a + :term:`file object` opened for writing. *encoding* [1]_ is the output encoding (default is US-ASCII). This is the XML file that is going to be manipulated:: Modified: python/branches/release31-maint/Doc/reference/datamodel.rst ============================================================================== --- python/branches/release31-maint/Doc/reference/datamodel.rst (original) +++ python/branches/release31-maint/Doc/reference/datamodel.rst Wed Sep 15 13:25:11 2010 @@ -781,9 +781,9 @@ single: stdout (in module sys) single: stderr (in module sys) - A file object represents an open file. Various shortcuts are available - to create file objects: the :func:`open` built-in function, and also - :func:`os.popen`, :func:`os.fdopen`, and the :meth:`makefile` method + A :term:`file object` represents an open file. Various shortcuts are + available to create file objects: the :func:`open` built-in function, and + also :func:`os.popen`, :func:`os.fdopen`, and the :meth:`makefile` method of socket objects (and perhaps by other functions or methods provided by extension modules). Modified: python/branches/release31-maint/Doc/tutorial/inputoutput.rst ============================================================================== --- python/branches/release31-maint/Doc/tutorial/inputoutput.rst (original) +++ python/branches/release31-maint/Doc/tutorial/inputoutput.rst Wed Sep 15 13:25:11 2010 @@ -232,8 +232,8 @@ builtin: open object: file -:func:`open` returns a file object, and is most commonly used with two -arguments: ``open(filename, mode)``. +:func:`open` returns a :term:`file object`, and is most commonly used with +two arguments: ``open(filename, mode)``. :: From python-checkins at python.org Wed Sep 15 13:25:22 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 13:25:22 +0200 (CEST) Subject: [Python-checkins] r84834 - python/branches/release27-maint Message-ID: <20100915112522.28D2FEE9D0@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 13:25:22 2010 New Revision: 84834 Log: Blocked revisions 84827-84829 via svnmerge ........ r84827 | antoine.pitrou | 2010-09-15 11:58:26 +0200 (mer., 15 sept. 2010) | 3 lines Add a glossary entry for file objects. ........ r84828 | antoine.pitrou | 2010-09-15 12:08:31 +0200 (mer., 15 sept. 2010) | 3 lines Update file-related information in the FAQ. ........ r84829 | antoine.pitrou | 2010-09-15 13:11:28 +0200 (mer., 15 sept. 2010) | 3 lines Add cross-references to the glossary entry for file objects. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Wed Sep 15 15:01:19 2010 From: python-checkins at python.org (matthias.klose) Date: Wed, 15 Sep 2010 15:01:19 +0200 (CEST) Subject: [Python-checkins] r84835 - python/branches/release27-maint/Lib/ctypes/util.py Message-ID: <20100915130119.51AAFEE9C7@mail.python.org> Author: matthias.klose Date: Wed Sep 15 15:01:19 2010 New Revision: 84835 Log: Try harder on issue #7356: ctypes.util: Make parsing of ldconfig output independent of the locale. Set LC_ALL=C too. Modified: python/branches/release27-maint/Lib/ctypes/util.py Modified: python/branches/release27-maint/Lib/ctypes/util.py ============================================================================== --- python/branches/release27-maint/Lib/ctypes/util.py (original) +++ python/branches/release27-maint/Lib/ctypes/util.py Wed Sep 15 15:01:19 2010 @@ -185,7 +185,7 @@ def _findLib_ldconfig(name): # XXX assuming GLIBC's ldconfig (with option -p) expr = r'/[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name) - f = os.popen('LANG=C /sbin/ldconfig -p 2>/dev/null') + f = os.popen('LC_ALL=C LANG=C /sbin/ldconfig -p 2>/dev/null') try: data = f.read() finally: From python-checkins at python.org Wed Sep 15 15:06:09 2010 From: python-checkins at python.org (matthias.klose) Date: Wed, 15 Sep 2010 15:06:09 +0200 (CEST) Subject: [Python-checkins] r84836 - python/branches/py3k/Lib/ctypes/util.py Message-ID: <20100915130609.406AAEE9F1@mail.python.org> Author: matthias.klose Date: Wed Sep 15 15:06:09 2010 New Revision: 84836 Log: Try harder on issue #7356: ctypes.util: Make parsing of ldconfig output independent of the locale. Set LC_ALL=C too. Modified: python/branches/py3k/Lib/ctypes/util.py Modified: python/branches/py3k/Lib/ctypes/util.py ============================================================================== --- python/branches/py3k/Lib/ctypes/util.py (original) +++ python/branches/py3k/Lib/ctypes/util.py Wed Sep 15 15:06:09 2010 @@ -205,7 +205,7 @@ # XXX assuming GLIBC's ldconfig (with option -p) expr = r'(\S+)\s+\((%s(?:, OS ABI:[^\)]*)?)\)[^/]*(/[^\(\)\s]*lib%s\.[^\(\)\s]*)' \ % (abi_type, re.escape(name)) - with contextlib.closing(os.popen('LANG=C /sbin/ldconfig -p 2>/dev/null')) as f: + with contextlib.closing(os.popen('LC_ALL=C LANG=C /sbin/ldconfig -p 2>/dev/null')) as f: data = f.read() res = re.search(expr, data) if not res: From python-checkins at python.org Wed Sep 15 15:08:12 2010 From: python-checkins at python.org (matthias.klose) Date: Wed, 15 Sep 2010 15:08:12 +0200 (CEST) Subject: [Python-checkins] r84837 - python/branches/release31-maint/Lib/ctypes/util.py Message-ID: <20100915130812.E60B0EE9C7@mail.python.org> Author: matthias.klose Date: Wed Sep 15 15:08:12 2010 New Revision: 84837 Log: Merged revisions 84836 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84836 | matthias.klose | 2010-09-15 15:06:09 +0200 (Mi, 15 Sep 2010) | 3 lines Try harder on issue #7356: ctypes.util: Make parsing of ldconfig output independent of the locale. Set LC_ALL=C too. ........ Modified: python/branches/release31-maint/Lib/ctypes/util.py Modified: python/branches/release31-maint/Lib/ctypes/util.py ============================================================================== --- python/branches/release31-maint/Lib/ctypes/util.py (original) +++ python/branches/release31-maint/Lib/ctypes/util.py Wed Sep 15 15:08:12 2010 @@ -219,7 +219,7 @@ # XXX assuming GLIBC's ldconfig (with option -p) expr = r'(\S+)\s+\((%s(?:, OS ABI:[^\)]*)?)\)[^/]*(/[^\(\)\s]*lib%s\.[^\(\)\s]*)' \ % (abi_type, re.escape(name)) - f = os.popen('LANG=C /sbin/ldconfig -p 2>/dev/null') + f = os.popen('LC_ALL=C LANG=C /sbin/ldconfig -p 2>/dev/null') try: data = f.read() finally: From ncoghlan at gmail.com Wed Sep 15 15:28:20 2010 From: ncoghlan at gmail.com (Nick Coghlan) Date: Wed, 15 Sep 2010 23:28:20 +1000 Subject: [Python-checkins] r84823 - python/branches/py3k/Doc/reference/expressions.rst In-Reply-To: <20100915000926.8AFF3EE9B1@mail.python.org> References: <20100915000926.8AFF3EE9B1@mail.python.org> Message-ID: On Wed, Sep 15, 2010 at 10:09 AM, raymond.hettinger wrote: > +The formal syntax makes no special provision for negative indices in > +sequences; however, built-in sequences all provide a :meth:`__getitem__` > +method that interprets negative indices by adding the length of the sequence > +to the index (so that ``x[-1]`` selects the last item of ``x``). ?The > +resulting value must be a nonnegative integer less than the number of items in > +the sequence, and the subscription selects the item whose index is that value > +(counting from zero). Since the support for negative indices and slicing > +occurs in the object's :meth:`__getitem__` method, subclasses overriding > +this method will need to explicitly add that support. Perhaps mention the slice.indices(len) helper for performing the standard conversion from negative indices to positive ones when dealing with negative indices *in* slices? Cheers, Nick. -- Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia From python-checkins at python.org Wed Sep 15 17:09:40 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 17:09:40 +0200 (CEST) Subject: [Python-checkins] r84838 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100915150940.98B77EE9EA@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 17:09:40 2010 New Revision: 84838 Log: Add entries to whatsnew Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Wed Sep 15 17:09:40 2010 @@ -211,6 +211,12 @@ (Proposed and implemented by Mark Dickinson; :issue:`9337`.) +* :class:`memoryview` objects now have a :meth:`release()` method and support + the context manager protocol. This allows timely release of any resources + that were acquired when requesting a buffer from the original object. + + (Added by Antoine Pitrou; :issue:`9757`.) + * The :func:`functools.wraps` decorator now adds a :attr:`__wrapped__` attribute pointing to the original callable function. This allows wrapped functions to be introspected. It also copies :attr:`__annotations__` if defined. And now @@ -397,6 +403,14 @@ 5-tuple), and :data:`ssl.OPENSSL_VERSION_NUMBER` (an integer). (Added by Antoine Pitrou; :issue:`8321`.) +* Instances of :class:`unittest.TestCase` have two new methods + :meth:`~unittest.TestCase.assertWarns` and :meth:`~unittest.TestCase.assertWarnsRegexp` + to check that a given warning type was triggered by the code under test:: + + with self.assertWarns(DeprecationWarning): + legacy_function('XYZ') + + * The previously deprecated :func:`string.maketrans` function has been removed in favor of the static methods, :meth:`bytes.maketrans` and :meth:`bytearray.maketrans`. This change solves the confusion around which @@ -464,7 +478,7 @@ (Contributed by Antoine Pitrou; :issue:`7451`.) -- Python's peephole optimizer now recognizes patterns such ``x in {1, 2, 3}`` as +* Python's peephole optimizer now recognizes patterns such ``x in {1, 2, 3}`` as being a test for membership in a set of constants. The optimizer recasts the :class:`set` as a :class:`frozenset` and stores the pre-built constant. @@ -486,6 +500,14 @@ (Patch by Florent Xicluna in :issue:`7622` and :issue:`7462`.) +* Serializing and unserializing data using the :mod:`pickle` module is now + up to 4x faster, thanks to various optimizations initially contributed + to the Unladen Swalled project. + + (Ported to Python 3 by Alexandre Vassalotti and Antoine Pitrou in + :issue:`9410`) + + Filenames and Unicode ===================== @@ -498,6 +520,8 @@ The :mod:`os` module has two new functions: :func:`~os.fsencode` and :func:`~os.fsdecode`. +.. XXX mention Victor's improvements for support of undecodable filenames. + .. IDLE ==== From python-checkins at python.org Wed Sep 15 17:13:17 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 15 Sep 2010 17:13:17 +0200 (CEST) Subject: [Python-checkins] r84839 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100915151317.9700CEE996@mail.python.org> Author: antoine.pitrou Date: Wed Sep 15 17:13:17 2010 New Revision: 84839 Log: Move library changes to the right section Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Wed Sep 15 17:13:17 2010 @@ -217,20 +217,6 @@ (Added by Antoine Pitrou; :issue:`9757`.) -* The :func:`functools.wraps` decorator now adds a :attr:`__wrapped__` attribute - pointing to the original callable function. This allows wrapped functions to - be introspected. It also copies :attr:`__annotations__` if defined. And now - it also gracefully skips over missing attributes such as :attr:`__doc__` which - might not be defined for the wrapped callable. - - (By Nick Coghlan and Terrence Cole; :issue:`9567`, :issue:`3445`, and - :issue:`8814`.) - -* The :mod:`abc` module now supports :func:`~abc.abstractclassmethod` and - :func:`~abc.abstractstaticmethod`. - - (Patch submitted by Daniel Urban; :issue:`5867`.) - * A warning message will now get printed at interpreter shutdown if the :data:`gc.garbage` list isn't empty. This is meant to make the programmer aware that their code contains object finalization issues. @@ -304,6 +290,20 @@ (Contributed by Raymond Hettinger.) +* The :func:`functools.wraps` decorator now adds a :attr:`__wrapped__` attribute + pointing to the original callable function. This allows wrapped functions to + be introspected. It also copies :attr:`__annotations__` if defined. And now + it also gracefully skips over missing attributes such as :attr:`__doc__` which + might not be defined for the wrapped callable. + + (By Nick Coghlan and Terrence Cole; :issue:`9567`, :issue:`3445`, and + :issue:`8814`.) + +* The :mod:`abc` module now supports :func:`~abc.abstractclassmethod` and + :func:`~abc.abstractstaticmethod`. + + (Patch submitted by Daniel Urban; :issue:`5867`.) + * The previously deprecated :func:`contextlib.nested` function has been removed in favor of a plain :keyword:`with` statement which can accept multiple context managers. The latter technique is faster (because it is built-in), From python-checkins at python.org Wed Sep 15 23:43:47 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 15 Sep 2010 23:43:47 +0200 (CEST) Subject: [Python-checkins] r84840 - python/branches/py3k/Lib/asyncore.py Message-ID: <20100915214347.79B11FB1A@mail.python.org> Author: giampaolo.rodola Date: Wed Sep 15 23:43:47 2010 New Revision: 84840 Log: Store all errors signaling a disconnection into a global frozenset to save some computation time on recv() and send(). Modified: python/branches/py3k/Lib/asyncore.py Modified: python/branches/py3k/Lib/asyncore.py ============================================================================== --- python/branches/py3k/Lib/asyncore.py (original) +++ python/branches/py3k/Lib/asyncore.py Wed Sep 15 23:43:47 2010 @@ -56,6 +56,8 @@ from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EINTR, EISCONN, EBADF, ECONNABORTED, errorcode +DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED)) + try: socket_map except NameError: @@ -364,7 +366,7 @@ except socket.error as why: if why.args[0] == EWOULDBLOCK: return 0 - elif why.args[0] in (ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED): + elif why.args[0] in DISCONNECTED: self.handle_close() return 0 else: @@ -382,7 +384,7 @@ return data except socket.error as why: # winsock sometimes throws ENOTCONN - if why.args[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED]: + if why.args[0] in DISCONNECTED: self.handle_close() return b'' else: From python-checkins at python.org Wed Sep 15 23:59:05 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 15 Sep 2010 23:59:05 +0200 (CEST) Subject: [Python-checkins] r84841 - python/branches/py3k/Lib/asyncore.py Message-ID: <20100915215905.01CD1EE9B9@mail.python.org> Author: giampaolo.rodola Date: Wed Sep 15 23:59:04 2010 New Revision: 84841 Log: rename DISCONNECTED global constant in _DISCONNECTED Modified: python/branches/py3k/Lib/asyncore.py Modified: python/branches/py3k/Lib/asyncore.py ============================================================================== --- python/branches/py3k/Lib/asyncore.py (original) +++ python/branches/py3k/Lib/asyncore.py Wed Sep 15 23:59:04 2010 @@ -56,7 +56,7 @@ from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EINTR, EISCONN, EBADF, ECONNABORTED, errorcode -DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED)) +_DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED)) try: socket_map @@ -366,7 +366,7 @@ except socket.error as why: if why.args[0] == EWOULDBLOCK: return 0 - elif why.args[0] in DISCONNECTED: + elif why.args[0] in _DISCONNECTED: self.handle_close() return 0 else: @@ -384,7 +384,7 @@ return data except socket.error as why: # winsock sometimes throws ENOTCONN - if why.args[0] in DISCONNECTED: + if why.args[0] in _DISCONNECTED: self.handle_close() return b'' else: From python-checkins at python.org Thu Sep 16 00:40:38 2010 From: python-checkins at python.org (georg.brandl) Date: Thu, 16 Sep 2010 00:40:38 +0200 (CEST) Subject: [Python-checkins] r84842 - peps/trunk/pep-0444.txt Message-ID: <20100915224038.95AECC5D5@mail.python.org> Author: georg.brandl Date: Thu Sep 16 00:40:38 2010 New Revision: 84842 Log: Add PEP 444, Python Web3 Interface. Added: peps/trunk/pep-0444.txt (contents, props changed) Added: peps/trunk/pep-0444.txt ============================================================================== --- (empty file) +++ peps/trunk/pep-0444.txt Thu Sep 16 00:40:38 2010 @@ -0,0 +1,1570 @@ +PEP: 444 +Title: Python Web3 Interface +Version: $Revision$ +Last-Modified: $Date$ +Author: Chris McDonough , + Armin Ronacher +Discussions-To: Python Web-SIG +Status: Draft +Type: Informational +Content-Type: text/x-rst +Created: 19-Jul-2010 + + +Abstract +======== + +This document specifies a proposed second-generation standard +interface between web servers and Python web applications or +frameworks. + + +Rationale and Goals +=================== + +This protocol and specification is influenced heavily by the Web +Services Gateway Interface (WSGI) 1.0 standard described in PEP 333 +[1]_ . The high-level rationale for having any standard that allows +Python-based web servers and applications to interoperate is outlined +in PEP 333. This document essentially uses PEP 333 as a template, and +changes its wording in various places for the purpose of forming a +different standard. + +Python currently boasts a wide variety of web application frameworks +which use the WSGI 1.0 protocol. However, due to changes in the +language, the WSGI 1.0 protocol is not compatible with Python 3. This +specification describes a standardized WSGI-like protocol that lets +Python 2.6, 2.7 and 3.1+ applications communicate with web servers. +Web3 is clearly a WSGI derivative; it only uses a different name than +"WSGI" in order to indicate that it is not in any way backwards +compatible. + +Applications and servers which are written to this specification are +meant to work properly under Python 2.6.X, Python 2.7.X and Python +3.1+. Neither an application nor a server that implements the Web3 +specification can be easily written which will work under Python 2 +versions earlier than 2.6 nor Python 3 versions earlier than 3.1. + +.. note:: + + Whatever Python 3 version fixed http://bugs.python.org/issue4006 so + ``os.environ['foo']`` returns surrogates (ala PEP 383) when the + value of 'foo' cannot be decoded using the current locale instead + of failing with a KeyError is the *true* minimum Python 3 version. + In particular, however, Python 3.0 is not supported. + +.. note:: + + Python 2.6 is the first Python version that supported an alias for + ``bytes`` and the ``b"foo"`` literal syntax. This is why it is the + minimum version supported by Web3. + +Explicability and documentability are the main technical drivers for +the decisions made within the standard. + + +Differences from WSGI +===================== + +- All protocol-specific environment names are prefixed with ``web3.`` + rather than ``wsgi.``, eg. ``web3.input`` rather than + ``wsgi.input``. + +- All values present as environment dictionary *values* are explicitly + *bytes* instances instead of native strings. (Environment *keys* + however are native strings, always ``str`` regardless of + platform). + +- All values returned by an application must be bytes instances, + including status code, header names and values, and the body. + +- Wherever WSGI 1.0 referred to an ``app_iter``, this specification + refers to a ``body``. + +- No ``start_response()`` callback (and therefore no ``write()`` + callable nor ``exc_info`` data). + +- The ``readline()`` function of ``web3.input`` must support a size + hint parameter. + +- The ``read()`` function of ``web3.input`` must be length delimited. + A call without a size argument must not read more than the content + length header specifies. In case a content length header is absent + the stream must not return anything on read. It must never request + more data than specified from the client. + +- No requirement for middleware to yield an empty string if it needs + more information from an application to produce output (e.g. no + "Middleware Handling of Block Boundaries"). + +- Filelike objects passed to a "file_wrapper" must have an + ``__iter__`` which returns bytes (never text). + +- ``wsgi.file_wrapper`` is not supported. + +- ``QUERY_STRING``, ``SCRIPT_NAME``, ``PATH_INFO`` values required to + be placed in environ by server (each as the empty bytes instance if + no associated value is received in the HTTP request). + +- ``web3.path_info`` and ``web3.script_name`` should be put into the + Web3 environment, if possible, by the origin Web3 server. When + available, each is the original, plain 7-bit ASCII, URL-encoded + variant of its CGI equivalent derived directly from the request URI + (with %2F segment markers and other meta-characters intact). If the + server cannot provide one (or both) of these values, it must omit + the value(s) it cannot provide from the environment. + +- This requirement was removed: "middleware components **must not** + block iteration waiting for multiple values from an application + iterable. If the middleware needs to accumulate more data from the + application before it can produce any output, it **must** yield an + empty string." + +- ``SERVER_PORT`` must be a bytes instance (not an integer). + +- The server must not inject an additional ``Content-Length`` header + by guessing the length from the response iterable. This must be set + by the application itself in all situations. + +- If the origin server advertises that it has the ``web3.async`` + capability, a Web3 application callable used by the server is + permitted to return a callable that accepts no arguments. When it + does so, this callable is to be called periodically by the origin + server until it returns a non-``None`` response, which must be a + normal Web3 response tuple. + + .. XXX (chrism) Needs a section of its own for explanation. + + +Specification Overview +====================== + +The Web3 interface has two sides: the "server" or "gateway" side, and +the "application" or "framework" side. The server side invokes a +callable object that is provided by the application side. The +specifics of how that object is provided are up to the server or +gateway. It is assumed that some servers or gateways will require an +application's deployer to write a short script to create an instance +of the server or gateway, and supply it with the application object. +Other servers and gateways may use configuration files or other +mechanisms to specify where an application object should be imported +from, or otherwise obtained. + +In addition to "pure" servers/gateways and applications/frameworks, it +is also possible to create "middleware" components that implement both +sides of this specification. Such components act as an application to +their containing server, and as a server to a contained application, +and can be used to provide extended APIs, content transformation, +navigation, and other useful functions. + +Throughout this specification, we will use the term "application +callable" to mean "a function, a method, or an instance with a +``__call__`` method". It is up to the server, gateway, or application +implementing the application callable to choose the appropriate +implementation technique for their needs. Conversely, a server, +gateway, or application that is invoking a callable **must not** have +any dependency on what kind of callable was provided to it. +Application callables are only to be called, not introspected upon. + + +The Application/Framework Side +------------------------------ + +The application object is simply a callable object that accepts one +argument. The term "object" should not be misconstrued as requiring +an actual object instance: a function, method, or instance with a +``__call__`` method are all acceptable for use as an application +object. Application objects must be able to be invoked more than +once, as virtually all servers/gateways (other than CGI) will make +such repeated requests. It this cannot be guaranteed by the +implementation of the actual application, it has to be wrapped in a +function that creates a new instance on each call. + +.. note:: + + Although we refer to it as an "application" object, this should not + be construed to mean that application developers will use Web3 as a + web programming API. It is assumed that application developers + will continue to use existing, high-level framework services to + develop their applications. Web3 is a tool for framework and + server developers, and is not intended to directly support + application developers.) + +An example of an application which is a function (``simple_app``):: + + def simple_app(environ): + """Simplest possible application object""" + status = b'200 OK' + headers = [(b'Content-type', b'text/plain')] + body = [b'Hello world!\n'] + return body, status, headers + +An example of an application which is an instance (``simple_app``):: + + class AppClass(object): + + """Produce the same output, but using an instance. An + instance of this class must be instantiated before it is + passed to the server. """ + + def __call__(self, environ): + status = b'200 OK' + headers = [(b'Content-type', b'text/plain')] + body = [b'Hello world!\n'] + return body, status, headers + + simple_app = AppClass() + +Alternately, an application callable may return a callable instead of +the tuple if the server supports asynchronous execution. See +information concerning ``web3.async`` for more information. + + +The Server/Gateway Side +----------------------- + +The server or gateway invokes the application callable once for each +request it receives from an HTTP client, that is directed at the +application. To illustrate, here is a simple CGI gateway, implemented +as a function taking an application object. Note that this simple +example has limited error handling, because by default an uncaught +exception will be dumped to ``sys.stderr`` and logged by the web +server. + +:: + + import locale + import os + import sys + + encoding = locale.getpreferredencoding() + + stdout = sys.stdout + + if hasattr(sys.stdout, 'buffer'): + # Python 3 compatibility; we need to be able to push bytes out + stdout = sys.stdout.buffer + + def get_environ(): + d = {} + for k, v in os.environ.items(): + # Python 3 compatibility + if not isinstance(v, bytes): + # We must explicitly encode the string to bytes under + # Python 3.1+ + v = v.encode(encoding, 'surrogateescape') + d[k] = v + return d + + def run_with_cgi(application): + + environ = get_environ() + environ['web3.input'] = sys.stdin + environ['web3.errors'] = sys.stderr + environ['web3.version'] = (1, 0) + environ['web3.multithread'] = False + environ['web3.multiprocess'] = True + environ['web3.run_once'] = True + environ['web3.async'] = False + + if environ.get('HTTPS', b'off') in (b'on', b'1'): + environ['web3.url_scheme'] = b'https' + else: + environ['web3.url_scheme'] = b'http' + + rv = application(environ) + if hasattr(rv, '__call__'): + raise TypeError('This webserver does not support asynchronous ' + 'responses.') + body, status, headers = rv + + CLRF = b'\r\n' + + try: + stdout.write(b'Status: ' + status + CRLF) + for header_name, header_val in headers: + stdout.write(header_name + b': ' + header_val + CRLF) + stdout.write(CRLF) + for chunk in body: + stdout.write(chunk) + stdout.flush() + finally: + if hasattr(body, 'close'): + body.close() + + +Middleware: Components that Play Both Sides +------------------------------------------- + +A single object may play the role of a server with respect to some +application(s), while also acting as an application with respect to +some server(s). Such "middleware" components can perform such +functions as: + +* Routing a request to different application objects based on the + target URL, after rewriting the ``environ`` accordingly. + +* Allowing multiple applications or frameworks to run side-by-side in + the same process. + +* Load balancing and remote processing, by forwarding requests and + responses over a network. + +* Perform content postprocessing, such as applying XSL stylesheets. + +The presence of middleware in general is transparent to both the +"server/gateway" and the "application/framework" sides of the +interface, and should require no special support. A user who desires +to incorporate middleware into an application simply provides the +middleware component to the server, as if it were an application, and +configures the middleware component to invoke the application, as if +the middleware component were a server. Of course, the "application" +that the middleware wraps may in fact be another middleware component +wrapping another application, and so on, creating what is referred to +as a "middleware stack". + +A middleware must support asychronous execution if possible or fall +back to disabling itself. + +Here a middleware that changes the ``HTTP_HOST`` key if an ``X-Host`` +header exists and adds a comment to all html responses:: + + import time + + def apply_filter(app, environ, filter_func): + """Helper function that passes the return value from an + application to a filter function when the results are + ready. + """ + app_response = app(environ) + + # synchronous response, filter now + if not hasattr(app_response, '__call__'): + return filter_func(*app_response) + + # asychronous response. filter when results are ready + def polling_function(): + rv = app_response() + if rv is not None: + return filter_func(*rv) + return polling_function + + def proxy_and_timing_support(app): + def new_application(environ): + def filter_func(body, status, headers): + now = time.time() + for key, value in headers: + if key.lower() == b'content-type' and \ + value.split(b';')[0] == b'text/html': + # assumes ascii compatible encoding in body, + # but the middleware should actually parse the + # content type header and figure out the + # encoding when doing that. + body += ('' % + (now - then)).encode('ascii') + break + return body, status, headers + then = time.time() + host = environ.get('HTTP_X_HOST') + if host is not None: + environ['HTTP_HOST'] = host + + # use the apply_filter function that applies a given filter + # function for both async and sync responses. + return apply_filter(app, environ, filter_func) + return new_application + + app = proxy_and_timing_support(app) + + +Specification Details +===================== + +The application callable must accept one positional argument. For the +sake of illustration, we have named it ``environ``, but it is not +required to have this name. A server or gateway **must** invoke the +application object using a positional (not keyword) argument. +(E.g. by calling ``status, headers, body = application(environ)`` as +shown above.) + +The ``environ`` parameter is a dictionary object, containing CGI-style +environment variables. This object **must** be a builtin Python +dictionary (*not* a subclass, ``UserDict`` or other dictionary +emulation), and the application is allowed to modify the dictionary in +any way it desires. The dictionary must also include certain +Web3-required variables (described in a later section), and may also +include server-specific extension variables, named according to a +convention that will be described below. + +When called by the server, the application object must return a tuple +yielding three elements: ``status``, ``headers`` and ``body``, or, if +supported by an async server, an argumentless callable which either +returns ``None`` or a tuple of those three elements. + +The ``status`` element is a status in bytes of the form ``b'999 +Message here'``. + +``headers`` is a Python list of ``(header_name, header_value)`` pairs +describing the HTTP response header. The ``headers`` structure must +be a literal Python list; it must yield two-tuples. Both +``header_name`` and ``header_value`` must be bytes values. + +The ``body`` is an iterable yielding zero or more bytes instances. +This can be accomplished in a variety of ways, such as by returning a +list containing bytes instances as ``body``, or by returning a +generator function as ``body`` that yields bytes instances, or by the +``body`` being an instance of a class which is iterable. Regardless +of how it is accomplished, the application object must always return a +``body`` iterable yielding zero or more bytes instances. + +The server or gateway must transmit the yielded bytes to the client in +an unbuffered fashion, completing the transmission of each set of +bytes before requesting another one. (In other words, applications +**should** perform their own buffering. See the `Buffering and +Streaming`_ section below for more on how application output must be +handled.) + +The server or gateway should treat the yielded bytes as binary byte +sequences: in particular, it should ensure that line endings are not +altered. The application is responsible for ensuring that the +string(s) to be written are in a format suitable for the client. (The +server or gateway **may** apply HTTP transfer encodings, or perform +other transformations for the purpose of implementing HTTP features +such as byte-range transmission. See `Other HTTP Features`_, below, +for more details.) + +If the ``body`` iterable returned by the application has a ``close()`` +method, the server or gateway **must** call that method upon +completion of the current request, whether the request was completed +normally, or terminated early due to an error. This is to support +resource release by the application amd is intended to complement PEP +325's generator support, and other common iterables with ``close()`` +methods. + +Finally, servers and gateways **must not** directly use any other +attributes of the ``body`` iterable returned by the application. + + +``environ`` Variables +--------------------- + +The ``environ`` dictionary is required to contain various CGI +environment variables, as defined by the Common Gateway Interface +specification [2]_. + +The following CGI variables **must** be present. Each key is a native +string. Each value is a bytes instance. + +.. note:: + + In Python 3.1+, a "native string" is a ``str`` type decoded using + the ``surrogateescape`` error handler, as done by + ``os.environ.__getitem__``. In Python 2.6 and 2.7, a "native + string" is a ``str`` types representing a set of bytes. + +``REQUEST_METHOD`` + The HTTP request method, such as ``"GET"`` or ``"POST"``. + +``SCRIPT_NAME`` + The initial portion of the request URL's "path" that corresponds to + the application object, so that the application knows its virtual + "location". This may be the empty bytes instance if the application + corresponds to the "root" of the server. SCRIPT_NAME will be a + bytes instance representing a sequence of URL-encoded segments + separated by the slash character (``/``). It is assumed that + ``%2F`` characters will be decoded into literal slash characters + within ``PATH_INFO`` , as per CGI. + +``PATH_INFO`` + The remainder of the request URL's "path", designating the virtual + "location" of the request's target within the application. This + **may** be a bytes instance if the request URL targets the + application root and does not have a trailing slash. PATH_INFO will + be a bytes instance representing a sequence of URL-encoded segments + separated by the slash character (``/``). It is assumed that + ``%2F`` characters will be decoded into literal slash characters + within ``PATH_INFO`` , as per CGI. + +``QUERY_STRING`` + The portion of the request URL (in bytes) that follows the ``"?"``, + if any, or the empty bytes instance. + +``SERVER_NAME``, ``SERVER_PORT`` + When combined with ``SCRIPT_NAME`` and ``PATH_INFO`` (or their raw + equivalents)`, these variables can be used to complete the URL. + Note, however, that ``HTTP_HOST``, if present, should be used in + preference to ``SERVER_NAME`` for reconstructing the request URL. + See the `URL Reconstruction`_ section below for more detail. + ``SERVER_PORT`` should be a bytes instance, not an integer. + +``SERVER_PROTOCOL`` + The version of the protocol the client used to send the request. + Typically this will be something like ``"HTTP/1.0"`` or + ``"HTTP/1.1"`` and may be used by the application to determine how + to treat any HTTP request headers. (This variable should probably + be called ``REQUEST_PROTOCOL``, since it denotes the protocol used + in the request, and is not necessarily the protocol that will be + used in the server's response. However, for compatibility with CGI + we have to keep the existing name.) + +The following CGI values **may** present be in the Web3 environment. +Each key is a native string. Each value is a bytes instances. + +``CONTENT_TYPE`` + The contents of any ``Content-Type`` fields in the HTTP request. + +``CONTENT_LENGTH`` + The contents of any ``Content-Length`` fields in the HTTP request. + +``HTTP_`` Variables + Variables corresponding to the client-supplied HTTP request headers + (i.e., variables whose names begin with ``"HTTP_"``). The presence + or absence of these variables should correspond with the presence or + absence of the appropriate HTTP header in the request. + +A server or gateway **should** attempt to provide as many other CGI +variables as are applicable, each with a string for its key and a +bytes instance for its value. In addition, if SSL is in use, the +server or gateway **should** also provide as many of the Apache SSL +environment variables [5]_ as are applicable, such as ``HTTPS=on`` and +``SSL_PROTOCOL``. Note, however, that an application that uses any +CGI variables other than the ones listed above are necessarily +non-portable to web servers that do not support the relevant +extensions. (For example, web servers that do not publish files will +not be able to provide a meaningful ``DOCUMENT_ROOT`` or +``PATH_TRANSLATED``.) + +A Web3-compliant server or gateway **should** document what variables +it provides, along with their definitions as appropriate. +Applications **should** check for the presence of any variables they +require, and have a fallback plan in the event such a variable is +absent. + +Note that CGI variable *values* must be bytes instances, if they are +present at all. It is a violation of this specification for a CGI +variable's value to be of any type other than ``bytes``. On Python 2, +this means they will be of type ``str``. On Python 3, this means they +will be of type ``bytes``. + +They *keys* of all CGI and non-CGI variables in the environ, however, +must be "native strings" (on both Python 2 and Python 3, they will be +of type ``str``). + +In addition to the CGI-defined variables, the ``environ`` dictionary +**may** also contain arbitrary operating-system "environment +variables", and **must** contain the following Web3-defined variables. + +===================== =============================================== +Variable Value +===================== =============================================== +``web3.version`` The tuple ``(1, 0)``, representing Web3 + version 1.0. + +``web3.url_scheme`` A bytes value representing the "scheme" portion of + the URL at which the application is being + invoked. Normally, this will have the value + ``b"http"`` or ``b"https"``, as appropriate. + +``web3.input`` An input stream (file-like object) from which bytes + constituting the HTTP request body can be read. + (The server or gateway may perform reads + on-demand as requested by the application, or + it may pre- read the client's request body and + buffer it in-memory or on disk, or use any + other technique for providing such an input + stream, according to its preference.) + +``web3.errors`` An output stream (file-like object) to which error + output text can be written, for the purpose of + recording program or other errors in a + standardized and possibly centralized location. + This should be a "text mode" stream; i.e., + applications should use ``"\n"`` as a line + ending, and assume that it will be converted to + the correct line ending by the server/gateway. + Applications may *not* send bytes to the + 'write' method of this stream; they may only + send text. + + For many servers, ``web3.errors`` will be the + server's main error log. Alternatively, this + may be ``sys.stderr``, or a log file of some + sort. The server's documentation should + include an explanation of how to configure this + or where to find the recorded output. A server + or gateway may supply different error streams + to different applications, if this is desired. + +``web3.multithread`` This value should evaluate true if the + application object may be simultaneously + invoked by another thread in the same process, + and should evaluate false otherwise. + +``web3.multiprocess`` This value should evaluate true if an + equivalent application object may be + simultaneously invoked by another process, and + should evaluate false otherwise. + +``web3.run_once`` This value should evaluate true if the server + or gateway expects (but does not guarantee!) + that the application will only be invoked this + one time during the life of its containing + process. Normally, this will only be true for + a gateway based on CGI (or something similar). + +``web3.script_name`` The non-URL-decoded ``SCRIPT_NAME`` value. + Through a historical inequity, by virtue of the + CGI specification, ``SCRIPT_NAME`` is present + within the environment as an already + URL-decoded string. This is the original + URL-encoded value derived from the request URI. + If the server cannot provide this value, it + must omit it from the environ. + +``web3.path_info`` The non-URL-decoded ``PATH_INFO`` value. + Through a historical inequity, by virtue of the + CGI specification, ``PATH_INFO`` is present + within the environment as an already + URL-decoded string. This is the original + URL-encoded value derived from the request URI. + If the server cannot provide this value, it + must omit it from the environ. + +``web3.async`` This is ``True`` if the webserver supports + async invocation. In that case an application + is allowed to return a callable instead of a + tuple with the response. The exact semantics + are not specified by this specification. + +===================== =============================================== + +Finally, the ``environ`` dictionary may also contain server-defined +variables. These variables should have names which are native +strings, composed of only lower-case letters, numbers, dots, and +underscores, and should be prefixed with a name that is unique to the +defining server or gateway. For example, ``mod_web3`` might define +variables with names like ``mod_web3.some_variable``. + + +Input Stream +~~~~~~~~~~~~ + +The input stream (``web3.input``) provided by the server must support +the following methods: + +===================== ======== +Method Notes +===================== ======== +``read(size)`` 1,4 +``readline([size])`` 1,2,4 +``readlines([size])`` 1,3,4 +``__iter__()`` 4 +===================== ======== + +The semantics of each method are as documented in the Python Library +Reference, except for these notes as listed in the table above: + +1. The server is not required to read past the client's specified + ``Content-Length``, and is allowed to simulate an end-of-file + condition if the application attempts to read past that point. The + application **should not** attempt to read more data than is + specified by the ``CONTENT_LENGTH`` variable. + +2. The implementation must support the optional ``size`` argument to + ``readline()``. + +3. The application is free to not supply a ``size`` argument to + ``readlines()``, and the server or gateway is free to ignore the + value of any supplied ``size`` argument. + +4. The ``read``, ``readline`` and ``__iter__`` methods must return a + bytes instance. The ``readlines`` method must return a sequence + which contains instances of bytes. + +The methods listed in the table above **must** be supported by all +servers conforming to this specification. Applications conforming to +this specification **must not** use any other methods or attributes of +the ``input`` object. In particular, applications **must not** +attempt to close this stream, even if it possesses a ``close()`` +method. + +The input stream should silently ignore attempts to read more than the +content length of the request. If no content length is specified the +stream must be a dummy stream that does not return anything. + + +Error Stream +~~~~~~~~~~~~ + +The error stream (``web3.errors``) provided by the server must support +the following methods: + +=================== ========== ======== +Method Stream Notes +=================== ========== ======== +``flush()`` ``errors`` 1 +``write(str)`` ``errors`` 2 +``writelines(seq)`` ``errors`` 2 +=================== ========== ======== + +The semantics of each method are as documented in the Python Library +Reference, except for these notes as listed in the table above: + +1. Since the ``errors`` stream may not be rewound, servers and + gateways are free to forward write operations immediately, without + buffering. In this case, the ``flush()`` method may be a no-op. + Portable applications, however, cannot assume that output is + unbuffered or that ``flush()`` is a no-op. They must call + ``flush()`` if they need to ensure that output has in fact been + written. (For example, to minimize intermingling of data from + multiple processes writing to the same error log.) + +2. The ``write()`` method must accept a string argument, but needn't + necessarily accept a bytes argument. The ``writelines()`` method + must accept a sequence argument that consists entirely of strings, + but needn't necessarily accept any bytes instance as a member of + the sequence. + +The methods listed in the table above **must** be supported by all +servers conforming to this specification. Applications conforming to +this specification **must not** use any other methods or attributes of +the ``errors`` object. In particular, applications **must not** +attempt to close this stream, even if it possesses a ``close()`` +method. + + +Values Returned by A Web3 Application +------------------------------------- + +Web3 applications return an iterable in the form (``status``, +``headers``, ``body``). The return value can be any iterable type +that returns exactly three values. If the server supports +asynchronous applications (``web3.async``), the response may be a +callable object (which accepts no arguments). + +The ``status`` value is assumed by a gateway or server to be an HTTP +"status" bytes instance like ``b'200 OK'`` or ``b'404 Not Found'``. +That is, it is a string consisting of a Status-Code and a +Reason-Phrase, in that order and separated by a single space, with no +surrounding whitespace or other characters. (See RFC 2616, Section +6.1.1 for more information.) The string **must not** contain control +characters, and must not be terminated with a carriage return, +linefeed, or combination thereof. + +The ``headers`` value is assumed by a gateway or server to be a +literal Python list of ``(header_name, header_value)`` tuples. Each +``header_name`` must be a bytes instance representing a valid HTTP +header field-name (as defined by RFC 2616, Section 4.2), without a +trailing colon or other punctuation. Each ``header_value`` must be a +bytes instance and **must not** include any control characters, +including carriage returns or linefeeds, either embedded or at the +end. (These requirements are to minimize the complexity of any +parsing that must be performed by servers, gateways, and intermediate +response processors that need to inspect or modify response headers.) + +In general, the server or gateway is responsible for ensuring that +correct headers are sent to the client: if the application omits a +header required by HTTP (or other relevant specifications that are in +effect), the server or gateway **must** add it. For example, the HTTP +``Date:`` and ``Server:`` headers would normally be supplied by the +server or gateway. The gateway must however not override values with +the same name if they are emitted by the application. + +(A reminder for server/gateway authors: HTTP header names are +case-insensitive, so be sure to take that into consideration when +examining application-supplied headers!) + +Applications and middleware are forbidden from using HTTP/1.1 +"hop-by-hop" features or headers, any equivalent features in HTTP/1.0, +or any headers that would affect the persistence of the client's +connection to the web server. These features are the exclusive +province of the actual web server, and a server or gateway **should** +consider it a fatal error for an application to attempt sending them, +and raise an error if they are supplied as return values from an +application in the ``headers`` structure. (For more specifics on +"hop-by-hop" features and headers, please see the `Other HTTP +Features`_ section below.) + + +Dealing with Compatibility Across Python Versions +------------------------------------------------- + +Creating Web3 code that runs under both Python 2.6/2.7 and Python 3.1+ +requires some care on the part of the developer. In general, the Web3 +specification assumes a certain level of equivalence between the +Python 2 ``str`` type and the Python 3 ``bytes`` type. For example, +under Python 2, the values present in the Web3 ``environ`` will be +instances of the ``str`` type; in Python 3, these will be instances of +the ``bytes`` type. The Python 3 ``bytes`` type does not possess all +the methods of the Python 2 ``str`` type, and some methods which it +does possess behave differently than the Python 2 ``str`` type. +Effectively, to ensure that Web3 middleware and applications work +across Python versions, developers must do these things: + +#) Do not assume comparison equivalence between text values and bytes + values. If you do so, your code may work under Python 2, but it + will not work properly under Python 3. For example, don't write + ``somebytes == 'abc'``. This will sometimes be true on Python 2 + but it will never be true on Python 3, because a sequence of bytes + never compares equal to a string under Python 3. Instead, always + compare a bytes value with a bytes value, e.g. "somebytes == + b'abc'". Code which does this is compatible with and works the + same in Python 2.6, 2.7, and 3.1. The ``b`` in front of ``'abc'`` + signals to Python 3 that the value is a literal bytes instance; + under Python 2 it's a forward compatibility placebo. + +#) Don't use the ``__contains__`` method (directly or indirectly) of + items that are meant to be byteslike without ensuring that its + argument is also a bytes instance. If you do so, your code may + work under Python 2, but it will not work properly under Python 3. + For example, ``'abc' in somebytes'`` will raise a ``TypeError`` + under Python 3, but it will return ``True`` under Python 2.6 and + 2.7. However, ``b'abc' in somebytes`` will work the same on both + versions. In Python 3.2, this restriction may be partially + removed, as it's rumored that bytes types may obtain a ``__mod__`` + implementation. + +#) ``__getitem__`` should not be used. + + .. XXX + +#) Dont try to use the ``format`` method or the ``__mod__`` method of + instances of bytes (directly or indirectly). In Python 2, the + ``str`` type which we treat equivalently to Python 3's ``bytes`` + supports these method but actual Python 3's ``bytes`` instances + don't support these methods. If you use these methods, your code + will work under Python 2, but not under Python 3. + +#) Do not try to concatenate a bytes value with a string value. This + may work under Python 2, but it will not work under Python 3. For + example, doing ``'abc' + somebytes`` will work under Python 2, but + it will result in a ``TypeError`` under Python 3. Instead, always + make sure you're concatenating two items of the same type, + e.g. ``b'abc' + somebytes``. + +Web3 expects byte values in other places, such as in all the values +returned by an application. + +In short, to ensure compatibility of Web3 application code between +Python 2 and Python 3, in Python 2, treat CGI and server variable +values in the environment as if they had the Python 3 ``bytes`` API +even though they actually have a more capable API. Likewise for all +stringlike values returned by a Web3 application. + + +Buffering and Streaming +----------------------- + +Generally speaking, applications will achieve the best throughput by +buffering their (modestly-sized) output and sending it all at once. +This is a common approach in existing frameworks: the output is +buffered in a StringIO or similar object, then transmitted all at +once, along with the response headers. + +The corresponding approach in Web3 is for the application to simply +return a single-element ``body`` iterable (such as a list) containing +the response body as a single string. This is the recommended +approach for the vast majority of application functions, that render +HTML pages whose text easily fits in memory. + +For large files, however, or for specialized uses of HTTP streaming +(such as multipart "server push"), an application may need to provide +output in smaller blocks (e.g. to avoid loading a large file into +memory). It's also sometimes the case that part of a response may be +time-consuming to produce, but it would be useful to send ahead the +portion of the response that precedes it. + +In these cases, applications will usually return a ``body`` iterator +(often a generator-iterator) that produces the output in a +block-by-block fashion. These blocks may be broken to coincide with +mulitpart boundaries (for "server push"), or just before +time-consuming tasks (such as reading another block of an on-disk +file). + +Web3 servers, gateways, and middleware **must not** delay the +transmission of any block; they **must** either fully transmit the +block to the client, or guarantee that they will continue transmission +even while the application is producing its next block. A +server/gateway or middleware may provide this guarantee in one of +three ways: + +1. Send the entire block to the operating system (and request that any + O/S buffers be flushed) before returning control to the + application, OR + +2. Use a different thread to ensure that the block continues to be + transmitted while the application produces the next block. + +3. (Middleware only) send the entire block to its parent + gateway/server. + +By providing this guarantee, Web3 allows applications to ensure that +transmission will not become stalled at an arbitrary point in their +output data. This is critical for proper functioning of +e.g. multipart "server push" streaming, where data between multipart +boundaries should be transmitted in full to the client. + + +Unicode Issues +-------------- + +HTTP does not directly support Unicode, and neither does this +interface. All encoding/decoding must be handled by the +**application**; all values passed to or from the server must be of +the Python 3 type ``bytes`` or instances of the Python 2 type ``str``, +not Python 2 ``unicode`` or Python 3 ``str`` objects. + +All "bytes instances" referred to in this specification **must**: + +- On Python 2, be of type ``str``. + +- On Python 3, be of type ``bytes``. + +All "bytes instances" **must not** : + +- On Python 2, be of type ``unicode``. + +- On Python 3, be of type ``str``. + +The result of using a textlike object where a byteslike object is +required is undefined. + +Values returned from a Web3 app as a status or as response headers +**must** follow RFC 2616 with respect to encoding. That is, the bytes +returned must contain a character stream of ISO-8859-1 characters, or +the character stream should use RFC 2047 MIME encoding. + +On Python platforms which do not have a native bytes-like type +(e.g. IronPython, etc.), but instead which generally use textlike +strings to represent bytes data, the definition of "bytes instance" +can be changed: their "bytes instances" must be native strings that +contain only code points representable in ISO-8859-1 encoding +(``\u0000`` through ``\u00FF``, inclusive). It is a fatal error for +an application on such a platform to supply strings containing any +other Unicode character or code point. Similarly, servers and +gateways on those platforms **must not** supply strings to an +application containing any other Unicode characters. + +.. XXX (armin: Jython now has a bytes type, we might remove this + section after seeing about IronPython) + + +HTTP 1.1 Expect/Continue +------------------------ + +Servers and gateways that implement HTTP 1.1 **must** provide +transparent support for HTTP 1.1's "expect/continue" mechanism. This +may be done in any of several ways: + +1. Respond to requests containing an ``Expect: 100-continue`` request + with an immediate "100 Continue" response, and proceed normally. + +2. Proceed with the request normally, but provide the application with + a ``web3.input`` stream that will send the "100 Continue" response + if/when the application first attempts to read from the input + stream. The read request must then remain blocked until the client + responds. + +3. Wait until the client decides that the server does not support + expect/continue, and sends the request body on its own. (This is + suboptimal, and is not recommended.) + +Note that these behavior restrictions do not apply for HTTP 1.0 +requests, or for requests that are not directed to an application +object. For more information on HTTP 1.1 Expect/Continue, see RFC +2616, sections 8.2.3 and 10.1.1. + + +Other HTTP Features +------------------- + +In general, servers and gateways should "play dumb" and allow the +application complete control over its output. They should only make +changes that do not alter the effective semantics of the application's +response. It is always possible for the application developer to add +middleware components to supply additional features, so server/gateway +developers should be conservative in their implementation. In a +sense, a server should consider itself to be like an HTTP "gateway +server", with the application being an HTTP "origin server". (See RFC +2616, section 1.3, for the definition of these terms.) + +However, because Web3 servers and applications do not communicate via +HTTP, what RFC 2616 calls "hop-by-hop" headers do not apply to Web3 +internal communications. Web3 applications **must not** generate any +"hop-by-hop" headers [4]_, attempt to use HTTP features that would +require them to generate such headers, or rely on the content of any +incoming "hop-by-hop" headers in the ``environ`` dictionary. Web3 +servers **must** handle any supported inbound "hop-by-hop" headers on +their own, such as by decoding any inbound ``Transfer-Encoding``, +including chunked encoding if applicable. + +Applying these principles to a variety of HTTP features, it should be +clear that a server **may** handle cache validation via the +``If-None-Match`` and ``If-Modified-Since`` request headers and the +``Last-Modified`` and ``ETag`` response headers. However, it is not +required to do this, and the application **should** perform its own +cache validation if it wants to support that feature, since the +server/gateway is not required to do such validation. + +Similarly, a server **may** re-encode or transport-encode an +application's response, but the application **should** use a suitable +content encoding on its own, and **must not** apply a transport +encoding. A server **may** transmit byte ranges of the application's +response if requested by the client, and the application doesn't +natively support byte ranges. Again, however, the application +**should** perform this function on its own if desired. + +Note that these restrictions on applications do not necessarily mean +that every application must reimplement every HTTP feature; many HTTP +features can be partially or fully implemented by middleware +components, thus freeing both server and application authors from +implementing the same features over and over again. + + +Thread Support +-------------- + +Thread support, or lack thereof, is also server-dependent. Servers +that can run multiple requests in parallel, **should** also provide +the option of running an application in a single-threaded fashion, so +that applications or frameworks that are not thread-safe may still be +used with that server. + + +Implementation/Application Notes +================================ + +Server Extension APIs +--------------------- + +Some server authors may wish to expose more advanced APIs, that +application or framework authors can use for specialized purposes. +For example, a gateway based on ``mod_python`` might wish to expose +part of the Apache API as a Web3 extension. + +In the simplest case, this requires nothing more than defining an +``environ`` variable, such as ``mod_python.some_api``. But, in many +cases, the possible presence of middleware can make this difficult. +For example, an API that offers access to the same HTTP headers that +are found in ``environ`` variables, might return different data if +``environ`` has been modified by middleware. + +In general, any extension API that duplicates, supplants, or bypasses +some portion of Web3 functionality runs the risk of being incompatible +with middleware components. Server/gateway developers should *not* +assume that nobody will use middleware, because some framework +developers specifically organize their frameworks to function almost +entirely as middleware of various kinds. + +So, to provide maximum compatibility, servers and gateways that +provide extension APIs that replace some Web3 functionality, **must** +design those APIs so that they are invoked using the portion of the +API that they replace. For example, an extension API to access HTTP +request headers must require the application to pass in its current +``environ``, so that the server/gateway may verify that HTTP headers +accessible via the API have not been altered by middleware. If the +extension API cannot guarantee that it will always agree with +``environ`` about the contents of HTTP headers, it must refuse service +to the application, e.g. by raising an error, returning ``None`` +instead of a header collection, or whatever is appropriate to the API. + +These guidelines also apply to middleware that adds information such +as parsed cookies, form variables, sessions, and the like to +``environ``. Specifically, such middleware should provide these +features as functions which operate on ``environ``, rather than simply +stuffing values into ``environ``. This helps ensure that information +is calculated from ``environ`` *after* any middleware has done any URL +rewrites or other ``environ`` modifications. + +It is very important that these "safe extension" rules be followed by +both server/gateway and middleware developers, in order to avoid a +future in which middleware developers are forced to delete any and all +extension APIs from ``environ`` to ensure that their mediation isn't +being bypassed by applications using those extensions! + + +Application Configuration +------------------------- + +This specification does not define how a server selects or obtains an +application to invoke. These and other configuration options are +highly server-specific matters. It is expected that server/gateway +authors will document how to configure the server to execute a +particular application object, and with what options (such as +threading options). + +Framework authors, on the other hand, should document how to create an +application object that wraps their framework's functionality. The +user, who has chosen both the server and the application framework, +must connect the two together. However, since both the framework and +the server have a common interface, this should be merely a mechanical +matter, rather than a significant engineering effort for each new +server/framework pair. + +Finally, some applications, frameworks, and middleware may wish to use +the ``environ`` dictionary to receive simple string configuration +options. Servers and gateways **should** support this by allowing an +application's deployer to specify name-value pairs to be placed in +``environ``. In the simplest case, this support can consist merely of +copying all operating system-supplied environment variables from +``os.environ`` into the ``environ`` dictionary, since the deployer in +principle can configure these externally to the server, or in the CGI +case they may be able to be set via the server's configuration files. + +Applications **should** try to keep such required variables to a +minimum, since not all servers will support easy configuration of +them. Of course, even in the worst case, persons deploying an +application can create a script to supply the necessary configuration +values:: + + from the_app import application + + def new_app(environ): + environ['the_app.configval1'] = b'something' + return application(environ) + +But, most existing applications and frameworks will probably only need +a single configuration value from ``environ``, to indicate the +location of their application or framework-specific configuration +file(s). (Of course, applications should cache such configuration, to +avoid having to re-read it upon each invocation.) + + +URL Reconstruction +------------------ + +If an application wishes to reconstruct a request's complete URL (as a +bytes object), it may do so using the following algorithm:: + + host = environ.get('HTTP_HOST') + + scheme = environ['web3.url_scheme'] + port = environ['SERVER_PORT'] + query = environ['QUERY_STRING'] + + url = scheme + b'://' + + if host: + url += host + else: + url += environ['SERVER_NAME'] + + if scheme == b'https': + if port != b'443': + url += b':' + port + else: + if port != b'80': + url += b':' + port + + if 'web3.script_name' in url: + url += url_quote(environ['web3.script_name']) + else: + url += environ['SCRIPT_NAME'] + if 'web3.path_info' in environ: + url += url_quote(environ['web3.path_info']) + else: + url += environ['PATH_INFO'] + if query: + url += b'?' + query + +Note that such a reconstructed URL may not be precisely the same URI +as requested by the client. Server rewrite rules, for example, may +have modified the client's originally requested URL to place it in a +canonical form. + + +Open Questions +============== + +- ``file_wrapper`` replacement. Currently nothing is specified here + but it's clear that the old system of in-band signalling is broken + if it does not provide a way to figure out as a middleware in the + process if the response is a file wrapper. + + +Points of Contention +==================== + +Outlined below are potential points of contention regarding this +specification. + + +WSGI 1.0 Compatibility +---------------------- + +Components written using the WSGI 1.0 specification will not +transparently interoperate with components written using this +specification. That's because the goals of this proposal and the +goals of WSGI 1.0 are not directly aligned. + +WSGI 1.0 is obliged to provide specification-level backwards +compatibility with versions of Python between 2.2 and 2.7. This +specification, however, ditches Python 2.5 and lower compatibility in +order to provide compatibility between relatively recent versions of +Python 2 (2.6 and 2.7) as well as relatively recent versions of Python +3 (3.1). + +It is currently impossible to write components which work reliably +under both Python 2 and Python 3 using the WSGI 1.0 specification, +because the specification implicitly posits that CGI and server +variable values in the environ and values returned via +``start_response`` represent a sequence of bytes that can be addressed +using the Python 2 string API. It posits such a thing because that +sort of data type was the sensible way to represent bytes in all +Python 2 versions, and WSGI 1.0 was conceived before Python 3 existed. + +Python 3's ``str`` type supports the full API provided by the Python 2 +``str`` type, but Python 3's ``str`` type does not represent a +sequence of bytes, it instead represents text. Therefore, using it to +represent environ values also requires that the environ byte sequence +be decoded to text via some encoding. We cannot decode these bytes to +text (at least in any way where the decoding has any meaning other +than as a tunnelling mechanism) without widening the scope of WSGI to +include server and gateway knowledge of decoding policies and +mechanics. WSGI 1.0 never concerned itself with encoding and +decoding. It made statements about allowable transport values, and +suggested that various values might be best decoded as one encoding or +another, but it never required a server to *perform* any decoding +before + +Python 3 does not have a stringlike type that can be used instead to +represent bytes: it has a ``bytes`` type. A bytes type operates quite +a bit like a Python 2 ``str`` in Python 3.1+, but it lacks behavior +equivalent to ``str.__mod__`` and its iteration protocol, and +containment, sequence treatment, and equivalence comparisons are +different. + +In either case, there is no type in Python 3 that behaves just like +the Python 2 ``str`` type, and a way to create such a type doesn't +exist because there is no such thing as a "String ABC" which would +allow a suitable type to be built. Due to this design +incompatibility, existing WSGI 1.0 servers, middleware, and +applications will not work under Python 3, even after they are run +through ``2to3``. + +Existing Web-SIG discussions about updating the WSGI specification so +that it is possible to write a WSGI application that runs in both +Python 2 and Python 3 tend to revolve around creating a +specification-level equivalence between the Python 2 ``str`` type +(which represents a sequence of bytes) and the Python 3 ``str`` type +(which represents text). Such an equivalence becomes strained in +various areas, given the different roles of these types. An arguably +more straightforward equivalence exists between the Python 3 ``bytes`` +type API and a subset of the Python 2 ``str`` type API. This +specification exploits this subset equivalence. + +In the meantime, aside from any Python 2 vs. Python 3 compatibility +issue, as various discussions on Web-SIG have pointed out, the WSGI +1.0 specification is too general, providing support (via ``.write``) +for asynchronous applications at the expense of implementation +complexity. This specification uses the fundamental incompatibility +between WSGI 1.0 and Python 3 as a natural divergence point to create +a specification with reduced complexity by changing specialized +support for asynchronous applications. + +To provide backwards compatibility for older WSGI 1.0 applications, so +that they may run on a Web3 stack, it is presumed that Web3 middleware +will be created which can be used "in front" of existing WSGI 1.0 +applications, allowing those existing WSGI 1.0 applications to run +under a Web3 stack. This middleware will require, when under Python +3, an equivalence to be drawn between Python 3 ``str`` types and the +bytes values represented by the HTTP request and all the attendant +encoding-guessing (or configuration) it implies. + +.. note:: + + Such middleware *might* in the future, instead of drawing an + equivalence between Python 3 ``str`` and HTTP byte values, make use + of a yet-to-be-created "ebytes" type (aka "bytes-with-benefits"), + particularly if a String ABC proposal is accepted into the Python + core and implemented. + +Conversely, it is presumed that WSGI 1.0 middleware will be created +which will allow a Web3 application to run behind a WSGI 1.0 stack on +the Python 2 platform. + + +Environ and Response Values as Bytes +------------------------------------ + +Casual middleware and application writers may consider the use of +bytes as environment values and response values inconvenient. In +particular, they won't be able to use common string formatting +functions such as ``('%s' % bytes_val)`` or +``bytes_val.format('123')`` because bytes don't have the same API as +strings on platforms such as Python 3 where the two types differ. +Likewise, on such platforms, stdlib HTTP-related API support for using +bytes interchangeably with text can be spotty. In places where bytes +are inconvenient or incompatible with library APIs, middleware and +application writers will have to decode such bytes to text explicitly. +This is particularly inconvenient for middleware writers: to work with +environment values as strings, they'll have to decode them from an +implied encoding and if they need to mutate an environ value, they'll +then need to encode the value into a byte stream before placing it +into the environ. While the use of bytes by the specification as +environ values might be inconvenient for casual developers, it +provides several benefits. + +Using bytes types to represent HTTP and server values to an +application most closely matches reality because HTTP is fundamentally +a bytes-oriented protocol. If the environ values are mandated to be +strings, each server will need to use heuristics to guess about the +encoding of various values provided by the HTTP environment. Using +all strings might increase casual middleware writer convenience, but +will also lead to ambiguity and confusion when a value cannot be +decoded to a meaningful non-surrogate string. + +Use of bytes as environ values avoids any potential for the need for +the specification to mandate that a participating server be informed +of encoding configuration parameters. If environ values are treated +as strings, and so must be decoded from bytes, configuration +parameters may eventually become necessary as policy clues from the +application deployer. Such a policy would be used to guess an +appropriate decoding strategy in various circumstances, effectively +placing the burden for enforcing a particular application encoding +policy upon the server. If the server must serve more than one +application, such configuration would quickly become complex. Many +policies would also be impossible to express declaratively. + +In reality, HTTP is a complicated and legacy-fraught protocol which +requires a complex set of heuristics to make sense of. It would be +nice if we could allow this protocol to protect us from this +complexity, but we cannot do so reliably while still providing to +application writers a level of control commensurate with reality. +Python applications must often deal with data embedded in the +environment which not only must be parsed by legacy heuristics, but +*does not conform even to any existing HTTP specification*. While +these eventualities are unpleasant, they crop up with regularity, +making it impossible and undesirable to hide them from application +developers, as application developers are the only people who are able +to decide upon an appropriate action when an HTTP specification +violation is detected. + +Some have argued for mixed use of bytes and string values as environ +*values*. This proposal avoids that strategy. Sole use of bytes as +environ values makes it possible to fit this specification entirely in +one's head; you won't need to guess about which values are strings and +which are bytes. + +This protocol would also fit in a developer's head if all environ +values were strings, but this specification doesn't use that strategy. +This will likely be the point of greatest contention regarding the use +of bytes. In defense of bytes: developers often prefer protocols with +consistent contracts, even if the contracts themselves are suboptimal. +If we hide encoding issues from a developer until a value that +contains surrogates causes problems after it has already reached +beyond the I/O boundary of their application, they will need to do a +lot more work to fix assumptions made by their application than if we +were to just present the problem much earlier in terms of "here's some +bytes, you decode them". This is also a counter-argument to the +"bytes are inconvenient" assumption: while presenting bytes to an +application developer may be inconvenient for a casual application +developer who doesn't care about edge cases, they are extremely +convenient for the application developer who needs to deal with +complex, dirty eventualities, because use of bytes allows him the +appropriate level of control with a clear separation of +responsibility. + +If the protocol uses bytes, it is presumed that libraries will be +created to make working with bytes-only in the environ and within +return values more pleasant; for example, analogues of the WSGI 1.0 +libraries named "WebOb" and "Werkzeug". Such libraries will fill the +gap between convenience and control, allowing the spec to remain +simple and regular while still allowing casual authors a convenient +way to create Web3 middleware and application components. This seems +to be a reasonable alternative to baking encoding policy into the +protocol, because many such libraries can be created independently +from the protocol, and application developers can choose the one that +provides them the appropriate levels of control and convenience for a +particular job. + +Here are some alternatives to using all bytes: + +- Have the server decode all values representing CGI and server + environ values into strings using the ``latin-1`` encoding, which is + lossless. Smuggle any undecodable bytes within the resulting + string. + +- Encode all CGI and server environ values to strings using the + ``utf-8`` encoding with the ``surrogateescape`` error handler. This + does not work under any existing Python 2. + +- Encode some values into bytes and other values into strings, as + decided by their typical usages. + + +Applications Should be Allowed to Read ``web3.input`` Past ``CONTENT_LENGTH`` +----------------------------------------------------------------------------- + +At [6]_, Graham Dumpleton makes the assertion that ``wsgi.input`` +should be required to return the empty string as a signifier of +out-of-data, and that applications should be allowed to read past the +number of bytes specified in ``CONTENT_LENGTH``, depending only upon +the empty string as an EOF marker. WSGI relies on an application +"being well behaved and once all data specified by ``CONTENT_LENGTH`` +is read, that it processes the data and returns any response. That +same socket connection could then be used for a subsequent request." +Graham would like WSGI adapters to be required to wrap raw socket +connections: "this wrapper object will need to count how much data has +been read, and when the amount of data reaches that as defined by +``CONTENT_LENGTH``, any subsequent reads should return an empty string +instead." This may be useful to support chunked encoding and input +filters. + + +``web3.input`` Unknown Length +----------------------------- + +There's no documented way to indicate that there is content in +``environ['web3.input']``, but the content length is unknown. + + +``read()`` of ``web3.input`` Should Support No-Size Calling Convention +---------------------------------------------------------------------- + +At [6]_, Graham Dumpleton makes the assertion that the ``read()`` +method of ``wsgi.input`` should be callable without arguments, and +that the result should be "all available request content". Needs +discussion. + +Comment Armin: I changed the spec to require that from an +implementation. I had too much pain with that in the past already. +Open for discussions though. + + +Input Filters should set environ ``CONTENT_LENGTH`` to -1 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +At [6]_, Graham Dumpleton suggests that an input filter might set +``environ['CONTENT_LENGTH']`` to -1 to indicate that it mutated the +input. + + +``headers`` as Literal List of Two-Tuples +----------------------------------------- + +Why do we make applications return a ``headers`` structure that is a +literal list of two-tuples? I think the iterability of ``headers`` +needs to be maintained while it moves up the stack, but I don't think +we need to be able to mutate it in place at all times. Could we +loosen that requirement? + +Comment Armin: Strong yes + + +Removed Requirement that Middleware Not Block +--------------------------------------------- + +This requirement was removed: "middleware components **must not** +block iteration waiting for multiple values from an application +iterable. If the middleware needs to accumulate more data from the +application before it can produce any output, it **must** yield an +empty string." This requirement existed to support asynchronous +applications and servers (see PEP 333's "Middleware Handling of Block +Boundaries"). Asynchronous applications are now serviced explicitly +by ``web3.async`` capable protocol (a Web3 application callable may +itself return a callable). + + +``web3.script_name`` and ``web3.path_info`` +------------------------------------------- + +These values are required to be placed into the environment by an +origin server under this specification. Unlike ``SCRIPT_NAME`` and +``PATH_INFO``, these must be the original *URL-encoded* variants +derived from the request URI. We probably need to figure out how +these should be computed originally, and what their values should be +if the server performs URL rewriting. + + +Long Response Headers +--------------------- + +Bob Brewer notes on Web-SIG [7]_: + + Each header_value must not include any control characters, + including carriage returns or linefeeds, either embedded or at the + end. (These requirements are to minimize the complexity of any + parsing that must be performed by servers, gateways, and + intermediate response processors that need to inspect or modify + response headers.) [1]_ + +That's understandable, but HTTP headers are defined as (mostly) +\*TEXT, and "words of \*TEXT MAY contain characters from character +sets other than ISO-8859-1 only when encoded according to the rules of +RFC 2047." [2]_ And RFC 2047 specifies that "an 'encoded-word' may +not be more than 75 characters long... If it is desirable to encode +more text than will fit in an 'encoded-word' of 75 characters, +multiple 'encoded-word's (separated by CRLF SPACE) may be used." [3]_ +This satisfies HTTP header folding rules, as well: "Header fields can +be extended over multiple lines by preceding each extra line with at +least one SP or HT." [1]_ + +So in my reading of HTTP, some code somewhere should introduce +newlines in longish, encoded response header values. I see three +options: + +1. Keep things as they are and disallow response header values if they + contain words over 75 chars that are outside the ISO-8859-1 + character set. + +2. Allow newline characters in WSGI response headers. + +3. Require/strongly suggest WSGI servers to do the encoding and + folding before sending the value over HTTP. + + +Request Trailers and Chunked Transfer Encoding +---------------------------------------------- + +When using chunked transfer encoding on request content, the RFCs +allow there to be request trailers. These are like request headers +but come after the final null data chunk. These trailers are only +available when the chunked data stream is finite length and when it +has all been read in. Neither WSGI nor Web3 currently supports them. + +.. XXX (armin) yield from application iterator should be specify write + plus flush by server. + +.. XXX (armin) websocket API. + + +References +========== + +.. [1] PEP 333: Python Web Services Gateway Interface + (http://www.python.org/dev/peps/pep-0333/) + +.. [2] The Common Gateway Interface Specification, v 1.1, 3rd Draft + (http://cgi-spec.golux.com/draft-coar-cgi-v11-03.txt) + +.. [3] "Chunked Transfer Coding" -- HTTP/1.1, section 3.6.1 + (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1) + +.. [4] "End-to-end and Hop-by-hop Headers" -- HTTP/1.1, Section 13.5.1 + (http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1) + +.. [5] mod_ssl Reference, "Environment Variables" + (http://www.modssl.org/docs/2.8/ssl_reference.html#ToC25) + +.. [6] Details on WSGI 1.0 amendments/clarifications. + (http://blog.dscpl.com.au/2009/10/details-on-wsgi-10-amendmentsclarificat.html) + +.. [7] [Web-SIG] WSGI and long response header values + http://mail.python.org/pipermail/web-sig/2006-September/002244.html + +Copyright +========= + +This document has been placed in the public domain. + + + +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + coding: utf-8 + End: From brett at python.org Thu Sep 16 01:15:33 2010 From: brett at python.org (Brett Cannon) Date: Wed, 15 Sep 2010 16:15:33 -0700 Subject: [Python-checkins] r84842 - peps/trunk/pep-0444.txt In-Reply-To: <20100915224038.95AECC5D5@mail.python.org> References: <20100915224038.95AECC5D5@mail.python.org> Message-ID: Can I just ask why 444 since 392 was the last assigned Python 2 number? On Wed, Sep 15, 2010 at 15:40, georg.brandl wrote: > Author: georg.brandl > Date: Thu Sep 16 00:40:38 2010 > New Revision: 84842 > > Log: > Add PEP 444, Python Web3 Interface. > > Added: > ? peps/trunk/pep-0444.txt ? (contents, props changed) > > Added: peps/trunk/pep-0444.txt > ============================================================================== > --- (empty file) > +++ peps/trunk/pep-0444.txt ? ? Thu Sep 16 00:40:38 2010 > @@ -0,0 +1,1570 @@ > +PEP: 444 > +Title: Python Web3 Interface > +Version: $Revision$ > +Last-Modified: $Date$ > +Author: Chris McDonough , > + ? ? ? ?Armin Ronacher > +Discussions-To: Python Web-SIG > +Status: Draft > +Type: Informational > +Content-Type: text/x-rst > +Created: 19-Jul-2010 > + > + > +Abstract > +======== > + > +This document specifies a proposed second-generation standard > +interface between web servers and Python web applications or > +frameworks. > + > + > +Rationale and Goals > +=================== > + > +This protocol and specification is influenced heavily by the Web > +Services Gateway Interface (WSGI) 1.0 standard described in PEP 333 > +[1]_ . ?The high-level rationale for having any standard that allows > +Python-based web servers and applications to interoperate is outlined > +in PEP 333. ?This document essentially uses PEP 333 as a template, and > +changes its wording in various places for the purpose of forming a > +different standard. > + > +Python currently boasts a wide variety of web application frameworks > +which use the WSGI 1.0 protocol. ?However, due to changes in the > +language, the WSGI 1.0 protocol is not compatible with Python 3. ?This > +specification describes a standardized WSGI-like protocol that lets > +Python 2.6, 2.7 and 3.1+ applications communicate with web servers. > +Web3 is clearly a WSGI derivative; it only uses a different name than > +"WSGI" in order to indicate that it is not in any way backwards > +compatible. > + > +Applications and servers which are written to this specification are > +meant to work properly under Python 2.6.X, Python 2.7.X and Python > +3.1+. ?Neither an application nor a server that implements the Web3 > +specification can be easily written which will work under Python 2 > +versions earlier than 2.6 nor Python 3 versions earlier than 3.1. > + > +.. note:: > + > + ? Whatever Python 3 version fixed http://bugs.python.org/issue4006 so > + ? ``os.environ['foo']`` returns surrogates (ala PEP 383) when the > + ? value of 'foo' cannot be decoded using the current locale instead > + ? of failing with a KeyError is the *true* minimum Python 3 version. > + ? In particular, however, Python 3.0 is not supported. > + > +.. note:: > + > + ? Python 2.6 is the first Python version that supported an alias for > + ? ``bytes`` and the ``b"foo"`` literal syntax. ?This is why it is the > + ? minimum version supported by Web3. > + > +Explicability and documentability are the main technical drivers for > +the decisions made within the standard. > + > + > +Differences from WSGI > +===================== > + > +- All protocol-specific environment names are prefixed with ``web3.`` > + ?rather than ``wsgi.``, eg. ``web3.input`` rather than > + ?``wsgi.input``. > + > +- All values present as environment dictionary *values* are explicitly > + ?*bytes* instances instead of native strings. ?(Environment *keys* > + ?however are native strings, always ``str`` regardless of > + ?platform). > + > +- All values returned by an application must be bytes instances, > + ?including status code, header names and values, and the body. > + > +- Wherever WSGI 1.0 referred to an ``app_iter``, this specification > + ?refers to a ``body``. > + > +- No ``start_response()`` callback (and therefore no ``write()`` > + ?callable nor ``exc_info`` data). > + > +- The ``readline()`` function of ``web3.input`` must support a size > + ?hint parameter. > + > +- The ``read()`` function of ``web3.input`` must be length delimited. > + ?A call without a size argument must not read more than the content > + ?length header specifies. ?In case a content length header is absent > + ?the stream must not return anything on read. ?It must never request > + ?more data than specified from the client. > + > +- No requirement for middleware to yield an empty string if it needs > + ?more information from an application to produce output (e.g. no > + ?"Middleware Handling of Block Boundaries"). > + > +- Filelike objects passed to a "file_wrapper" must have an > + ?``__iter__`` which returns bytes (never text). > + > +- ``wsgi.file_wrapper`` is not supported. > + > +- ``QUERY_STRING``, ``SCRIPT_NAME``, ``PATH_INFO`` values required to > + ?be placed in environ by server (each as the empty bytes instance if > + ?no associated value is received in the HTTP request). > + > +- ``web3.path_info`` and ``web3.script_name`` should be put into the > + ?Web3 environment, if possible, by the origin Web3 server. ?When > + ?available, each is the original, plain 7-bit ASCII, URL-encoded > + ?variant of its CGI equivalent derived directly from the request URI > + ?(with %2F segment markers and other meta-characters intact). ?If the > + ?server cannot provide one (or both) of these values, it must omit > + ?the value(s) it cannot provide from the environment. > + > +- This requirement was removed: "middleware components **must not** > + ?block iteration waiting for multiple values from an application > + ?iterable. ?If the middleware needs to accumulate more data from the > + ?application before it can produce any output, it **must** yield an > + ?empty string." > + > +- ``SERVER_PORT`` must be a bytes instance (not an integer). > + > +- The server must not inject an additional ``Content-Length`` header > + ?by guessing the length from the response iterable. ?This must be set > + ?by the application itself in all situations. > + > +- If the origin server advertises that it has the ``web3.async`` > + ?capability, a Web3 application callable used by the server is > + ?permitted to return a callable that accepts no arguments. ?When it > + ?does so, this callable is to be called periodically by the origin > + ?server until it returns a non-``None`` response, which must be a > + ?normal Web3 response tuple. > + > + ?.. XXX (chrism) Needs a section of its own for explanation. > + > + > +Specification Overview > +====================== > + > +The Web3 interface has two sides: the "server" or "gateway" side, and > +the "application" or "framework" side. ?The server side invokes a > +callable object that is provided by the application side. ?The > +specifics of how that object is provided are up to the server or > +gateway. ?It is assumed that some servers or gateways will require an > +application's deployer to write a short script to create an instance > +of the server or gateway, and supply it with the application object. > +Other servers and gateways may use configuration files or other > +mechanisms to specify where an application object should be imported > +from, or otherwise obtained. > + > +In addition to "pure" servers/gateways and applications/frameworks, it > +is also possible to create "middleware" components that implement both > +sides of this specification. ?Such components act as an application to > +their containing server, and as a server to a contained application, > +and can be used to provide extended APIs, content transformation, > +navigation, and other useful functions. > + > +Throughout this specification, we will use the term "application > +callable" to mean "a function, a method, or an instance with a > +``__call__`` method". ?It is up to the server, gateway, or application > +implementing the application callable to choose the appropriate > +implementation technique for their needs. ?Conversely, a server, > +gateway, or application that is invoking a callable **must not** have > +any dependency on what kind of callable was provided to it. > +Application callables are only to be called, not introspected upon. > + > + > +The Application/Framework Side > +------------------------------ > + > +The application object is simply a callable object that accepts one > +argument. ?The term "object" should not be misconstrued as requiring > +an actual object instance: a function, method, or instance with a > +``__call__`` method are all acceptable for use as an application > +object. ?Application objects must be able to be invoked more than > +once, as virtually all servers/gateways (other than CGI) will make > +such repeated requests. ?It this cannot be guaranteed by the > +implementation of the actual application, it has to be wrapped in a > +function that creates a new instance on each call. > + > +.. note:: > + > + ? Although we refer to it as an "application" object, this should not > + ? be construed to mean that application developers will use Web3 as a > + ? web programming API. ?It is assumed that application developers > + ? will continue to use existing, high-level framework services to > + ? develop their applications. ?Web3 is a tool for framework and > + ? server developers, and is not intended to directly support > + ? application developers.) > + > +An example of an application which is a function (``simple_app``):: > + > + ? ?def simple_app(environ): > + ? ? ? ?"""Simplest possible application object""" > + ? ? ? ?status = b'200 OK' > + ? ? ? ?headers = [(b'Content-type', b'text/plain')] > + ? ? ? ?body = [b'Hello world!\n'] > + ? ? ? ?return body, status, headers > + > +An example of an application which is an instance (``simple_app``):: > + > + ? ?class AppClass(object): > + > + ? ? ? ?"""Produce the same output, but using an instance. ?An > + ? ? ? ?instance of this class must be instantiated before it is > + ? ? ? ?passed to the server. ?""" > + > + ? ? ?def __call__(self, environ): > + ? ? ? ? ? ?status = b'200 OK' > + ? ? ? ? ? ?headers = [(b'Content-type', b'text/plain')] > + ? ? ? ? ? ?body = [b'Hello world!\n'] > + ? ? ? ? ? ?return body, status, headers > + > + ? ?simple_app = AppClass() > + > +Alternately, an application callable may return a callable instead of > +the tuple if the server supports asynchronous execution. ?See > +information concerning ``web3.async`` for more information. > + > + > +The Server/Gateway Side > +----------------------- > + > +The server or gateway invokes the application callable once for each > +request it receives from an HTTP client, that is directed at the > +application. ?To illustrate, here is a simple CGI gateway, implemented > +as a function taking an application object. ?Note that this simple > +example has limited error handling, because by default an uncaught > +exception will be dumped to ``sys.stderr`` and logged by the web > +server. > + > +:: > + > + ? ?import locale > + ? ?import os > + ? ?import sys > + > + ? ?encoding = locale.getpreferredencoding() > + > + ? ?stdout = sys.stdout > + > + ? ?if hasattr(sys.stdout, 'buffer'): > + ? ? ? ?# Python 3 compatibility; we need to be able to push bytes out > + ? ? ? ?stdout = sys.stdout.buffer > + > + ? ?def get_environ(): > + ? ? ? ?d = {} > + ? ? ? ?for k, v in os.environ.items(): > + ? ? ? ? ? ?# Python 3 compatibility > + ? ? ? ? ? ?if not isinstance(v, bytes): > + ? ? ? ? ? ? ? ?# We must explicitly encode the string to bytes under > + ? ? ? ? ? ? ? ?# Python 3.1+ > + ? ? ? ? ? ? ? ?v = v.encode(encoding, 'surrogateescape') > + ? ? ? ? ? ?d[k] = v > + ? ? ? ?return d > + > + ? ?def run_with_cgi(application): > + > + ? ? ? ?environ = get_environ() > + ? ? ? ?environ['web3.input'] ? ? ? ?= sys.stdin > + ? ? ? ?environ['web3.errors'] ? ? ? = sys.stderr > + ? ? ? ?environ['web3.version'] ? ? ?= (1, 0) > + ? ? ? ?environ['web3.multithread'] ?= False > + ? ? ? ?environ['web3.multiprocess'] = True > + ? ? ? ?environ['web3.run_once'] ? ? = True > + ? ? ? ?environ['web3.async'] ? ? ? ?= False > + > + ? ? ? ?if environ.get('HTTPS', b'off') in (b'on', b'1'): > + ? ? ? ? ? ?environ['web3.url_scheme'] = b'https' > + ? ? ? ?else: > + ? ? ? ? ? ?environ['web3.url_scheme'] = b'http' > + > + ? ? ? ?rv = application(environ) > + ? ? ? ?if hasattr(rv, '__call__'): > + ? ? ? ? ? ?raise TypeError('This webserver does not support asynchronous ' > + ? ? ? ? ? ? ? ? ? ? ? ? ? ?'responses.') > + ? ? ? ?body, status, headers = rv > + > + ? ? ? ?CLRF = b'\r\n' > + > + ? ? ? ?try: > + ? ? ? ? ? ?stdout.write(b'Status: ' + status + CRLF) > + ? ? ? ? ? ?for header_name, header_val in headers: > + ? ? ? ? ? ? ? ?stdout.write(header_name + b': ' + header_val + CRLF) > + ? ? ? ? ? ?stdout.write(CRLF) > + ? ? ? ? ? ?for chunk in body: > + ? ? ? ? ? ? ? ?stdout.write(chunk) > + ? ? ? ? ? ? ? ?stdout.flush() > + ? ? ? ?finally: > + ? ? ? ? ? ?if hasattr(body, 'close'): > + ? ? ? ? ? ? ? ?body.close() > + > + > +Middleware: Components that Play Both Sides > +------------------------------------------- > + > +A single object may play the role of a server with respect to some > +application(s), while also acting as an application with respect to > +some server(s). ?Such "middleware" components can perform such > +functions as: > + > +* Routing a request to different application objects based on the > + ?target URL, after rewriting the ``environ`` accordingly. > + > +* Allowing multiple applications or frameworks to run side-by-side in > + ?the same process. > + > +* Load balancing and remote processing, by forwarding requests and > + ?responses over a network. > + > +* Perform content postprocessing, such as applying XSL stylesheets. > + > +The presence of middleware in general is transparent to both the > +"server/gateway" and the "application/framework" sides of the > +interface, and should require no special support. ?A user who desires > +to incorporate middleware into an application simply provides the > +middleware component to the server, as if it were an application, and > +configures the middleware component to invoke the application, as if > +the middleware component were a server. ?Of course, the "application" > +that the middleware wraps may in fact be another middleware component > +wrapping another application, and so on, creating what is referred to > +as a "middleware stack". > + > +A middleware must support asychronous execution if possible or fall > +back to disabling itself. > + > +Here a middleware that changes the ``HTTP_HOST`` key if an ``X-Host`` > +header exists and adds a comment to all html responses:: > + > + ? ?import time > + > + ? ?def apply_filter(app, environ, filter_func): > + ? ? ? ?"""Helper function that passes the return value from an > + ? ? ? ?application to a filter function when the results are > + ? ? ? ?ready. > + ? ? ? ?""" > + ? ? ? ?app_response = app(environ) > + > + ? ? ? ?# synchronous response, filter now > + ? ? ? ?if not hasattr(app_response, '__call__'): > + ? ? ? ? ? ?return filter_func(*app_response) > + > + ? ? ? ?# asychronous response. ?filter when results are ready > + ? ? ? ?def polling_function(): > + ? ? ? ? ? ?rv = app_response() > + ? ? ? ? ? ?if rv is not None: > + ? ? ? ? ? ? ? ?return filter_func(*rv) > + ? ? ? ?return polling_function > + > + ? ?def proxy_and_timing_support(app): > + ? ? ? ?def new_application(environ): > + ? ? ? ? ? ?def filter_func(body, status, headers): > + ? ? ? ? ? ? ? ?now = time.time() > + ? ? ? ? ? ? ? ?for key, value in headers: > + ? ? ? ? ? ? ? ? ? ?if key.lower() == b'content-type' and \ > + ? ? ? ? ? ? ? ? ? ? ? value.split(b';')[0] == b'text/html': > + ? ? ? ? ? ? ? ? ? ? ? ?# assumes ascii compatible encoding in body, > + ? ? ? ? ? ? ? ? ? ? ? ?# but the middleware should actually parse the > + ? ? ? ? ? ? ? ? ? ? ? ?# content type header and figure out the > + ? ? ? ? ? ? ? ? ? ? ? ?# encoding when doing that. > + ? ? ? ? ? ? ? ? ? ? ? ?body += ('' % > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (now - then)).encode('ascii') > + ? ? ? ? ? ? ? ? ? ? ? ?break > + ? ? ? ? ? ? ? ?return body, status, headers > + ? ? ? ? ? ?then = time.time() > + ? ? ? ? ? ?host = environ.get('HTTP_X_HOST') > + ? ? ? ? ? ?if host is not None: > + ? ? ? ? ? ? ? ?environ['HTTP_HOST'] = host > + > + ? ? ? ? ? ?# use the apply_filter function that applies a given filter > + ? ? ? ? ? ?# function for both async and sync responses. > + ? ? ? ? ? ?return apply_filter(app, environ, filter_func) > + ? ? ? ?return new_application > + > + ? ?app = proxy_and_timing_support(app) > + > + > +Specification Details > +===================== > + > +The application callable must accept one positional argument. ?For the > +sake of illustration, we have named it ``environ``, but it is not > +required to have this name. ?A server or gateway **must** invoke the > +application object using a positional (not keyword) argument. > +(E.g. by calling ``status, headers, body = application(environ)`` as > +shown above.) > + > +The ``environ`` parameter is a dictionary object, containing CGI-style > +environment variables. ?This object **must** be a builtin Python > +dictionary (*not* a subclass, ``UserDict`` or other dictionary > +emulation), and the application is allowed to modify the dictionary in > +any way it desires. ?The dictionary must also include certain > +Web3-required variables (described in a later section), and may also > +include server-specific extension variables, named according to a > +convention that will be described below. > + > +When called by the server, the application object must return a tuple > +yielding three elements: ``status``, ``headers`` and ``body``, or, if > +supported by an async server, an argumentless callable which either > +returns ``None`` or a tuple of those three elements. > + > +The ``status`` element is a status in bytes of the form ``b'999 > +Message here'``. > + > +``headers`` is a Python list of ``(header_name, header_value)`` pairs > +describing the HTTP response header. ?The ``headers`` structure must > +be a literal Python list; it must yield two-tuples. ?Both > +``header_name`` and ``header_value`` must be bytes values. > + > +The ``body`` is an iterable yielding zero or more bytes instances. > +This can be accomplished in a variety of ways, such as by returning a > +list containing bytes instances as ``body``, or by returning a > +generator function as ``body`` that yields bytes instances, or by the > +``body`` being an instance of a class which is iterable. ?Regardless > +of how it is accomplished, the application object must always return a > +``body`` iterable yielding zero or more bytes instances. > + > +The server or gateway must transmit the yielded bytes to the client in > +an unbuffered fashion, completing the transmission of each set of > +bytes before requesting another one. ?(In other words, applications > +**should** perform their own buffering. ?See the `Buffering and > +Streaming`_ section below for more on how application output must be > +handled.) > + > +The server or gateway should treat the yielded bytes as binary byte > +sequences: in particular, it should ensure that line endings are not > +altered. ?The application is responsible for ensuring that the > +string(s) to be written are in a format suitable for the client. ?(The > +server or gateway **may** apply HTTP transfer encodings, or perform > +other transformations for the purpose of implementing HTTP features > +such as byte-range transmission. ?See `Other HTTP Features`_, below, > +for more details.) > + > +If the ``body`` iterable returned by the application has a ``close()`` > +method, the server or gateway **must** call that method upon > +completion of the current request, whether the request was completed > +normally, or terminated early due to an error. ?This is to support > +resource release by the application amd is intended to complement PEP > +325's generator support, and other common iterables with ``close()`` > +methods. > + > +Finally, servers and gateways **must not** directly use any other > +attributes of the ``body`` iterable returned by the application. > + > + > +``environ`` Variables > +--------------------- > + > +The ``environ`` dictionary is required to contain various CGI > +environment variables, as defined by the Common Gateway Interface > +specification [2]_. > + > +The following CGI variables **must** be present. ?Each key is a native > +string. ?Each value is a bytes instance. > + > +.. note:: > + > + ? In Python 3.1+, a "native string" is a ``str`` type decoded using > + ? the ``surrogateescape`` error handler, as done by > + ? ``os.environ.__getitem__``. ?In Python 2.6 and 2.7, a "native > + ? string" is a ``str`` types representing a set of bytes. > + > +``REQUEST_METHOD`` > + ?The HTTP request method, such as ``"GET"`` or ``"POST"``. > + > +``SCRIPT_NAME`` > + ?The initial portion of the request URL's "path" that corresponds to > + ?the application object, so that the application knows its virtual > + ?"location". ?This may be the empty bytes instance if the application > + ?corresponds to the "root" of the server. ?SCRIPT_NAME will be a > + ?bytes instance representing a sequence of URL-encoded segments > + ?separated by the slash character (``/``). ?It is assumed that > + ?``%2F`` characters will be decoded into literal slash characters > + ?within ``PATH_INFO`` , as per CGI. > + > +``PATH_INFO`` > + ?The remainder of the request URL's "path", designating the virtual > + ?"location" of the request's target within the application. ?This > + ?**may** be a bytes instance if the request URL targets the > + ?application root and does not have a trailing slash. ?PATH_INFO will > + ?be a bytes instance representing a sequence of URL-encoded segments > + ?separated by the slash character (``/``). ?It is assumed that > + ?``%2F`` characters will be decoded into literal slash characters > + ?within ``PATH_INFO`` , as per CGI. > + > +``QUERY_STRING`` > + ?The portion of the request URL (in bytes) that follows the ``"?"``, > + ?if any, or the empty bytes instance. > + > +``SERVER_NAME``, ``SERVER_PORT`` > + ?When combined with ``SCRIPT_NAME`` and ``PATH_INFO`` (or their raw > + ?equivalents)`, these variables can be used to complete the URL. > + ?Note, however, that ``HTTP_HOST``, if present, should be used in > + ?preference to ``SERVER_NAME`` for reconstructing the request URL. > + ?See the `URL Reconstruction`_ section below for more detail. > + ?``SERVER_PORT`` should be a bytes instance, not an integer. > + > +``SERVER_PROTOCOL`` > + ?The version of the protocol the client used to send the request. > + ?Typically this will be something like ``"HTTP/1.0"`` or > + ?``"HTTP/1.1"`` and may be used by the application to determine how > + ?to treat any HTTP request headers. ?(This variable should probably > + ?be called ``REQUEST_PROTOCOL``, since it denotes the protocol used > + ?in the request, and is not necessarily the protocol that will be > + ?used in the server's response. ?However, for compatibility with CGI > + ?we have to keep the existing name.) > + > +The following CGI values **may** present be in the Web3 environment. > +Each key is a native string. ?Each value is a bytes instances. > + > +``CONTENT_TYPE`` > + ?The contents of any ``Content-Type`` fields in the HTTP request. > + > +``CONTENT_LENGTH`` > + ?The contents of any ``Content-Length`` fields in the HTTP request. > + > +``HTTP_`` Variables > + ?Variables corresponding to the client-supplied HTTP request headers > + ?(i.e., variables whose names begin with ``"HTTP_"``). ?The presence > + ?or absence of these variables should correspond with the presence or > + ?absence of the appropriate HTTP header in the request. > + > +A server or gateway **should** attempt to provide as many other CGI > +variables as are applicable, each with a string for its key and a > +bytes instance for its value. ?In addition, if SSL is in use, the > +server or gateway **should** also provide as many of the Apache SSL > +environment variables [5]_ as are applicable, such as ``HTTPS=on`` and > +``SSL_PROTOCOL``. ?Note, however, that an application that uses any > +CGI variables other than the ones listed above are necessarily > +non-portable to web servers that do not support the relevant > +extensions. ?(For example, web servers that do not publish files will > +not be able to provide a meaningful ``DOCUMENT_ROOT`` or > +``PATH_TRANSLATED``.) > + > +A Web3-compliant server or gateway **should** document what variables > +it provides, along with their definitions as appropriate. > +Applications **should** check for the presence of any variables they > +require, and have a fallback plan in the event such a variable is > +absent. > + > +Note that CGI variable *values* must be bytes instances, if they are > +present at all. ?It is a violation of this specification for a CGI > +variable's value to be of any type other than ``bytes``. ?On Python 2, > +this means they will be of type ``str``. ?On Python 3, this means they > +will be of type ``bytes``. > + > +They *keys* of all CGI and non-CGI variables in the environ, however, > +must be "native strings" (on both Python 2 and Python 3, they will be > +of type ``str``). > + > +In addition to the CGI-defined variables, the ``environ`` dictionary > +**may** also contain arbitrary operating-system "environment > +variables", and **must** contain the following Web3-defined variables. > + > +===================== ?=============================================== > +Variable ? ? ? ? ? ? ? Value > +===================== ?=============================================== > +``web3.version`` ? ? ? The tuple ``(1, 0)``, representing Web3 > + ? ? ? ? ? ? ? ? ? ? ? version 1.0. > + > +``web3.url_scheme`` ? ?A bytes value representing the "scheme" portion of > + ? ? ? ? ? ? ? ? ? ? ? the URL at which the application is being > + ? ? ? ? ? ? ? ? ? ? ? invoked. ?Normally, this will have the value > + ? ? ? ? ? ? ? ? ? ? ? ``b"http"`` or ``b"https"``, as appropriate. > + > +``web3.input`` ? ? ? ? An input stream (file-like object) from which bytes > + ? ? ? ? ? ? ? ? ? ? ? constituting the HTTP request body can be read. > + ? ? ? ? ? ? ? ? ? ? ? (The server or gateway may perform reads > + ? ? ? ? ? ? ? ? ? ? ? on-demand as requested by the application, or > + ? ? ? ? ? ? ? ? ? ? ? it may pre- read the client's request body and > + ? ? ? ? ? ? ? ? ? ? ? buffer it in-memory or on disk, or use any > + ? ? ? ? ? ? ? ? ? ? ? other technique for providing such an input > + ? ? ? ? ? ? ? ? ? ? ? stream, according to its preference.) > + > +``web3.errors`` ? ? ? ?An output stream (file-like object) to which error > + ? ? ? ? ? ? ? ? ? ? ? output text can be written, for the purpose of > + ? ? ? ? ? ? ? ? ? ? ? recording program or other errors in a > + ? ? ? ? ? ? ? ? ? ? ? standardized and possibly centralized location. > + ? ? ? ? ? ? ? ? ? ? ? This should be a "text mode" stream; i.e., > + ? ? ? ? ? ? ? ? ? ? ? applications should use ``"\n"`` as a line > + ? ? ? ? ? ? ? ? ? ? ? ending, and assume that it will be converted to > + ? ? ? ? ? ? ? ? ? ? ? the correct line ending by the server/gateway. > + ? ? ? ? ? ? ? ? ? ? ? Applications may *not* send bytes to the > + ? ? ? ? ? ? ? ? ? ? ? 'write' method of this stream; they may only > + ? ? ? ? ? ? ? ? ? ? ? send text. > + > + ? ? ? ? ? ? ? ? ? ? ? For many servers, ``web3.errors`` will be the > + ? ? ? ? ? ? ? ? ? ? ? server's main error log. Alternatively, this > + ? ? ? ? ? ? ? ? ? ? ? may be ``sys.stderr``, or a log file of some > + ? ? ? ? ? ? ? ? ? ? ? sort. ?The server's documentation should > + ? ? ? ? ? ? ? ? ? ? ? include an explanation of how to configure this > + ? ? ? ? ? ? ? ? ? ? ? or where to find the recorded output. ?A server > + ? ? ? ? ? ? ? ? ? ? ? or gateway may supply different error streams > + ? ? ? ? ? ? ? ? ? ? ? to different applications, if this is desired. > + > +``web3.multithread`` ? This value should evaluate true if the > + ? ? ? ? ? ? ? ? ? ? ? application object may be simultaneously > + ? ? ? ? ? ? ? ? ? ? ? invoked by another thread in the same process, > + ? ? ? ? ? ? ? ? ? ? ? and should evaluate false otherwise. > + > +``web3.multiprocess`` ?This value should evaluate true if an > + ? ? ? ? ? ? ? ? ? ? ? equivalent application object may be > + ? ? ? ? ? ? ? ? ? ? ? simultaneously invoked by another process, and > + ? ? ? ? ? ? ? ? ? ? ? should evaluate false otherwise. > + > +``web3.run_once`` ? ? ?This value should evaluate true if the server > + ? ? ? ? ? ? ? ? ? ? ? or gateway expects (but does not guarantee!) > + ? ? ? ? ? ? ? ? ? ? ? that the application will only be invoked this > + ? ? ? ? ? ? ? ? ? ? ? one time during the life of its containing > + ? ? ? ? ? ? ? ? ? ? ? process. ?Normally, this will only be true for > + ? ? ? ? ? ? ? ? ? ? ? a gateway based on CGI (or something similar). > + > +``web3.script_name`` ? The non-URL-decoded ``SCRIPT_NAME`` value. > + ? ? ? ? ? ? ? ? ? ? ? Through a historical inequity, by virtue of the > + ? ? ? ? ? ? ? ? ? ? ? CGI specification, ``SCRIPT_NAME`` is present > + ? ? ? ? ? ? ? ? ? ? ? within the environment as an already > + ? ? ? ? ? ? ? ? ? ? ? URL-decoded string. ?This is the original > + ? ? ? ? ? ? ? ? ? ? ? URL-encoded value derived from the request URI. > + ? ? ? ? ? ? ? ? ? ? ? If the server cannot provide this value, it > + ? ? ? ? ? ? ? ? ? ? ? must omit it from the environ. > + > +``web3.path_info`` ? ? The non-URL-decoded ``PATH_INFO`` value. > + ? ? ? ? ? ? ? ? ? ? ? Through a historical inequity, by virtue of the > + ? ? ? ? ? ? ? ? ? ? ? CGI specification, ``PATH_INFO`` is present > + ? ? ? ? ? ? ? ? ? ? ? within the environment as an already > + ? ? ? ? ? ? ? ? ? ? ? URL-decoded string. ?This is the original > + ? ? ? ? ? ? ? ? ? ? ? URL-encoded value derived from the request URI. > + ? ? ? ? ? ? ? ? ? ? ? If the server cannot provide this value, it > + ? ? ? ? ? ? ? ? ? ? ? must omit it from the environ. > + > +``web3.async`` ? ? ? ? This is ``True`` if the webserver supports > + ? ? ? ? ? ? ? ? ? ? ? async invocation. ?In that case an application > + ? ? ? ? ? ? ? ? ? ? ? is allowed to return a callable instead of a > + ? ? ? ? ? ? ? ? ? ? ? tuple with the response. ?The exact semantics > + ? ? ? ? ? ? ? ? ? ? ? are not specified by this specification. > + > +===================== ?=============================================== > + > +Finally, the ``environ`` dictionary may also contain server-defined > +variables. ?These variables should have names which are native > +strings, composed of only lower-case letters, numbers, dots, and > +underscores, and should be prefixed with a name that is unique to the > +defining server or gateway. ?For example, ``mod_web3`` might define > +variables with names like ``mod_web3.some_variable``. > + > + > +Input Stream > +~~~~~~~~~~~~ > + > +The input stream (``web3.input``) provided by the server must support > +the following methods: > + > +===================== ?======== > +Method ? ? ? ? ? ? ? ? Notes > +===================== ?======== > +``read(size)`` ? ? ? ? 1,4 > +``readline([size])`` ? 1,2,4 > +``readlines([size])`` ?1,3,4 > +``__iter__()`` ? ? ? ? 4 > +===================== ?======== > + > +The semantics of each method are as documented in the Python Library > +Reference, except for these notes as listed in the table above: > + > +1. The server is not required to read past the client's specified > + ? ``Content-Length``, and is allowed to simulate an end-of-file > + ? condition if the application attempts to read past that point. ?The > + ? application **should not** attempt to read more data than is > + ? specified by the ``CONTENT_LENGTH`` variable. > + > +2. The implementation must support the optional ``size`` argument to > + ? ``readline()``. > + > +3. The application is free to not supply a ``size`` argument to > + ? ``readlines()``, and the server or gateway is free to ignore the > + ? value of any supplied ``size`` argument. > + > +4. The ``read``, ``readline`` and ``__iter__`` methods must return a > + ? bytes instance. ?The ``readlines`` method must return a sequence > + ? which contains instances of bytes. > + > +The methods listed in the table above **must** be supported by all > +servers conforming to this specification. ?Applications conforming to > +this specification **must not** use any other methods or attributes of > +the ``input`` object. ?In particular, applications **must not** > +attempt to close this stream, even if it possesses a ``close()`` > +method. > + > +The input stream should silently ignore attempts to read more than the > +content length of the request. ?If no content length is specified the > +stream must be a dummy stream that does not return anything. > + > + > +Error Stream > +~~~~~~~~~~~~ > + > +The error stream (``web3.errors``) provided by the server must support > +the following methods: > + > +=================== ? ========== ?======== > +Method ? ? ? ? ? ? ? ?Stream ? ? ?Notes > +=================== ? ========== ?======== > +``flush()`` ? ? ? ? ? ``errors`` ?1 > +``write(str)`` ? ? ? ?``errors`` ?2 > +``writelines(seq)`` ? ``errors`` ?2 > +=================== ? ========== ?======== > + > +The semantics of each method are as documented in the Python Library > +Reference, except for these notes as listed in the table above: > + > +1. Since the ``errors`` stream may not be rewound, servers and > + ? gateways are free to forward write operations immediately, without > + ? buffering. ?In this case, the ``flush()`` method may be a no-op. > + ? Portable applications, however, cannot assume that output is > + ? unbuffered or that ``flush()`` is a no-op. ?They must call > + ? ``flush()`` if they need to ensure that output has in fact been > + ? written. ?(For example, to minimize intermingling of data from > + ? multiple processes writing to the same error log.) > + > +2. The ``write()`` method must accept a string argument, but needn't > + ? necessarily accept a bytes argument. ?The ``writelines()`` method > + ? must accept a sequence argument that consists entirely of strings, > + ? but needn't necessarily accept any bytes instance as a member of > + ? the sequence. > + > +The methods listed in the table above **must** be supported by all > +servers conforming to this specification. ?Applications conforming to > +this specification **must not** use any other methods or attributes of > +the ``errors`` object. ?In particular, applications **must not** > +attempt to close this stream, even if it possesses a ``close()`` > +method. > + > + > +Values Returned by A Web3 Application > +------------------------------------- > + > +Web3 applications return an iterable in the form (``status``, > +``headers``, ``body``). ?The return value can be any iterable type > +that returns exactly three values. ?If the server supports > +asynchronous applications (``web3.async``), the response may be a > +callable object (which accepts no arguments). > + > +The ``status`` value is assumed by a gateway or server to be an HTTP > +"status" bytes instance like ``b'200 OK'`` or ``b'404 Not Found'``. > +That is, it is a string consisting of a Status-Code and a > +Reason-Phrase, in that order and separated by a single space, with no > +surrounding whitespace or other characters. ?(See RFC 2616, Section > +6.1.1 for more information.) ?The string **must not** contain control > +characters, and must not be terminated with a carriage return, > +linefeed, or combination thereof. > + > +The ``headers`` value is assumed by a gateway or server to be a > +literal Python list of ``(header_name, header_value)`` tuples. ?Each > +``header_name`` must be a bytes instance representing a valid HTTP > +header field-name (as defined by RFC 2616, Section 4.2), without a > +trailing colon or other punctuation. ?Each ``header_value`` must be a > +bytes instance and **must not** include any control characters, > +including carriage returns or linefeeds, either embedded or at the > +end. ?(These requirements are to minimize the complexity of any > +parsing that must be performed by servers, gateways, and intermediate > +response processors that need to inspect or modify response headers.) > + > +In general, the server or gateway is responsible for ensuring that > +correct headers are sent to the client: if the application omits a > +header required by HTTP (or other relevant specifications that are in > +effect), the server or gateway **must** add it. ?For example, the HTTP > +``Date:`` and ``Server:`` headers would normally be supplied by the > +server or gateway. ?The gateway must however not override values with > +the same name if they are emitted by the application. > + > +(A reminder for server/gateway authors: HTTP header names are > +case-insensitive, so be sure to take that into consideration when > +examining application-supplied headers!) > + > +Applications and middleware are forbidden from using HTTP/1.1 > +"hop-by-hop" features or headers, any equivalent features in HTTP/1.0, > +or any headers that would affect the persistence of the client's > +connection to the web server. ?These features are the exclusive > +province of the actual web server, and a server or gateway **should** > +consider it a fatal error for an application to attempt sending them, > +and raise an error if they are supplied as return values from an > +application in the ``headers`` structure. ?(For more specifics on > +"hop-by-hop" features and headers, please see the `Other HTTP > +Features`_ section below.) > + > + > +Dealing with Compatibility Across Python Versions > +------------------------------------------------- > + > +Creating Web3 code that runs under both Python 2.6/2.7 and Python 3.1+ > +requires some care on the part of the developer. ?In general, the Web3 > +specification assumes a certain level of equivalence between the > +Python 2 ``str`` type and the Python 3 ``bytes`` type. ?For example, > +under Python 2, the values present in the Web3 ``environ`` will be > +instances of the ``str`` type; in Python 3, these will be instances of > +the ``bytes`` type. ?The Python 3 ``bytes`` type does not possess all > +the methods of the Python 2 ``str`` type, and some methods which it > +does possess behave differently than the Python 2 ``str`` type. > +Effectively, to ensure that Web3 middleware and applications work > +across Python versions, developers must do these things: > + > +#) Do not assume comparison equivalence between text values and bytes > + ? values. ?If you do so, your code may work under Python 2, but it > + ? will not work properly under Python 3. ?For example, don't write > + ? ``somebytes == 'abc'``. ?This will sometimes be true on Python 2 > + ? but it will never be true on Python 3, because a sequence of bytes > + ? never compares equal to a string under Python 3. ?Instead, always > + ? compare a bytes value with a bytes value, e.g. "somebytes == > + ? b'abc'". ?Code which does this is compatible with and works the > + ? same in Python 2.6, 2.7, and 3.1. ?The ``b`` in front of ``'abc'`` > + ? signals to Python 3 that the value is a literal bytes instance; > + ? under Python 2 it's a forward compatibility placebo. > + > +#) Don't use the ``__contains__`` method (directly or indirectly) of > + ? items that are meant to be byteslike without ensuring that its > + ? argument is also a bytes instance. ?If you do so, your code may > + ? work under Python 2, but it will not work properly under Python 3. > + ? For example, ``'abc' in somebytes'`` will raise a ``TypeError`` > + ? under Python 3, but it will return ``True`` under Python 2.6 and > + ? 2.7. ?However, ``b'abc' in somebytes`` will work the same on both > + ? versions. ?In Python 3.2, this restriction may be partially > + ? removed, as it's rumored that bytes types may obtain a ``__mod__`` > + ? implementation. > + > +#) ``__getitem__`` should not be used. > + > + ? .. XXX > + > +#) Dont try to use the ``format`` method or the ``__mod__`` method of > + ? instances of bytes (directly or indirectly). ?In Python 2, the > + ? ``str`` type which we treat equivalently to Python 3's ``bytes`` > + ? supports these method but actual Python 3's ``bytes`` instances > + ? don't support these methods. ?If you use these methods, your code > + ? will work under Python 2, but not under Python 3. > + > +#) Do not try to concatenate a bytes value with a string value. ?This > + ? may work under Python 2, but it will not work under Python 3. ?For > + ? example, doing ``'abc' + somebytes`` will work under Python 2, but > + ? it will result in a ``TypeError`` under Python 3. ?Instead, always > + ? make sure you're concatenating two items of the same type, > + ? e.g. ``b'abc' + somebytes``. > + > +Web3 expects byte values in other places, such as in all the values > +returned by an application. > + > +In short, to ensure compatibility of Web3 application code between > +Python 2 and Python 3, in Python 2, treat CGI and server variable > +values in the environment as if they had the Python 3 ``bytes`` API > +even though they actually have a more capable API. ?Likewise for all > +stringlike values returned by a Web3 application. > + > + > +Buffering and Streaming > +----------------------- > + > +Generally speaking, applications will achieve the best throughput by > +buffering their (modestly-sized) output and sending it all at once. > +This is a common approach in existing frameworks: the output is > +buffered in a StringIO or similar object, then transmitted all at > +once, along with the response headers. > + > +The corresponding approach in Web3 is for the application to simply > +return a single-element ``body`` iterable (such as a list) containing > +the response body as a single string. ?This is the recommended > +approach for the vast majority of application functions, that render > +HTML pages whose text easily fits in memory. > + > +For large files, however, or for specialized uses of HTTP streaming > +(such as multipart "server push"), an application may need to provide > +output in smaller blocks (e.g. to avoid loading a large file into > +memory). ?It's also sometimes the case that part of a response may be > +time-consuming to produce, but it would be useful to send ahead the > +portion of the response that precedes it. > + > +In these cases, applications will usually return a ``body`` iterator > +(often a generator-iterator) that produces the output in a > +block-by-block fashion. ?These blocks may be broken to coincide with > +mulitpart boundaries (for "server push"), or just before > +time-consuming tasks (such as reading another block of an on-disk > +file). > + > +Web3 servers, gateways, and middleware **must not** delay the > +transmission of any block; they **must** either fully transmit the > +block to the client, or guarantee that they will continue transmission > +even while the application is producing its next block. ?A > +server/gateway or middleware may provide this guarantee in one of > +three ways: > + > +1. Send the entire block to the operating system (and request that any > + ? O/S buffers be flushed) before returning control to the > + ? application, OR > + > +2. Use a different thread to ensure that the block continues to be > + ? transmitted while the application produces the next block. > + > +3. (Middleware only) send the entire block to its parent > + ? gateway/server. > + > +By providing this guarantee, Web3 allows applications to ensure that > +transmission will not become stalled at an arbitrary point in their > +output data. ?This is critical for proper functioning of > +e.g. multipart "server push" streaming, where data between multipart > +boundaries should be transmitted in full to the client. > + > + > +Unicode Issues > +-------------- > + > +HTTP does not directly support Unicode, and neither does this > +interface. ?All encoding/decoding must be handled by the > +**application**; all values passed to or from the server must be of > +the Python 3 type ``bytes`` or instances of the Python 2 type ``str``, > +not Python 2 ``unicode`` or Python 3 ``str`` objects. > + > +All "bytes instances" referred to in this specification **must**: > + > +- On Python 2, be of type ``str``. > + > +- On Python 3, be of type ``bytes``. > + > +All "bytes instances" **must not** : > + > +- On Python 2, ?be of type ``unicode``. > + > +- On Python 3, be of type ``str``. > + > +The result of using a textlike object where a byteslike object is > +required is undefined. > + > +Values returned from a Web3 app as a status or as response headers > +**must** follow RFC 2616 with respect to encoding. ?That is, the bytes > +returned must contain a character stream of ISO-8859-1 characters, or > +the character stream should use RFC 2047 MIME encoding. > + > +On Python platforms which do not have a native bytes-like type > +(e.g. IronPython, etc.), but instead which generally use textlike > +strings to represent bytes data, the definition of "bytes instance" > +can be changed: their "bytes instances" must be native strings that > +contain only code points representable in ISO-8859-1 encoding > +(``\u0000`` through ``\u00FF``, inclusive). ?It is a fatal error for > +an application on such a platform to supply strings containing any > +other Unicode character or code point. ?Similarly, servers and > +gateways on those platforms **must not** supply strings to an > +application containing any other Unicode characters. > + > +.. XXX (armin: Jython now has a bytes type, we might remove this > + ? section after seeing about IronPython) > + > + > +HTTP 1.1 Expect/Continue > +------------------------ > + > +Servers and gateways that implement HTTP 1.1 **must** provide > +transparent support for HTTP 1.1's "expect/continue" mechanism. ?This > +may be done in any of several ways: > + > +1. Respond to requests containing an ``Expect: 100-continue`` request > + ? with an immediate "100 Continue" response, and proceed normally. > + > +2. Proceed with the request normally, but provide the application with > + ? a ``web3.input`` stream that will send the "100 Continue" response > + ? if/when the application first attempts to read from the input > + ? stream. ?The read request must then remain blocked until the client > + ? responds. > + > +3. Wait until the client decides that the server does not support > + ? expect/continue, and sends the request body on its own. ?(This is > + ? suboptimal, and is not recommended.) > + > +Note that these behavior restrictions do not apply for HTTP 1.0 > +requests, or for requests that are not directed to an application > +object. ?For more information on HTTP 1.1 Expect/Continue, see RFC > +2616, sections 8.2.3 and 10.1.1. > + > + > +Other HTTP Features > +------------------- > + > +In general, servers and gateways should "play dumb" and allow the > +application complete control over its output. ?They should only make > +changes that do not alter the effective semantics of the application's > +response. ?It is always possible for the application developer to add > +middleware components to supply additional features, so server/gateway > +developers should be conservative in their implementation. ?In a > +sense, a server should consider itself to be like an HTTP "gateway > +server", with the application being an HTTP "origin server". ?(See RFC > +2616, section 1.3, for the definition of these terms.) > + > +However, because Web3 servers and applications do not communicate via > +HTTP, what RFC 2616 calls "hop-by-hop" headers do not apply to Web3 > +internal communications. ?Web3 applications **must not** generate any > +"hop-by-hop" headers [4]_, attempt to use HTTP features that would > +require them to generate such headers, or rely on the content of any > +incoming "hop-by-hop" headers in the ``environ`` dictionary. ?Web3 > +servers **must** handle any supported inbound "hop-by-hop" headers on > +their own, such as by decoding any inbound ``Transfer-Encoding``, > +including chunked encoding if applicable. > + > +Applying these principles to a variety of HTTP features, it should be > +clear that a server **may** handle cache validation via the > +``If-None-Match`` and ``If-Modified-Since`` request headers and the > +``Last-Modified`` and ``ETag`` response headers. ?However, it is not > +required to do this, and the application **should** perform its own > +cache validation if it wants to support that feature, since the > +server/gateway is not required to do such validation. > + > +Similarly, a server **may** re-encode or transport-encode an > +application's response, but the application **should** use a suitable > +content encoding on its own, and **must not** apply a transport > +encoding. ?A server **may** transmit byte ranges of the application's > +response if requested by the client, and the application doesn't > +natively support byte ranges. ?Again, however, the application > +**should** perform this function on its own if desired. > + > +Note that these restrictions on applications do not necessarily mean > +that every application must reimplement every HTTP feature; many HTTP > +features can be partially or fully implemented by middleware > +components, thus freeing both server and application authors from > +implementing the same features over and over again. > + > + > +Thread Support > +-------------- > + > +Thread support, or lack thereof, is also server-dependent. ?Servers > +that can run multiple requests in parallel, **should** also provide > +the option of running an application in a single-threaded fashion, so > +that applications or frameworks that are not thread-safe may still be > +used with that server. > + > + > +Implementation/Application Notes > +================================ > + > +Server Extension APIs > +--------------------- > + > +Some server authors may wish to expose more advanced APIs, that > +application or framework authors can use for specialized purposes. > +For example, a gateway based on ``mod_python`` might wish to expose > +part of the Apache API as a Web3 extension. > + > +In the simplest case, this requires nothing more than defining an > +``environ`` variable, such as ``mod_python.some_api``. ?But, in many > +cases, the possible presence of middleware can make this difficult. > +For example, an API that offers access to the same HTTP headers that > +are found in ``environ`` variables, might return different data if > +``environ`` has been modified by middleware. > + > +In general, any extension API that duplicates, supplants, or bypasses > +some portion of Web3 functionality runs the risk of being incompatible > +with middleware components. ?Server/gateway developers should *not* > +assume that nobody will use middleware, because some framework > +developers specifically organize their frameworks to function almost > +entirely as middleware of various kinds. > + > +So, to provide maximum compatibility, servers and gateways that > +provide extension APIs that replace some Web3 functionality, **must** > +design those APIs so that they are invoked using the portion of the > +API that they replace. ?For example, an extension API to access HTTP > +request headers must require the application to pass in its current > +``environ``, so that the server/gateway may verify that HTTP headers > +accessible via the API have not been altered by middleware. ?If the > +extension API cannot guarantee that it will always agree with > +``environ`` about the contents of HTTP headers, it must refuse service > +to the application, e.g. by raising an error, returning ``None`` > +instead of a header collection, or whatever is appropriate to the API. > + > +These guidelines also apply to middleware that adds information such > +as parsed cookies, form variables, sessions, and the like to > +``environ``. ?Specifically, such middleware should provide these > +features as functions which operate on ``environ``, rather than simply > +stuffing values into ``environ``. ?This helps ensure that information > +is calculated from ``environ`` *after* any middleware has done any URL > +rewrites or other ``environ`` modifications. > + > +It is very important that these "safe extension" rules be followed by > +both server/gateway and middleware developers, in order to avoid a > +future in which middleware developers are forced to delete any and all > +extension APIs from ``environ`` to ensure that their mediation isn't > +being bypassed by applications using those extensions! > + > + > +Application Configuration > +------------------------- > + > +This specification does not define how a server selects or obtains an > +application to invoke. ?These and other configuration options are > +highly server-specific matters. ?It is expected that server/gateway > +authors will document how to configure the server to execute a > +particular application object, and with what options (such as > +threading options). > + > +Framework authors, on the other hand, should document how to create an > +application object that wraps their framework's functionality. ?The > +user, who has chosen both the server and the application framework, > +must connect the two together. ?However, since both the framework and > +the server have a common interface, this should be merely a mechanical > +matter, rather than a significant engineering effort for each new > +server/framework pair. > + > +Finally, some applications, frameworks, and middleware may wish to use > +the ``environ`` dictionary to receive simple string configuration > +options. ?Servers and gateways **should** support this by allowing an > +application's deployer to specify name-value pairs to be placed in > +``environ``. ?In the simplest case, this support can consist merely of > +copying all operating system-supplied environment variables from > +``os.environ`` into the ``environ`` dictionary, since the deployer in > +principle can configure these externally to the server, or in the CGI > +case they may be able to be set via the server's configuration files. > + > +Applications **should** try to keep such required variables to a > +minimum, since not all servers will support easy configuration of > +them. ?Of course, even in the worst case, persons deploying an > +application can create a script to supply the necessary configuration > +values:: > + > + ? from the_app import application > + > + ? def new_app(environ): > + ? ? ? environ['the_app.configval1'] = b'something' > + ? ? ? return application(environ) > + > +But, most existing applications and frameworks will probably only need > +a single configuration value from ``environ``, to indicate the > +location of their application or framework-specific configuration > +file(s). ?(Of course, applications should cache such configuration, to > +avoid having to re-read it upon each invocation.) > + > + > +URL Reconstruction > +------------------ > + > +If an application wishes to reconstruct a request's complete URL (as a > +bytes object), it may do so using the following algorithm:: > + > + ? ?host = environ.get('HTTP_HOST') > + > + ? ?scheme = environ['web3.url_scheme'] > + ? ?port = environ['SERVER_PORT'] > + ? ?query = environ['QUERY_STRING'] > + > + ? ?url = scheme + b'://' > + > + ? ?if host: > + ? ? ? ?url += host > + ? ?else: > + ? ? ? ?url += environ['SERVER_NAME'] > + > + ? ? ? ?if scheme == b'https': > + ? ? ? ? ? ?if port != b'443': > + ? ? ? ? ? ? ? url += b':' + port > + ? ? ? ?else: > + ? ? ? ? ? ?if port != b'80': > + ? ? ? ? ? ? ? url += b':' + port > + > + ? ?if 'web3.script_name' in url: > + ? ? ? ?url += url_quote(environ['web3.script_name']) > + ? ?else: > + ? ? ? ?url += environ['SCRIPT_NAME'] > + ? ?if 'web3.path_info' in environ: > + ? ? ? ?url += url_quote(environ['web3.path_info']) > + ? ?else: > + ? ? ? ?url += environ['PATH_INFO'] > + ? ?if query: > + ? ? ? ?url += b'?' + query > + > +Note that such a reconstructed URL may not be precisely the same URI > +as requested by the client. ?Server rewrite rules, for example, may > +have modified the client's originally requested URL to place it in a > +canonical form. > + > + > +Open Questions > +============== > + > +- ``file_wrapper`` replacement. ?Currently nothing is specified here > + ?but it's clear that the old system of in-band signalling is broken > + ?if it does not provide a way to figure out as a middleware in the > + ?process if the response is a file wrapper. > + > + > +Points of Contention > +==================== > + > +Outlined below are potential points of contention regarding this > +specification. > + > + > +WSGI 1.0 Compatibility > +---------------------- > + > +Components written using the WSGI 1.0 specification will not > +transparently interoperate with components written using this > +specification. ?That's because the goals of this proposal and the > +goals of WSGI 1.0 are not directly aligned. > + > +WSGI 1.0 is obliged to provide specification-level backwards > +compatibility with versions of Python between 2.2 and 2.7. ?This > +specification, however, ditches Python 2.5 and lower compatibility in > +order to provide compatibility between relatively recent versions of > +Python 2 (2.6 and 2.7) as well as relatively recent versions of Python > +3 (3.1). > + > +It is currently impossible to write components which work reliably > +under both Python 2 and Python 3 using the WSGI 1.0 specification, > +because the specification implicitly posits that CGI and server > +variable values in the environ and values returned via > +``start_response`` represent a sequence of bytes that can be addressed > +using the Python 2 string API. ?It posits such a thing because that > +sort of data type was the sensible way to represent bytes in all > +Python 2 versions, and WSGI 1.0 was conceived before Python 3 existed. > + > +Python 3's ``str`` type supports the full API provided by the Python 2 > +``str`` type, but Python 3's ``str`` type does not represent a > +sequence of bytes, it instead represents text. ?Therefore, using it to > +represent environ values also requires that the environ byte sequence > +be decoded to text via some encoding. ?We cannot decode these bytes to > +text (at least in any way where the decoding has any meaning other > +than as a tunnelling mechanism) without widening the scope of WSGI to > +include server and gateway knowledge of decoding policies and > +mechanics. ?WSGI 1.0 never concerned itself with encoding and > +decoding. ?It made statements about allowable transport values, and > +suggested that various values might be best decoded as one encoding or > +another, but it never required a server to *perform* any decoding > +before > + > +Python 3 does not have a stringlike type that can be used instead to > +represent bytes: it has a ``bytes`` type. ?A bytes type operates quite > +a bit like a Python 2 ``str`` in Python 3.1+, but it lacks behavior > +equivalent to ``str.__mod__`` and its iteration protocol, and > +containment, sequence treatment, and equivalence comparisons are > +different. > + > +In either case, there is no type in Python 3 that behaves just like > +the Python 2 ``str`` type, and a way to create such a type doesn't > +exist because there is no such thing as a "String ABC" which would > +allow a suitable type to be built. ?Due to this design > +incompatibility, existing WSGI 1.0 servers, middleware, and > +applications will not work under Python 3, even after they are run > +through ``2to3``. > + > +Existing Web-SIG discussions about updating the WSGI specification so > +that it is possible to write a WSGI application that runs in both > +Python 2 and Python 3 tend to revolve around creating a > +specification-level equivalence between the Python 2 ``str`` type > +(which represents a sequence of bytes) and the Python 3 ``str`` type > +(which represents text). ?Such an equivalence becomes strained in > +various areas, given the different roles of these types. ?An arguably > +more straightforward equivalence exists between the Python 3 ``bytes`` > +type API and a subset of the Python 2 ``str`` type API. ?This > +specification exploits this subset equivalence. > + > +In the meantime, aside from any Python 2 vs. Python 3 compatibility > +issue, as various discussions on Web-SIG have pointed out, the WSGI > +1.0 specification is too general, providing support (via ``.write``) > +for asynchronous applications at the expense of implementation > +complexity. ?This specification uses the fundamental incompatibility > +between WSGI 1.0 and Python 3 as a natural divergence point to create > +a specification with reduced complexity by changing specialized > +support for asynchronous applications. > + > +To provide backwards compatibility for older WSGI 1.0 applications, so > +that they may run on a Web3 stack, it is presumed that Web3 middleware > +will be created which can be used "in front" of existing WSGI 1.0 > +applications, allowing those existing WSGI 1.0 applications to run > +under a Web3 stack. ?This middleware will require, when under Python > +3, an equivalence to be drawn between Python 3 ``str`` types and the > +bytes values represented by the HTTP request and all the attendant > +encoding-guessing (or configuration) it implies. > + > +.. note:: > + > + ? Such middleware *might* in the future, instead of drawing an > + ? equivalence between Python 3 ``str`` and HTTP byte values, make use > + ? of a yet-to-be-created "ebytes" type (aka "bytes-with-benefits"), > + ? particularly if a String ABC proposal is accepted into the Python > + ? core and implemented. > + > +Conversely, it is presumed that WSGI 1.0 middleware will be created > +which will allow a Web3 application to run behind a WSGI 1.0 stack on > +the Python 2 platform. > + > + > +Environ and Response Values as Bytes > +------------------------------------ > + > +Casual middleware and application writers may consider the use of > +bytes as environment values and response values inconvenient. ?In > +particular, they won't be able to use common string formatting > +functions such as ``('%s' % bytes_val)`` or > +``bytes_val.format('123')`` because bytes don't have the same API as > +strings on platforms such as Python 3 where the two types differ. > +Likewise, on such platforms, stdlib HTTP-related API support for using > +bytes interchangeably with text can be spotty. ?In places where bytes > +are inconvenient or incompatible with library APIs, middleware and > +application writers will have to decode such bytes to text explicitly. > +This is particularly inconvenient for middleware writers: to work with > +environment values as strings, they'll have to decode them from an > +implied encoding and if they need to mutate an environ value, they'll > +then need to encode the value into a byte stream before placing it > +into the environ. ?While the use of bytes by the specification as > +environ values might be inconvenient for casual developers, it > +provides several benefits. > + > +Using bytes types to represent HTTP and server values to an > +application most closely matches reality because HTTP is fundamentally > +a bytes-oriented protocol. ?If the environ values are mandated to be > +strings, each server will need to use heuristics to guess about the > +encoding of various values provided by the HTTP environment. ?Using > +all strings might increase casual middleware writer convenience, but > +will also lead to ambiguity and confusion when a value cannot be > +decoded to a meaningful non-surrogate string. > + > +Use of bytes as environ values avoids any potential for the need for > +the specification to mandate that a participating server be informed > +of encoding configuration parameters. ?If environ values are treated > +as strings, and so must be decoded from bytes, configuration > +parameters may eventually become necessary as policy clues from the > +application deployer. ?Such a policy would be used to guess an > +appropriate decoding strategy in various circumstances, effectively > +placing the burden for enforcing a particular application encoding > +policy upon the server. ?If the server must serve more than one > +application, such configuration would quickly become complex. ?Many > +policies would also be impossible to express declaratively. > + > +In reality, HTTP is a complicated and legacy-fraught protocol which > +requires a complex set of heuristics to make sense of. It would be > +nice if we could allow this protocol to protect us from this > +complexity, but we cannot do so reliably while still providing to > +application writers a level of control commensurate with reality. > +Python applications must often deal with data embedded in the > +environment which not only must be parsed by legacy heuristics, but > +*does not conform even to any existing HTTP specification*. ?While > +these eventualities are unpleasant, they crop up with regularity, > +making it impossible and undesirable to hide them from application > +developers, as application developers are the only people who are able > +to decide upon an appropriate action when an HTTP specification > +violation is detected. > + > +Some have argued for mixed use of bytes and string values as environ > +*values*. ?This proposal avoids that strategy. ?Sole use of bytes as > +environ values makes it possible to fit this specification entirely in > +one's head; you won't need to guess about which values are strings and > +which are bytes. > + > +This protocol would also fit in a developer's head if all environ > +values were strings, but this specification doesn't use that strategy. > +This will likely be the point of greatest contention regarding the use > +of bytes. ?In defense of bytes: developers often prefer protocols with > +consistent contracts, even if the contracts themselves are suboptimal. > +If we hide encoding issues from a developer until a value that > +contains surrogates causes problems after it has already reached > +beyond the I/O boundary of their application, they will need to do a > +lot more work to fix assumptions made by their application than if we > +were to just present the problem much earlier in terms of "here's some > +bytes, you decode them". ?This is also a counter-argument to the > +"bytes are inconvenient" assumption: while presenting bytes to an > +application developer may be inconvenient for a casual application > +developer who doesn't care about edge cases, they are extremely > +convenient for the application developer who needs to deal with > +complex, dirty eventualities, because use of bytes allows him the > +appropriate level of control with a clear separation of > +responsibility. > + > +If the protocol uses bytes, it is presumed that libraries will be > +created to make working with bytes-only in the environ and within > +return values more pleasant; for example, analogues of the WSGI 1.0 > +libraries named "WebOb" and "Werkzeug". ?Such libraries will fill the > +gap between convenience and control, allowing the spec to remain > +simple and regular while still allowing casual authors a convenient > +way to create Web3 middleware and application components. ?This seems > +to be a reasonable alternative to baking encoding policy into the > +protocol, because many such libraries can be created independently > +from the protocol, and application developers can choose the one that > +provides them the appropriate levels of control and convenience for a > +particular job. > + > +Here are some alternatives to using all bytes: > + > +- Have the server decode all values representing CGI and server > + ?environ values into strings using the ``latin-1`` encoding, which is > + ?lossless. ?Smuggle any undecodable bytes within the resulting > + ?string. > + > +- Encode all CGI and server environ values to strings using the > + ?``utf-8`` encoding with the ``surrogateescape`` error handler. ?This > + ?does not work under any existing Python 2. > + > +- Encode some values into bytes and other values into strings, as > + ?decided by their typical usages. > + > + > +Applications Should be Allowed to Read ``web3.input`` Past ``CONTENT_LENGTH`` > +----------------------------------------------------------------------------- > + > +At [6]_, Graham Dumpleton makes the assertion that ``wsgi.input`` > +should be required to return the empty string as a signifier of > +out-of-data, and that applications should be allowed to read past the > +number of bytes specified in ``CONTENT_LENGTH``, depending only upon > +the empty string as an EOF marker. ?WSGI relies on an application > +"being well behaved and once all data specified by ``CONTENT_LENGTH`` > +is read, that it processes the data and returns any response. That > +same socket connection could then be used for a subsequent request." > +Graham would like WSGI adapters to be required to wrap raw socket > +connections: "this wrapper object will need to count how much data has > +been read, and when the amount of data reaches that as defined by > +``CONTENT_LENGTH``, any subsequent reads should return an empty string > +instead." ?This may be useful to support chunked encoding and input > +filters. > + > + > +``web3.input`` Unknown Length > +----------------------------- > + > +There's no documented way to indicate that there is content in > +``environ['web3.input']``, but the content length is unknown. > + > + > +``read()`` of ``web3.input`` Should Support No-Size Calling Convention > +---------------------------------------------------------------------- > + > +At [6]_, Graham Dumpleton makes the assertion that the ``read()`` > +method of ``wsgi.input`` should be callable without arguments, and > +that the result should be "all available request content". ?Needs > +discussion. > + > +Comment Armin: I changed the spec to require that from an > +implementation. ?I had too much pain with that in the past already. > +Open for discussions though. > + > + > +Input Filters should set environ ``CONTENT_LENGTH`` to -1 > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + > +At [6]_, Graham Dumpleton suggests that an input filter might set > +``environ['CONTENT_LENGTH']`` to -1 to indicate that it mutated the > +input. > + > + > +``headers`` as Literal List of Two-Tuples > +----------------------------------------- > + > +Why do we make applications return a ``headers`` structure that is a > +literal list of two-tuples? ?I think the iterability of ``headers`` > +needs to be maintained while it moves up the stack, but I don't think > +we need to be able to mutate it in place at all times. ?Could we > +loosen that requirement? > + > +Comment Armin: Strong yes > + > + > +Removed Requirement that Middleware Not Block > +--------------------------------------------- > + > +This requirement was removed: "middleware components **must not** > +block iteration waiting for multiple values from an application > +iterable. ?If the middleware needs to accumulate more data from the > +application before it can produce any output, it **must** yield an > +empty string." ?This requirement existed to support asynchronous > +applications and servers (see PEP 333's "Middleware Handling of Block > +Boundaries"). ?Asynchronous applications are now serviced explicitly > +by ``web3.async`` capable protocol (a Web3 application callable may > +itself return a callable). > + > + > +``web3.script_name`` and ``web3.path_info`` > +------------------------------------------- > + > +These values are required to be placed into the environment by an > +origin server under this specification. ?Unlike ``SCRIPT_NAME`` and > +``PATH_INFO``, these must be the original *URL-encoded* variants > +derived from the request URI. ?We probably need to figure out how > +these should be computed originally, and what their values should be > +if the server performs URL rewriting. > + > + > +Long Response Headers > +--------------------- > + > +Bob Brewer notes on Web-SIG [7]_: > + > + ? ?Each header_value must not include any control characters, > + ? ?including carriage returns or linefeeds, either embedded or at the > + ? ?end. ?(These requirements are to minimize the complexity of any > + ? ?parsing that must be performed by servers, gateways, and > + ? ?intermediate response processors that need to inspect or modify > + ? ?response headers.) [1]_ > + > +That's understandable, but HTTP headers are defined as (mostly) > +\*TEXT, and "words of \*TEXT MAY contain characters from character > +sets other than ISO-8859-1 only when encoded according to the rules of > +RFC 2047." ?[2]_ And RFC 2047 specifies that "an 'encoded-word' may > +not be more than 75 characters long... ?If it is desirable to encode > +more text than will fit in an 'encoded-word' of 75 characters, > +multiple 'encoded-word's (separated by CRLF SPACE) may be used." [3]_ > +This satisfies HTTP header folding rules, as well: "Header fields can > +be extended over multiple lines by preceding each extra line with at > +least one SP or HT." [1]_ > + > +So in my reading of HTTP, some code somewhere should introduce > +newlines in longish, encoded response header values. ?I see three > +options: > + > +1. Keep things as they are and disallow response header values if they > + ? contain words over 75 chars that are outside the ISO-8859-1 > + ? character set. > + > +2. Allow newline characters in WSGI response headers. > + > +3. Require/strongly suggest WSGI servers to do the encoding and > + ? folding before sending the value over HTTP. > + > + > +Request Trailers and Chunked Transfer Encoding > +---------------------------------------------- > + > +When using chunked transfer encoding on request content, the RFCs > +allow there to be request trailers. ?These are like request headers > +but come after the final null data chunk. ?These trailers are only > +available when the chunked data stream is finite length and when it > +has all been read in. ?Neither WSGI nor Web3 currently supports them. > + > +.. XXX (armin) yield from application iterator should be specify write > + ? plus flush by server. > + > +.. XXX (armin) websocket API. > + > + > +References > +========== > + > +.. [1] PEP 333: Python Web Services Gateway Interface > + ? (http://www.python.org/dev/peps/pep-0333/) > + > +.. [2] The Common Gateway Interface Specification, v 1.1, 3rd Draft > + ? (http://cgi-spec.golux.com/draft-coar-cgi-v11-03.txt) > + > +.. [3] "Chunked Transfer Coding" -- HTTP/1.1, section 3.6.1 > + ? (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1) > + > +.. [4] "End-to-end and Hop-by-hop Headers" -- HTTP/1.1, Section 13.5.1 > + ? (http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1) > + > +.. [5] mod_ssl Reference, "Environment Variables" > + ? (http://www.modssl.org/docs/2.8/ssl_reference.html#ToC25) > + > +.. [6] Details on WSGI 1.0 amendments/clarifications. > + ? (http://blog.dscpl.com.au/2009/10/details-on-wsgi-10-amendmentsclarificat.html) > + > +.. [7] [Web-SIG] WSGI and long response header values > + ? http://mail.python.org/pipermail/web-sig/2006-September/002244.html > + > +Copyright > +========= > + > +This document has been placed in the public domain. > + > + > + > +.. > + ? Local Variables: > + ? mode: indented-text > + ? indent-tabs-mode: nil > + ? sentence-end-double-space: t > + ? fill-column: 70 > + ? coding: utf-8 > + ? End: > _______________________________________________ > Python-checkins mailing list > Python-checkins at python.org > http://mail.python.org/mailman/listinfo/python-checkins > From benjamin at python.org Thu Sep 16 01:17:21 2010 From: benjamin at python.org (Benjamin Peterson) Date: Wed, 15 Sep 2010 18:17:21 -0500 Subject: [Python-checkins] r84842 - peps/trunk/pep-0444.txt In-Reply-To: References: <20100915224038.95AECC5D5@mail.python.org> Message-ID: 2010/9/15 Brett Cannon : > Can I just ask why 444 since 392 was the last assigned Python 2 number? Likely because WSGI 1.0 is PEP 333. -- Regards, Benjamin From python-checkins at python.org Thu Sep 16 02:31:21 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 16 Sep 2010 02:31:21 +0200 (CEST) Subject: [Python-checkins] r84843 - python/branches/py3k/Lib/functools.py Message-ID: <20100916003121.32500EE996@mail.python.org> Author: raymond.hettinger Date: Thu Sep 16 02:31:21 2010 New Revision: 84843 Log: Improve comment Modified: python/branches/py3k/Lib/functools.py Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Thu Sep 16 02:31:21 2010 @@ -81,7 +81,7 @@ ('__gt__', lambda self, other: not other >= self), ('__lt__', lambda self, other: not self >= other)] } - # Find comparisons not inherited from object. + # Find user-defined comparisons (not those inherited from object). roots = [op for op in convert if getattr(cls, op, None) is not getattr(object, op, None)] if not roots: raise ValueError('must define at least one ordering operation: < > <= >=') From solipsis at pitrou.net Thu Sep 16 05:10:18 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 16 Sep 2010 05:10:18 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84843): sum=0 Message-ID: py3k results for svn r84843 (hg cset f3996a54cf59) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflog0iNZS6', '-x'] From g.brandl at gmx.net Thu Sep 16 09:20:04 2010 From: g.brandl at gmx.net (Georg Brandl) Date: Thu, 16 Sep 2010 09:20:04 +0200 Subject: [Python-checkins] r84842 - peps/trunk/pep-0444.txt In-Reply-To: References: <20100915224038.95AECC5D5@mail.python.org> Message-ID: Am 16.09.2010 01:17, schrieb Benjamin Peterson: > 2010/9/15 Brett Cannon : >> Can I just ask why 444 since 392 was the last assigned Python 2 number? > > Likely because WSGI 1.0 is PEP 333. Exactly :) Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out. From python-checkins at python.org Thu Sep 16 10:06:05 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 16 Sep 2010 10:06:05 +0200 (CEST) Subject: [Python-checkins] r84844 - python/branches/py3k/Lib/collections.py Message-ID: <20100916080605.AA415E0C8@mail.python.org> Author: raymond.hettinger Date: Thu Sep 16 10:06:05 2010 New Revision: 84844 Log: Remove unneeded exception chaining. Modified: python/branches/py3k/Lib/collections.py Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Thu Sep 16 10:06:05 2010 @@ -306,7 +306,7 @@ try: exec(template, namespace) except SyntaxError as e: - raise SyntaxError(e.msg + ':\n' + template) from e + raise SyntaxError(e.msg + ':\n\n' + template) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to the frame From python-checkins at python.org Thu Sep 16 12:50:07 2010 From: python-checkins at python.org (armin.ronacher) Date: Thu, 16 Sep 2010 12:50:07 +0200 (CEST) Subject: [Python-checkins] r84845 - peps/trunk/pep-0444.txt Message-ID: <20100916105007.D8387FAEF@mail.python.org> Author: armin.ronacher Date: Thu Sep 16 12:50:07 2010 New Revision: 84845 Log: Fixed a case where the order of arguments was wrong for PEP 444 Modified: peps/trunk/pep-0444.txt Modified: peps/trunk/pep-0444.txt ============================================================================== --- peps/trunk/pep-0444.txt (original) +++ peps/trunk/pep-0444.txt Thu Sep 16 12:50:07 2010 @@ -384,7 +384,7 @@ sake of illustration, we have named it ``environ``, but it is not required to have this name. A server or gateway **must** invoke the application object using a positional (not keyword) argument. -(E.g. by calling ``status, headers, body = application(environ)`` as +(E.g. by calling ``body, status, headers = application(environ)`` as shown above.) The ``environ`` parameter is a dictionary object, containing CGI-style From nnorwitz at gmail.com Thu Sep 16 13:32:50 2010 From: nnorwitz at gmail.com (Neal Norwitz) Date: Thu, 16 Sep 2010 07:32:50 -0400 Subject: [Python-checkins] Python Regression Test Failures refleak (1) Message-ID: <20100916113250.GA19054@kbk-i386-bb.dyndns.org> More important issues: ---------------------- test_bz2 leaked [-77, 0, 0] references, sum=-77 Less important issues: ---------------------- From python-checkins at python.org Thu Sep 16 13:35:07 2010 From: python-checkins at python.org (ronald.oussoren) Date: Thu, 16 Sep 2010 13:35:07 +0200 (CEST) Subject: [Python-checkins] r84846 - in python/branches/release27-maint: Modules/_ctypes/callbacks.c Modules/_ctypes/libffi_osx/include/ffi.h Modules/_ctypes/malloc_closure.c setup.py Message-ID: <20100916113507.3817DEE986@mail.python.org> Author: ronald.oussoren Date: Thu Sep 16 13:35:07 2010 New Revision: 84846 Log: Merged revisions 84535 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84535 | ronald.oussoren | 2010-09-05 20:25:59 +0200 (Sun, 05 Sep 2010) | 2 lines Fix for issue9662, patch by ?ukasz Langa in issue5504. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Modules/_ctypes/callbacks.c python/branches/release27-maint/Modules/_ctypes/libffi_osx/include/ffi.h python/branches/release27-maint/Modules/_ctypes/malloc_closure.c python/branches/release27-maint/setup.py Modified: python/branches/release27-maint/Modules/_ctypes/callbacks.c ============================================================================== --- python/branches/release27-maint/Modules/_ctypes/callbacks.c (original) +++ python/branches/release27-maint/Modules/_ctypes/callbacks.c Thu Sep 16 13:35:07 2010 @@ -469,9 +469,13 @@ "ffi_prep_cif failed with %d", result); goto error; } +#if defined(X86_DARWIN) || defined(POWERPC_DARWIN) + result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p); +#else result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn, p, p->pcl_exec); +#endif if (result != FFI_OK) { PyErr_Format(PyExc_RuntimeError, "ffi_prep_closure failed with %d", result); Modified: python/branches/release27-maint/Modules/_ctypes/libffi_osx/include/ffi.h ============================================================================== --- python/branches/release27-maint/Modules/_ctypes/libffi_osx/include/ffi.h (original) +++ python/branches/release27-maint/Modules/_ctypes/libffi_osx/include/ffi.h Thu Sep 16 13:35:07 2010 @@ -264,6 +264,9 @@ void (*fun)(ffi_cif*,void*,void**,void*), void* user_data); +void ffi_closure_free(void *); +void *ffi_closure_alloc (size_t size, void **code); + typedef struct ffi_raw_closure { char tramp[FFI_TRAMPOLINE_SIZE]; ffi_cif* cif; @@ -349,4 +352,4 @@ } #endif -#endif // #ifndef LIBFFI_H \ No newline at end of file +#endif // #ifndef LIBFFI_H Modified: python/branches/release27-maint/Modules/_ctypes/malloc_closure.c ============================================================================== --- python/branches/release27-maint/Modules/_ctypes/malloc_closure.c (original) +++ python/branches/release27-maint/Modules/_ctypes/malloc_closure.c Thu Sep 16 13:35:07 2010 @@ -110,7 +110,6 @@ return NULL; item = free_list; free_list = item->next; - *codeloc = (void *)item; + *codeloc = (void *)item; return (void *)item; } - Modified: python/branches/release27-maint/setup.py ============================================================================== --- python/branches/release27-maint/setup.py (original) +++ python/branches/release27-maint/setup.py Thu Sep 16 13:35:07 2010 @@ -1875,6 +1875,7 @@ depends = ['_ctypes/ctypes.h'] if sys.platform == 'darwin': + sources.append('_ctypes/malloc_closure.c') sources.append('_ctypes/darwin/dlfcn_simple.c') extra_compile_args.append('-DMACOSX') include_dirs.append('_ctypes/darwin') From python-checkins at python.org Thu Sep 16 14:02:17 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 16 Sep 2010 14:02:17 +0200 (CEST) Subject: [Python-checkins] r84847 - python/branches/py3k/Doc/library/re.rst Message-ID: <20100916120217.8C93FFD78@mail.python.org> Author: raymond.hettinger Date: Thu Sep 16 14:02:17 2010 New Revision: 84847 Log: Add tokenizer example to regex docs. Modified: python/branches/py3k/Doc/library/re.rst Modified: python/branches/py3k/Doc/library/re.rst ============================================================================== --- python/branches/py3k/Doc/library/re.rst (original) +++ python/branches/py3k/Doc/library/re.rst Thu Sep 16 14:02:17 2010 @@ -1282,3 +1282,66 @@ <_sre.SRE_Match object at ...> >>> re.match("\\\\", r"\\") <_sre.SRE_Match object at ...> + + +Writing a Tokenizer +^^^^^^^^^^^^^^^^^^^ + +A `tokenizer or scanner `_ +analyzes a string to categorize groups of characters. This is a useful first +step in writing a compiler or interpreter. + +The text categories are specified with regular expressions. The technique is +to combine those into a single master regular expression and to loop over +successive matches:: + + Token = collections.namedtuple('Token', 'typ value line column') + + def tokenize(s): + tok_spec = [ + ('NUMBER', r'\d+(.\d+)?'), # Integer or decimal number + ('ASSIGN', r':='), # Assignment operator + ('END', ';'), # Statement terminator + ('ID', r'[A-Za-z]+'), # Identifiers + ('OP', r'[+*\/\-]'), # Arithmetic operators + ('NEWLINE', r'\n'), # Line endings + ('SKIP', r'[ \t]'), # Skip over spaces and tabs + ] + tok_re = '|'.join('(?P<%s>%s)' % pair for pair in tok_spec) + gettok = re.compile(tok_re).match + line = 1 + pos = line_start = 0 + mo = gettok(s) + while mo is not None: + typ = mo.lastgroup + if typ == 'NEWLINE': + line_start = pos + line += 1 + elif typ != 'SKIP': + yield Token(typ, mo.group(typ), line, mo.start()-line_start) + pos = mo.end() + mo = gettok(s, pos) + if pos != len(s): + raise RuntimeError('Unexpected character %r on line %d' %(s[pos], line)) + + >>> statements = '''\ + total := total + price * quantity; + tax := price * 0.05; + ''' + >>> for token in tokenize(statements): + ... print(token) + ... + Token(typ='ID', value='total', line=1, column=8) + Token(typ='ASSIGN', value=':=', line=1, column=14) + Token(typ='ID', value='total', line=1, column=17) + Token(typ='OP', value='+', line=1, column=23) + Token(typ='ID', value='price', line=1, column=25) + Token(typ='OP', value='*', line=1, column=31) + Token(typ='ID', value='quantity', line=1, column=33) + Token(typ='END', value=';', line=1, column=41) + Token(typ='ID', value='tax', line=2, column=9) + Token(typ='ASSIGN', value=':=', line=2, column=13) + Token(typ='ID', value='price', line=2, column=16) + Token(typ='OP', value='*', line=2, column=22) + Token(typ='NUMBER', value='0.05', line=2, column=24) + Token(typ='END', value=';', line=2, column=28) From python-checkins at python.org Thu Sep 16 16:26:07 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Thu, 16 Sep 2010 16:26:07 +0200 (CEST) Subject: [Python-checkins] r84848 - python/branches/release31-maint/Lib/test/test_trace.py Message-ID: <20100916142607.14D69EE987@mail.python.org> Author: alexander.belopolsky Date: Thu Sep 16 16:26:01 2010 New Revision: 84848 Log: Issue 9315: Removed list comprehention test. Modified: python/branches/release31-maint/Lib/test/test_trace.py Modified: python/branches/release31-maint/Lib/test/test_trace.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_trace.py (original) +++ python/branches/release31-maint/Lib/test/test_trace.py Thu Sep 16 16:26:01 2010 @@ -70,12 +70,6 @@ def traced_doubler(num): return num * 2 -def traced_caller_list_comprehension(): - k = 10 - mylist = [traced_doubler(i) for i in range(k)] - return mylist - - class TracedClass(object): def __init__(self, x): self.a = x @@ -156,22 +150,6 @@ } self.assertEqual(self.tracer.results().counts, expected) - def test_trace_list_comprehension(self): - self.tracer.runfunc(traced_caller_list_comprehension) - - firstlineno_calling = get_firstlineno(traced_caller_list_comprehension) - firstlineno_called = get_firstlineno(traced_doubler) - expected = { - (self.my_py_filename, firstlineno_calling + 1): 1, - # List compehentions work differently in 3.x, so the count - # below changed compared to 2.x. - (self.my_py_filename, firstlineno_calling + 2): 12, - (self.my_py_filename, firstlineno_calling + 3): 1, - (self.my_py_filename, firstlineno_called + 1): 10, - } - self.assertEqual(self.tracer.results().counts, expected) - - def test_linear_methods(self): # XXX todo: later add 'static_method_linear' and 'class_method_linear' # here, once issue1764286 is resolved From python-checkins at python.org Thu Sep 16 17:04:49 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 16 Sep 2010 17:04:49 +0200 (CEST) Subject: [Python-checkins] r84849 - python/branches/py3k/Lib/test/test_gc.py Message-ID: <20100916150449.E7FC8EE99F@mail.python.org> Author: antoine.pitrou Date: Thu Sep 16 17:04:49 2010 New Revision: 84849 Log: Relax test condition (fix failures on FreeBSD buildbots) Modified: python/branches/py3k/Lib/test/test_gc.py Modified: python/branches/py3k/Lib/test/test_gc.py ============================================================================== --- python/branches/py3k/Lib/test/test_gc.py (original) +++ python/branches/py3k/Lib/test/test_gc.py Thu Sep 16 17:04:49 2010 @@ -495,11 +495,13 @@ stderr = run_command(code % "0") self.assertIn(b"gc: 2 uncollectable objects at shutdown", stderr) - self.assertNotIn(b"[, ]", stderr) + self.assertNotIn(b"", stderr) # With DEBUG_UNCOLLECTABLE, the garbage list gets printed stderr = run_command(code % "gc.DEBUG_UNCOLLECTABLE") self.assertIn(b"gc: 2 uncollectable objects at shutdown", stderr) - self.assertIn(b"[, ]", stderr) + self.assertTrue( + (b"[, ]" in stderr) or + (b"[, ]" in stderr), stderr) # With DEBUG_SAVEALL, no additional message should get printed # (because gc.garbage also contains normally reclaimable cyclic # references, and its elements get printed at runtime anyway). From python-checkins at python.org Thu Sep 16 18:13:52 2010 From: python-checkins at python.org (armin.ronacher) Date: Thu, 16 Sep 2010 18:13:52 +0200 (CEST) Subject: [Python-checkins] r84850 - peps/trunk/pep-0444.txt Message-ID: <20100916161352.5FC85EE9B5@mail.python.org> Author: armin.ronacher Date: Thu Sep 16 18:13:52 2010 New Revision: 84850 Log: The return value is a tuple, not an iterable Modified: peps/trunk/pep-0444.txt Modified: peps/trunk/pep-0444.txt ============================================================================== --- peps/trunk/pep-0444.txt (original) +++ peps/trunk/pep-0444.txt Thu Sep 16 18:13:52 2010 @@ -736,11 +736,10 @@ Values Returned by A Web3 Application ------------------------------------- -Web3 applications return an iterable in the form (``status``, -``headers``, ``body``). The return value can be any iterable type -that returns exactly three values. If the server supports -asynchronous applications (``web3.async``), the response may be a -callable object (which accepts no arguments). +Web3 applications return a tuple in the form (``status``, ``headers``, +``body``). If the server supports asynchronous applications +(``web3.async``), the response may be a callable object (which accepts no +arguments). The ``status`` value is assumed by a gateway or server to be an HTTP "status" bytes instance like ``b'200 OK'`` or ``b'404 Not Found'``. From python-checkins at python.org Thu Sep 16 19:50:57 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Thu, 16 Sep 2010 19:50:57 +0200 (CEST) Subject: [Python-checkins] r84851 - in python/branches/py3k: Misc/NEWS PCbuild/bz2.vcproj Message-ID: <20100916175057.AF25AEE995@mail.python.org> Author: hirokazu.yamamoto Date: Thu Sep 16 19:50:57 2010 New Revision: 84851 Log: Issue #9810: Compile bzip2 source files in python's project file directly. It used to be built with bzip2's makefile. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/PCbuild/bz2.vcproj Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Sep 16 19:50:57 2010 @@ -183,6 +183,9 @@ Build ----- +- Issue #9810: Compile bzip2 source files in python's project file + directly. It used to be built with bzip2's makefile. + - Issue #9848: Stopping trying to build _weakref in setup.py as it is a built-in module. Modified: python/branches/py3k/PCbuild/bz2.vcproj ============================================================================== --- python/branches/py3k/PCbuild/bz2.vcproj (original) +++ python/branches/py3k/PCbuild/bz2.vcproj Thu Sep 16 19:50:57 2010 @@ -43,6 +43,7 @@ + + + + + + + + + + + + + + + + + + + + + + From python-checkins at python.org Thu Sep 16 21:10:17 2010 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 16 Sep 2010 21:10:17 +0200 (CEST) Subject: [Python-checkins] r84852 - in python/branches/py3k: Lib/collections.py Lib/test/test_collections.py Misc/NEWS Message-ID: <20100916191017.840BCEE9E2@mail.python.org> Author: raymond.hettinger Date: Thu Sep 16 21:10:17 2010 New Revision: 84852 Log: Issue 9865: add __sizeof__ to OrderedDict. Modified: python/branches/py3k/Lib/collections.py python/branches/py3k/Lib/test/test_collections.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Thu Sep 16 21:10:17 2010 @@ -97,17 +97,6 @@ yield curr.key curr = curr.prev - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - tmp = self.__map, self.__root, self.__hardroot - del self.__map, self.__root, self.__hardroot - inst_dict = vars(self).copy() - self.__map, self.__root, self.__hardroot = tmp - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - def clear(self): 'od.clear() -> None. Remove all items from od.' root = self.__root @@ -162,6 +151,26 @@ link.next = first root.next = first.prev = link + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__root, self.__hardroot + del self.__map, self.__root, self.__hardroot + inst_dict = vars(self).copy() + self.__map, self.__root, self.__hardroot = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def __sizeof__(self): + sizeof = _sys.getsizeof + n = len(self) + 1 # number of links including root + size = sizeof(self.__dict__) # instance dictionary + size += sizeof(self.__map) * 2 # internal dict and inherited dict + size += sizeof(self.__hardroot) * n # link objects + size += sizeof(self.__root) * n # proxy objects + return size + setdefault = MutableMapping.setdefault update = MutableMapping.update pop = MutableMapping.pop Modified: python/branches/py3k/Lib/test/test_collections.py ============================================================================== --- python/branches/py3k/Lib/test/test_collections.py (original) +++ python/branches/py3k/Lib/test/test_collections.py Thu Sep 16 21:10:17 2010 @@ -994,6 +994,12 @@ with self.assertRaises(KeyError): od.move_to_end('x') + def test_sizeof(self): + # Wimpy test: Just verify the reported size is larger than a regular dict + d = dict(a=1) + od = OrderedDict(**d) + self.assertGreater(sys.getsizeof(od), sys.getsizeof(d)) + class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): type2test = OrderedDict Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Sep 16 21:10:17 2010 @@ -52,6 +52,8 @@ Library ------- +- Issue #9865: collections.OrderedDict now has a __sizeof__ method. + - Issue #9854: The default read() implementation in io.RawIOBase now handles non-blocking readinto() returning None correctly. From python-checkins at python.org Thu Sep 16 21:35:17 2010 From: python-checkins at python.org (brett.cannon) Date: Thu, 16 Sep 2010 21:35:17 +0200 (CEST) Subject: [Python-checkins] r84853 - peps/trunk/propcheck.py Message-ID: <20100916193517.C49DBEE984@mail.python.org> Author: brett.cannon Date: Thu Sep 16 21:35:17 2010 New Revision: 84853 Log: Make propcheck.py executable. Modified: peps/trunk/propcheck.py (contents, props changed) Modified: peps/trunk/propcheck.py ============================================================================== --- peps/trunk/propcheck.py (original) +++ peps/trunk/propcheck.py Thu Sep 16 21:35:17 2010 @@ -1,3 +1,4 @@ +#!/usr/bin/env python """Perform an integrity check upon all PEPs to make sure the needed svn properties are set.""" From python-checkins at python.org Thu Sep 16 22:00:17 2010 From: python-checkins at python.org (phillip.eby) Date: Thu, 16 Sep 2010 22:00:17 +0200 (CEST) Subject: [Python-checkins] r84854 - peps/trunk/pep-0333.txt Message-ID: <20100916200017.E09D9EE984@mail.python.org> Author: phillip.eby Date: Thu Sep 16 22:00:17 2010 New Revision: 84854 Log: Fix semantic breakage for Python 2.1 (yes, WSGI supports Python 2.1, which doesn't have an 'object' type), and the description using iter(filelike.read, '') was intentional; changing it broke the spec. (I.e., running that portion of the spec would raise TypeError) Modified: peps/trunk/pep-0333.txt Modified: peps/trunk/pep-0333.txt ============================================================================== --- peps/trunk/pep-0333.txt (original) +++ peps/trunk/pep-0333.txt Thu Sep 16 22:00:17 2010 @@ -172,7 +172,7 @@ return ['Hello world!\n'] - class AppClass(object): + class AppClass: """Produce the same output, but using a class (Note: 'AppClass' is the "application" here, so calling it @@ -321,7 +321,7 @@ from piglatin import piglatin - class LatinIter(object): + class LatinIter: """Transform iterated output to piglatin, if it's okay to do so @@ -345,7 +345,7 @@ else: return self._next() - class Latinator(object): + class Latinator: # by default, don't transform output transform = False @@ -1371,7 +1371,7 @@ Apart from the handling of ``close()``, the semantics of returning a file wrapper from the application should be the same as if the -application had returned ``iter(filelike, '')``. In other words, +application had returned ``iter(filelike.read, '')``. In other words, transmission should begin at the current position within the "file" at the time that transmission begins, and continue until the end is reached. @@ -1390,7 +1390,7 @@ are portable across platforms. Here's a simple platform-agnostic file wrapper class, suitable for old (pre 2.2) and new Pythons alike:: - class FileWrapper(object): + class FileWrapper: def __init__(self, filelike, blksize=8192): self.filelike = filelike From python-checkins at python.org Fri Sep 17 01:52:03 2010 From: python-checkins at python.org (brett.cannon) Date: Fri, 17 Sep 2010 01:52:03 +0200 (CEST) Subject: [Python-checkins] r84855 - in peps/trunk/pep0: constants.py output.py Message-ID: <20100916235203.0F53EEE983@mail.python.org> Author: brett.cannon Date: Fri Sep 17 01:52:02 2010 New Revision: 84855 Log: Set the date for when PEP 0 was last generated. Modified: peps/trunk/pep0/constants.py peps/trunk/pep0/output.py Modified: peps/trunk/pep0/constants.py ============================================================================== --- peps/trunk/pep0/constants.py (original) +++ peps/trunk/pep0/constants.py Fri Sep 17 01:52:02 2010 @@ -5,8 +5,7 @@ header = u"""PEP: 0 Title: Index of Python Enhancement Proposals (PEPs) -Version: $Revision$ -Last-Modified: $Date$ +Last-Modified: %s Author: David Goodger , Barry Warsaw Status: Active Modified: peps/trunk/pep0/output.py ============================================================================== --- peps/trunk/pep0/output.py (original) +++ peps/trunk/pep0/output.py Fri Sep 17 01:52:02 2010 @@ -1,4 +1,5 @@ """Code to handle the output of PEP 0.""" +import datetime import sys import unicodedata @@ -99,7 +100,8 @@ def write_pep0(peps, output=sys.stdout): - print>>output, constants.header + today = datetime.date.today().strftime("%Y-%m-%d") + print>>output, constants.header % today print>>output print>>output, u"Introduction" print>>output, constants.intro From solipsis at pitrou.net Fri Sep 17 05:08:09 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 17 Sep 2010 05:08:09 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84852): sum=0 Message-ID: py3k results for svn r84852 (hg cset 175c53584cb9) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogK61KEt', '-x'] From python-checkins at python.org Fri Sep 17 08:26:45 2010 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 17 Sep 2010 08:26:45 +0200 (CEST) Subject: [Python-checkins] r84856 - python/branches/py3k/Doc/library/re.rst Message-ID: <20100917062645.E2DC7EE995@mail.python.org> Author: raymond.hettinger Date: Fri Sep 17 08:26:45 2010 New Revision: 84856 Log: Fix typo in example regular expression. Modified: python/branches/py3k/Doc/library/re.rst Modified: python/branches/py3k/Doc/library/re.rst ============================================================================== --- python/branches/py3k/Doc/library/re.rst (original) +++ python/branches/py3k/Doc/library/re.rst Fri Sep 17 08:26:45 2010 @@ -1299,7 +1299,7 @@ def tokenize(s): tok_spec = [ - ('NUMBER', r'\d+(.\d+)?'), # Integer or decimal number + ('NUMBER', r'\d+(\.\d*)?'), # Integer or decimal number ('ASSIGN', r':='), # Assignment operator ('END', ';'), # Statement terminator ('ID', r'[A-Za-z]+'), # Identifiers From python-checkins at python.org Fri Sep 17 10:53:31 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Fri, 17 Sep 2010 10:53:31 +0200 (CEST) Subject: [Python-checkins] r84857 - python/branches/py3k/PCbuild/rt.bat Message-ID: <20100917085331.7E2B3EE9A1@mail.python.org> Author: hirokazu.yamamoto Date: Fri Sep 17 10:53:31 2010 New Revision: 84857 Log: Let's see if tcl/tk test runs on windows buildbot with this fix. Modified: python/branches/py3k/PCbuild/rt.bat Modified: python/branches/py3k/PCbuild/rt.bat ============================================================================== --- python/branches/py3k/PCbuild/rt.bat (original) +++ python/branches/py3k/PCbuild/rt.bat Fri Sep 17 10:53:31 2010 @@ -30,7 +30,7 @@ set suffix= set qmode= set dashO= -set tcltk= +set tcltk=tcltk :CheckOpts if "%1"=="-O" (set dashO=-O) & shift & goto CheckOpts From python-checkins at python.org Fri Sep 17 12:09:04 2010 From: python-checkins at python.org (vinay.sajip) Date: Fri, 17 Sep 2010 12:09:04 +0200 (CEST) Subject: [Python-checkins] r84858 - in python/branches: py3k/Doc/library/logging.rst release27-maint/Doc/library/logging.rst Message-ID: <20100917100904.748EDEE998@mail.python.org> Author: vinay.sajip Date: Fri Sep 17 12:09:04 2010 New Revision: 84858 Log: Improved basicConfig and custom level documentation. Modified: python/branches/py3k/Doc/library/logging.rst python/branches/release27-maint/Doc/library/logging.rst Modified: python/branches/py3k/Doc/library/logging.rst ============================================================================== --- python/branches/py3k/Doc/library/logging.rst (original) +++ python/branches/py3k/Doc/library/logging.rst Fri Sep 17 12:09:04 2010 @@ -589,6 +589,22 @@ the message to its destination. Most user-defined subclasses of :class:`Handler` will need to override this :meth:`emit`. +.. _custom-levels: + +Custom Levels +^^^^^^^^^^^^^ + +Defining your own levels is possible, but should not be necessary, as the +existing levels have been chosen on the basis of practical experience. +However, if you are convinced that you need custom levels, great care should +be exercised when doing this, and it is possibly *a very bad idea to define +custom levels if you are developing a library*. That's because if multiple +library authors all define their own custom levels, there is a chance that +the logging output from such multiple libraries used together will be +difficult for the using developer to control and/or interpret, because a +given numeric value might mean different things for different libraries. + + Useful Handlers --------------- @@ -790,12 +806,19 @@ interpreted as for :func:`debug`. Exception info is added to the logging message. This function should only be called from an exception handler. - .. function:: log(level, msg, *args, **kwargs) Logs a message with level *level* on the root logger. The other arguments are interpreted as for :func:`debug`. + PLEASE NOTE: The above module-level functions which delegate to the root + logger should *not* be used in threads, in versions of Python earlier than + 2.7.1 and 3.2, unless at least one handler has been added to the root + logger *before* the threads are started. These convenience functions call + :func:`basicConfig` to ensure that at least one handler is available; in + earlier versions of Python, this can (under rare circumstances) lead to + handlers being added multiple times to the root logger, which can in turn + lead to multiple messages for the same event. .. function:: disable(lvl) @@ -817,6 +840,8 @@ registered using this function, levels should be positive integers and they should increase in increasing order of severity. + NOTE: If you are thinking of defining your own levels, please see the section + on :ref:`custom-levels`. .. function:: getLevelName(lvl) @@ -848,6 +873,13 @@ This function does nothing if the root logger already has handlers configured for it. + PLEASE NOTE: This function should be called from the main thread + before other threads are started. In versions of Python prior to + 2.7.1 and 3.2, if this function is called from multiple threads, + it is possible (in rare circumstances) that a handler will be added + to the root logger more than once, leading to unexpected results + such as messages being duplicated in the log. + The following keyword arguments are supported. +--------------+---------------------------------------------+ @@ -875,7 +907,6 @@ | | present, 'stream' is ignored. | +--------------+---------------------------------------------+ - .. function:: shutdown() Informs the logging system to perform an orderly shutdown by flushing and Modified: python/branches/release27-maint/Doc/library/logging.rst ============================================================================== --- python/branches/release27-maint/Doc/library/logging.rst (original) +++ python/branches/release27-maint/Doc/library/logging.rst Fri Sep 17 12:09:04 2010 @@ -593,6 +593,22 @@ the message to its destination. Most user-defined subclasses of :class:`Handler` will need to override this :meth:`emit`. +.. _custom-levels: + +Custom Levels +^^^^^^^^^^^^^ + +Defining your own levels is possible, but should not be necessary, as the +existing levels have been chosen on the basis of practical experience. +However, if you are convinced that you need custom levels, great care should +be exercised when doing this, and it is possibly *a very bad idea to define +custom levels if you are developing a library*. That's because if multiple +library authors all define their own custom levels, there is a chance that +the logging output from such multiple libraries used together will be +difficult for the using developer to control and/or interpret, because a +given numeric value might mean different things for different libraries. + + Useful Handlers --------------- @@ -792,6 +808,14 @@ Logs a message with level *level* on the root logger. The other arguments are interpreted as for :func:`debug`. + PLEASE NOTE: The above module-level functions which delegate to the root + logger should *not* be used in threads, in versions of Python earlier than + 2.7.1 and 3.2, unless at least one handler has been added to the root + logger *before* the threads are started. These convenience functions call + :func:`basicConfig` to ensure that at least one handler is available; in + earlier versions of Python, this can (under rare circumstances) lead to + handlers being added multiple times to the root logger, which can in turn + lead to multiple messages for the same event. .. function:: disable(lvl) @@ -813,6 +837,8 @@ registered using this function, levels should be positive integers and they should increase in increasing order of severity. + NOTE: If you are thinking of defining your own levels, please see the section + on :ref:`custom-levels`. .. function:: getLevelName(lvl) @@ -847,6 +873,13 @@ .. versionchanged:: 2.4 Formerly, :func:`basicConfig` did not take any keyword arguments. + PLEASE NOTE: This function should be called from the main thread + before other threads are started. In versions of Python prior to + 2.7.1 and 3.2, if this function is called from multiple threads, + it is possible (in rare circumstances) that a handler will be added + to the root logger more than once, leading to unexpected results + such as messages being duplicated in the log. + The following keyword arguments are supported. +--------------+---------------------------------------------+ From python-checkins at python.org Fri Sep 17 14:45:26 2010 From: python-checkins at python.org (vinay.sajip) Date: Fri, 17 Sep 2010 14:45:26 +0200 (CEST) Subject: [Python-checkins] r84859 - in python/branches: py3k/Doc/library/logging.rst release27-maint/Doc/library/logging.rst Message-ID: <20100917124526.34276EE98E@mail.python.org> Author: vinay.sajip Date: Fri Sep 17 14:45:26 2010 New Revision: 84859 Log: Improved Filter documentation. Modified: python/branches/py3k/Doc/library/logging.rst python/branches/release27-maint/Doc/library/logging.rst Modified: python/branches/py3k/Doc/library/logging.rst ============================================================================== --- python/branches/py3k/Doc/library/logging.rst (original) +++ python/branches/py3k/Doc/library/logging.rst Fri Sep 17 14:45:26 2010 @@ -1465,6 +1465,8 @@ 2008-01-18 14:49:54,033 d.e.f WARNING IP: 127.0.0.1 User: jim A message at WARNING level with 2 parameters +.. _filters-contextual: + Using Filters to impart contextual information ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -2852,6 +2854,18 @@ will not be filtered by a logger's filter setting, unless the filter has also been applied to those descendant loggers. +Other uses for filters +^^^^^^^^^^^^^^^^^^^^^^ + +Although filters are used primarily to filter records based on more +sophisticated criteria than levels, they get to see every record which is +processed by the handler or logger they're attached to: this can be useful if +you want to do things like counting how many records were processed by a +particular logger or handler, or adding, changing or removing attributes in +the LogRecord being processed. Obviously changing the LogRecord needs to be +done with some care, but it does allow the injection of contextual information +into logs (see :ref:`filters-contextual`). + .. _log-record: LogRecord Objects Modified: python/branches/release27-maint/Doc/library/logging.rst ============================================================================== --- python/branches/release27-maint/Doc/library/logging.rst (original) +++ python/branches/release27-maint/Doc/library/logging.rst Fri Sep 17 14:45:26 2010 @@ -1479,6 +1479,8 @@ The :class:`LoggerAdapter` class was not present in previous versions. +.. _filters-contextual: + Using Filters to impart contextual information ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -2657,7 +2659,7 @@ Filter Objects -------------- -Filters can be used by :class:`Handler`\ s and :class:`Logger`\ s for +:class:`Filter`\ s can be used by :class:`Handler`\ s and :class:`Logger`\ s for more sophisticated filtering than is provided by levels. The base filter class only allows events which are below a certain point in the logger hierarchy. For example, a filter initialized with "A.B" will allow events logged by loggers @@ -2685,6 +2687,18 @@ will not be filtered by a logger's filter setting, unless the filter has also been applied to those descendant loggers. +Other uses for filters +^^^^^^^^^^^^^^^^^^^^^^ + +Although filters are used primarily to filter records based on more +sophisticated criteria than levels, they get to see every record which is +processed by the handler or logger they're attached to: this can be useful if +you want to do things like counting how many records were processed by a +particular logger or handler, or adding, changing or removing attributes in +the LogRecord being processed. Obviously changing the LogRecord needs to be +done with some care, but it does allow the injection of contextual information +into logs (see :ref:`filters-contextual`). + .. _log-record: LogRecord Objects From python-checkins at python.org Fri Sep 17 16:13:36 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Fri, 17 Sep 2010 16:13:36 +0200 (CEST) Subject: [Python-checkins] r84860 - python/branches/release27-maint Message-ID: <20100917141336.04961EE98C@mail.python.org> Author: hirokazu.yamamoto Date: Fri Sep 17 16:13:35 2010 New Revision: 84860 Log: Blocked revisions 84643-84645,84741 via svnmerge ........ r84643 | hirokazu.yamamoto | 2010-09-09 15:08:36 +0900 | 4 lines Updated VC6 files. * pythoncore.dsp: updated project file * readme.txt: removed dead link * tcl852.patch: fixed patch. it was doubled. ........ r84644 | hirokazu.yamamoto | 2010-09-09 15:14:23 +0900 | 1 line Updated VS7.1 project file. (I cannot test this file because I don't have VS7.1) ........ r84645 | hirokazu.yamamoto | 2010-09-09 15:24:43 +0900 | 5 lines PCBuild cosmetic fixes. * pythoncore.vcproj: Fixed indentation * _multiprocessing.vcproj: Converted ProjectGUID to uppercase. Otherwise, VS8 _multiprocessing.vcproj created by vs9to8.py was modified every time loads it in VS8 IDE. ........ r84741 | hirokazu.yamamoto | 2010-09-13 01:06:18 +0900 | 2 lines Fixed refcount bug. I placed Py_INCREF in create_comerror() for compatibility with Python2.7. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Fri Sep 17 18:35:37 2010 From: python-checkins at python.org (senthil.kumaran) Date: Fri, 17 Sep 2010 18:35:37 +0200 (CEST) Subject: [Python-checkins] r84861 - in python/branches/py3k/Lib/distutils: dir_util.py tests/test_dir_util.py Message-ID: <20100917163537.D9794EE98E@mail.python.org> Author: senthil.kumaran Date: Fri Sep 17 18:35:37 2010 New Revision: 84861 Log: Fix Issue2236: Distutils' mkpath implementation ignoring the "mode" parameter Modified: python/branches/py3k/Lib/distutils/dir_util.py python/branches/py3k/Lib/distutils/tests/test_dir_util.py Modified: python/branches/py3k/Lib/distutils/dir_util.py ============================================================================== --- python/branches/py3k/Lib/distutils/dir_util.py (original) +++ python/branches/py3k/Lib/distutils/dir_util.py Fri Sep 17 18:35:37 2010 @@ -68,7 +68,7 @@ if not dry_run: try: - os.mkdir(head) + os.mkdir(head, mode) created_dirs.append(head) except OSError as exc: raise DistutilsFileError( Modified: python/branches/py3k/Lib/distutils/tests/test_dir_util.py ============================================================================== --- python/branches/py3k/Lib/distutils/tests/test_dir_util.py (original) +++ python/branches/py3k/Lib/distutils/tests/test_dir_util.py Fri Sep 17 18:35:37 2010 @@ -1,6 +1,7 @@ """Tests for distutils.dir_util.""" import unittest import os +import stat import shutil from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, @@ -48,6 +49,12 @@ wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + def test_mkpath_with_custom_mode(self): + mkpath(self.target, 0o700) + self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + mkpath(self.target2, 0o555) + self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + def test_create_tree_verbosity(self): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) From python-checkins at python.org Fri Sep 17 18:40:01 2010 From: python-checkins at python.org (senthil.kumaran) Date: Fri, 17 Sep 2010 18:40:01 +0200 (CEST) Subject: [Python-checkins] r84862 - in python/branches/release27-maint: Lib/distutils/dir_util.py Lib/distutils/tests/test_dir_util.py Message-ID: <20100917164001.D5D1AEE994@mail.python.org> Author: senthil.kumaran Date: Fri Sep 17 18:40:01 2010 New Revision: 84862 Log: Merged revisions 84861 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84861 | senthil.kumaran | 2010-09-17 22:05:37 +0530 (Fri, 17 Sep 2010) | 3 lines Fix Issue2236: Distutils' mkpath implementation ignoring the "mode" parameter ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/distutils/dir_util.py python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py Modified: python/branches/release27-maint/Lib/distutils/dir_util.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/dir_util.py (original) +++ python/branches/release27-maint/Lib/distutils/dir_util.py Fri Sep 17 18:40:01 2010 @@ -68,7 +68,7 @@ if not dry_run: try: - os.mkdir(head) + os.mkdir(head, mode) created_dirs.append(head) except OSError, exc: raise DistutilsFileError, \ Modified: python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py (original) +++ python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py Fri Sep 17 18:40:01 2010 @@ -1,6 +1,7 @@ """Tests for distutils.dir_util.""" import unittest import os +import stat import shutil from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, @@ -48,6 +49,12 @@ wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + def test_mkpath_with_custom_mode(self): + mkpath(self.target, 0o700) + self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + mkpath(self.target2, 0o555) + self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + def test_create_tree_verbosity(self): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) From python-checkins at python.org Fri Sep 17 18:42:05 2010 From: python-checkins at python.org (senthil.kumaran) Date: Fri, 17 Sep 2010 18:42:05 +0200 (CEST) Subject: [Python-checkins] r84863 - in python/branches/release31-maint: Lib/distutils/dir_util.py Lib/distutils/tests/test_dir_util.py Message-ID: <20100917164205.D06FFEE9F7@mail.python.org> Author: senthil.kumaran Date: Fri Sep 17 18:42:05 2010 New Revision: 84863 Log: Merged revisions 84861 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84861 | senthil.kumaran | 2010-09-17 22:05:37 +0530 (Fri, 17 Sep 2010) | 3 lines Fix Issue2236: Distutils' mkpath implementation ignoring the "mode" parameter ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/distutils/dir_util.py python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py Modified: python/branches/release31-maint/Lib/distutils/dir_util.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/dir_util.py (original) +++ python/branches/release31-maint/Lib/distutils/dir_util.py Fri Sep 17 18:42:05 2010 @@ -68,7 +68,7 @@ if not dry_run: try: - os.mkdir(head) + os.mkdir(head, mode) created_dirs.append(head) except OSError as exc: raise DistutilsFileError( Modified: python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py (original) +++ python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py Fri Sep 17 18:42:05 2010 @@ -1,6 +1,7 @@ """Tests for distutils.dir_util.""" import unittest import os +import stat import shutil from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, @@ -48,6 +49,12 @@ wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + def test_mkpath_with_custom_mode(self): + mkpath(self.target, 0o700) + self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + mkpath(self.target2, 0o555) + self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + def test_create_tree_verbosity(self): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) From python-checkins at python.org Fri Sep 17 20:57:36 2010 From: python-checkins at python.org (vinay.sajip) Date: Fri, 17 Sep 2010 20:57:36 +0200 (CEST) Subject: [Python-checkins] r84864 - in python/branches/py3k: Lib/test/test_logging.py Misc/NEWS Message-ID: <20100917185736.ED737EEA29@mail.python.org> Author: vinay.sajip Date: Fri Sep 17 20:57:36 2010 New Revision: 84864 Log: Issue #9441: logging: Improved test coverage for rotating file handlers. Modified: python/branches/py3k/Lib/test/test_logging.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/test/test_logging.py ============================================================================== --- python/branches/py3k/Lib/test/test_logging.py (original) +++ python/branches/py3k/Lib/test/test_logging.py Fri Sep 17 20:57:36 2010 @@ -26,6 +26,7 @@ import logging.config import codecs +import datetime import pickle import io import gc @@ -1790,6 +1791,81 @@ self.assertEqual(data.name, self.que_logger.name) self.assertEqual((data.msg, data.args), (msg, None)) +class BaseFileTest(BaseTest): + "Base class for handler tests that write log files" + + def setUp(self): + BaseTest.setUp(self) + self.fn = tempfile.mktemp(".log") + self.rmfiles = [] + + def tearDown(self): + for fn in self.rmfiles: + os.unlink(fn) + + def assertLogFile(self, filename): + "Assert a log file is there and register it for deletion" + self.assertTrue(os.path.exists(filename), + msg="Log file %r does not exist") + self.rmfiles.append(filename) + + +class RotatingFileHandlerTest(BaseFileTest): + def next_rec(self): + return logging.LogRecord('n', logging.DEBUG, 'p', 1, + self.next_message(), None, None, None) + + def test_should_not_rollover(self): + # If maxbytes is zero rollover never occurs + rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0) + self.assertFalse(rh.shouldRollover(None)) + + def test_should_rollover(self): + rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1) + self.assertTrue(rh.shouldRollover(self.next_rec())) + + def test_file_created(self): + # checks that the file is created and assumes it was created + # by us + self.assertFalse(os.path.exists(self.fn)) + rh = logging.handlers.RotatingFileHandler(self.fn) + rh.emit(self.next_rec()) + self.assertLogFile(self.fn) + + def test_rollover_filenames(self): + rh = logging.handlers.RotatingFileHandler( + self.fn, backupCount=2, maxBytes=1) + rh.emit(self.next_rec()) + self.assertLogFile(self.fn) + rh.emit(self.next_rec()) + self.assertLogFile(self.fn + ".1") + rh.emit(self.next_rec()) + self.assertLogFile(self.fn + ".2") + self.assertFalse(os.path.exists(self.fn + ".3")) + + +class TimedRotatingFileHandlerTest(BaseFileTest): + # test methods added below + pass + +def secs(**kw): + return datetime.timedelta(**kw) // datetime.timedelta(seconds=1) + +for when, exp in (('S', 1), + ('M', 60), + ('H', 60 * 60), + ('D', 60 * 60 * 24), + ('MIDNIGHT', 60 * 60 * 23), + # current time (epoch start) is a Thursday, W0 means Monday + ('W0', secs(days=4, hours=23)),): + def test_compute_rollover(self, when=when, exp=exp): + rh = logging.handlers.TimedRotatingFileHandler( + self.fn, when=when, interval=1, backupCount=0) + self.assertEquals(exp, rh.computeRollover(0.0)) + setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover) + + + # Set the locale to the platform-dependent default. I have no idea # why the test does this, but in any case we save the current locale # first and restore it at the end. @@ -1799,7 +1875,8 @@ CustomLevelsAndFiltersTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest, MemoryTest, EncodingTest, WarningsTest, ConfigDictTest, ManagerTest, - ChildLoggerTest, QueueHandlerTest) + ChildLoggerTest, QueueHandlerTest, + RotatingFileHandlerTest, TimedRotatingFileHandlerTest) if __name__ == "__main__": test_main() Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Sep 17 20:57:36 2010 @@ -52,6 +52,8 @@ Library ------- +- Issue #9441: logging has better coverage for rotating file handlers. + - Issue #9865: collections.OrderedDict now has a __sizeof__ method. - Issue #9854: The default read() implementation in io.RawIOBase now From python-checkins at python.org Sat Sep 18 01:27:10 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Sat, 18 Sep 2010 01:27:10 +0200 (CEST) Subject: [Python-checkins] r84865 - in python/branches/py3k: Lib/tkinter/font.py Lib/tkinter/test/test_tkinter/test_font.py Misc/NEWS Message-ID: <20100917232710.0A9ADEE9B3@mail.python.org> Author: amaury.forgeotdarc Date: Sat Sep 18 01:27:09 2010 New Revision: 84865 Log: #1730136: Fix comparison between a tk Font object and an object of a different type. Added: python/branches/py3k/Lib/tkinter/test/test_tkinter/test_font.py (contents, props changed) Modified: python/branches/py3k/Lib/tkinter/font.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/tkinter/font.py ============================================================================== --- python/branches/py3k/Lib/tkinter/font.py (original) +++ python/branches/py3k/Lib/tkinter/font.py Sat Sep 18 01:27:09 2010 @@ -97,7 +97,7 @@ return self.name def __eq__(self, other): - return self.name == other.name and isinstance(other, Font) + return isinstance(other, Font) and self.name == other.name def __getitem__(self, key): return self.cget(key) Added: python/branches/py3k/Lib/tkinter/test/test_tkinter/test_font.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/tkinter/test/test_tkinter/test_font.py Sat Sep 18 01:27:09 2010 @@ -0,0 +1,20 @@ +import unittest +import tkinter +from tkinter import font +from test.support import requires, run_unittest + +requires('gui') + +class FontTest(unittest.TestCase): + def test_font_eq(self): + font1 = font.nametofont("system") + font2 = font.nametofont("system") + self.assertIsNot(font1, font2) + self.assertEqual(font1, font2) + self.assertNotEqual(font1, font1.copy()) + self.assertNotEqual(font1, 0) + +tests_gui = (FontTest, ) + +if __name__ == "__main__": + run_unittest(*tests_gui) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 18 01:27:09 2010 @@ -52,6 +52,9 @@ Library ------- +- Issue #1730136: Fix the comparison between a tk.font.Font and an object of + another kind. + - Issue #9441: logging has better coverage for rotating file handlers. - Issue #9865: collections.OrderedDict now has a __sizeof__ method. From python-checkins at python.org Sat Sep 18 01:34:26 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 18 Sep 2010 01:34:26 +0200 (CEST) Subject: [Python-checkins] r84866 - in python/branches/py3k: Lib/posixpath.py Misc/NEWS Message-ID: <20100917233426.972A6EE9B3@mail.python.org> Author: victor.stinner Date: Sat Sep 18 01:34:26 2010 New Revision: 84866 Log: Issue #767645: Set os.path.supports_unicode_filenames to True in posixpath Previous commit changed macpath but macpath is not used anymore as os.path Modified: python/branches/py3k/Lib/posixpath.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/posixpath.py ============================================================================== --- python/branches/py3k/Lib/posixpath.py (original) +++ python/branches/py3k/Lib/posixpath.py Sat Sep 18 01:34:26 2010 @@ -427,7 +427,7 @@ path = normpath(resolved) return path -supports_unicode_filenames = False +supports_unicode_filenames = (sys.platform == 'darwin') def relpath(path, start=None): """Return a relative version of a path""" Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 18 01:34:26 2010 @@ -74,8 +74,7 @@ - logging: Enhanced HTTPHandler with secure and credentials initializers. -- Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X - (macpath module). +- Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X. - Issue #9837: The read() method of ZipExtFile objects (as returned by ZipFile.open()) could return more bytes than requested. From python-checkins at python.org Sat Sep 18 01:35:30 2010 From: python-checkins at python.org (vinay.sajip) Date: Sat, 18 Sep 2010 01:35:30 +0200 (CEST) Subject: [Python-checkins] r84867 - python/branches/py3k/Lib/test/test_logging.py Message-ID: <20100917233530.00D4CEEA11@mail.python.org> Author: vinay.sajip Date: Sat Sep 18 01:35:29 2010 New Revision: 84867 Log: Temporarily commented out test which succeeds locally but fails on buildbots, while investigating. Modified: python/branches/py3k/Lib/test/test_logging.py Modified: python/branches/py3k/Lib/test/test_logging.py ============================================================================== --- python/branches/py3k/Lib/test/test_logging.py (original) +++ python/branches/py3k/Lib/test/test_logging.py Sat Sep 18 01:35:29 2010 @@ -1876,7 +1876,9 @@ ConfigFileTest, SocketHandlerTest, MemoryTest, EncodingTest, WarningsTest, ConfigDictTest, ManagerTest, ChildLoggerTest, QueueHandlerTest, - RotatingFileHandlerTest, TimedRotatingFileHandlerTest) + RotatingFileHandlerTest, + #TimedRotatingFileHandlerTest + ) if __name__ == "__main__": test_main() From python-checkins at python.org Sat Sep 18 01:35:50 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 18 Sep 2010 01:35:50 +0200 (CEST) Subject: [Python-checkins] r84868 - in python/branches/release27-maint: Lib/posixpath.py Misc/NEWS Message-ID: <20100917233550.64239EEA06@mail.python.org> Author: victor.stinner Date: Sat Sep 18 01:35:50 2010 New Revision: 84868 Log: Merged revisions 84866 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84866 | victor.stinner | 2010-09-18 01:34:26 +0200 (sam., 18 sept. 2010) | 4 lines Issue #767645: Set os.path.supports_unicode_filenames to True in posixpath Previous commit changed macpath but macpath is not used anymore as os.path ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/posixpath.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/posixpath.py ============================================================================== --- python/branches/release27-maint/Lib/posixpath.py (original) +++ python/branches/release27-maint/Lib/posixpath.py Sat Sep 18 01:35:50 2010 @@ -394,7 +394,7 @@ path = normpath(resolved) return path -supports_unicode_filenames = False +supports_unicode_filenames = (sys.platform == 'darwin') def relpath(path, start=curdir): """Return a relative version of a path""" Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sat Sep 18 01:35:50 2010 @@ -55,8 +55,7 @@ - Issue #9826: OrderedDict.__repr__ can now handle self-referential values: d['x'] = d. -- Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X - (macpath module). +- Issue #767645: Set os.path.supports_unicode_filenames to True on Mac OS X. - Issue #9837: The read() method of ZipExtFile objects (as returned by ZipFile.open()) could return more bytes than requested. From python-checkins at python.org Sat Sep 18 01:39:42 2010 From: python-checkins at python.org (amaury.forgeotdarc) Date: Sat, 18 Sep 2010 01:39:42 +0200 (CEST) Subject: [Python-checkins] r84869 - python/branches/py3k/Modules/posixmodule.c Message-ID: <20100917233942.EF256EEA65@mail.python.org> Author: amaury.forgeotdarc Date: Sat Sep 18 01:39:42 2010 New Revision: 84869 Log: Remove unused code in posixmodule.c Modified: python/branches/py3k/Modules/posixmodule.c Modified: python/branches/py3k/Modules/posixmodule.c ============================================================================== --- python/branches/py3k/Modules/posixmodule.c (original) +++ python/branches/py3k/Modules/posixmodule.c Sat Sep 18 01:39:42 2010 @@ -557,14 +557,6 @@ return PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); } -#ifdef MS_WINDOWS -static PyObject * -posix_error_with_unicode_filename(Py_UNICODE* name) -{ - return PyErr_SetFromErrnoWithUnicodeFilename(PyExc_OSError, name); -} -#endif /* MS_WINDOWS */ - static PyObject * posix_error_with_allocated_filename(PyObject* name) @@ -1628,66 +1620,6 @@ return v; } -#ifdef MS_WINDOWS - -/* IsUNCRoot -- test whether the supplied path is of the form \\SERVER\SHARE\, - where / can be used in place of \ and the trailing slash is optional. - Both SERVER and SHARE must have at least one character. -*/ - -#define ISSLASHA(c) ((c) == '\\' || (c) == '/') -#define ISSLASHW(c) ((c) == L'\\' || (c) == L'/') -#ifndef ARRAYSIZE -#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0])) -#endif - -static BOOL -IsUNCRootA(char *path, int pathlen) -{ - #define ISSLASH ISSLASHA - - int i, share; - - if (pathlen < 5 || !ISSLASH(path[0]) || !ISSLASH(path[1])) - /* minimum UNCRoot is \\x\y */ - return FALSE; - for (i = 2; i < pathlen ; i++) - if (ISSLASH(path[i])) break; - if (i == 2 || i == pathlen) - /* do not allow \\\SHARE or \\SERVER */ - return FALSE; - share = i+1; - for (i = share; i < pathlen; i++) - if (ISSLASH(path[i])) break; - return (i != share && (i == pathlen || i == pathlen-1)); - - #undef ISSLASH -} - -static BOOL -IsUNCRootW(Py_UNICODE *path, int pathlen) -{ - #define ISSLASH ISSLASHW - - int i, share; - - if (pathlen < 5 || !ISSLASH(path[0]) || !ISSLASH(path[1])) - /* minimum UNCRoot is \\x\y */ - return FALSE; - for (i = 2; i < pathlen ; i++) - if (ISSLASH(path[i])) break; - if (i == 2 || i == pathlen) - /* do not allow \\\SHARE or \\SERVER */ - return FALSE; - share = i+1; - for (i = share; i < pathlen; i++) - if (ISSLASH(path[i])) break; - return (i != share && (i == pathlen || i == pathlen-1)); - - #undef ISSLASH -} -#endif /* MS_WINDOWS */ - static PyObject * posix_do_stat(PyObject *self, PyObject *args, char *format, From python-checkins at python.org Sat Sep 18 02:31:44 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sat, 18 Sep 2010 02:31:44 +0200 (CEST) Subject: [Python-checkins] r84870 - python/branches/py3k/PCbuild/rt.bat Message-ID: <20100918003144.55EACEE9D6@mail.python.org> Author: hirokazu.yamamoto Date: Sat Sep 18 02:31:44 2010 New Revision: 84870 Log: regrtest.py now runs python in build/test_python_xxx. (deeper than here) So failed to load tcl/tk dll because $(dist)/tcltk/bin was set to PATH as relative path. (Windows) Modified: python/branches/py3k/PCbuild/rt.bat Modified: python/branches/py3k/PCbuild/rt.bat ============================================================================== --- python/branches/py3k/PCbuild/rt.bat (original) +++ python/branches/py3k/PCbuild/rt.bat Sat Sep 18 02:31:44 2010 @@ -38,7 +38,7 @@ if "%1"=="-d" (set suffix=_d) & shift & goto CheckOpts if "%1"=="-x64" (set prefix=amd64) & (set tcltk=tcltk64) & shift & goto CheckOpts -PATH %PATH%;..\..\%tcltk%\bin +PATH %PATH%;%~dp0..\..\%tcltk%\bin set exe=%prefix%\python%suffix% set cmd=%exe% %dashO% -Wd -E -bb ../lib/test/regrtest.py %1 %2 %3 %4 %5 %6 %7 %8 %9 if defined qmode goto Qmode From python-checkins at python.org Sat Sep 18 04:55:03 2010 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 18 Sep 2010 04:55:03 +0200 (CEST) Subject: [Python-checkins] r84871 - python/branches/py3k/Lib/distutils/tests/test_dir_util.py Message-ID: <20100918025503.E27D6EE9D6@mail.python.org> Author: senthil.kumaran Date: Sat Sep 18 04:55:03 2010 New Revision: 84871 Log: Skip the distutils mode test on Windows OS. Modified: python/branches/py3k/Lib/distutils/tests/test_dir_util.py Modified: python/branches/py3k/Lib/distutils/tests/test_dir_util.py ============================================================================== --- python/branches/py3k/Lib/distutils/tests/test_dir_util.py (original) +++ python/branches/py3k/Lib/distutils/tests/test_dir_util.py Sat Sep 18 04:55:03 2010 @@ -3,6 +3,7 @@ import os import stat import shutil +import sys from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, ensure_relative) @@ -49,6 +50,8 @@ wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + @unittest.skipIf(sys.platform.startswith('win'), + "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): mkpath(self.target, 0o700) self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) From python-checkins at python.org Sat Sep 18 04:57:28 2010 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 18 Sep 2010 04:57:28 +0200 (CEST) Subject: [Python-checkins] r84872 - in python/branches/release31-maint: Lib/distutils/tests/test_dir_util.py Message-ID: <20100918025728.894C3EE9D6@mail.python.org> Author: senthil.kumaran Date: Sat Sep 18 04:57:28 2010 New Revision: 84872 Log: Merged revisions 84871 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84871 | senthil.kumaran | 2010-09-18 08:25:03 +0530 (Sat, 18 Sep 2010) | 3 lines Skip the distutils mode test on Windows OS. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py Modified: python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py (original) +++ python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py Sat Sep 18 04:57:28 2010 @@ -3,6 +3,7 @@ import os import stat import shutil +import sys from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, ensure_relative) @@ -49,6 +50,8 @@ wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + @unittest.skipIf(sys.platform.startswith('win'), + "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): mkpath(self.target, 0o700) self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) From python-checkins at python.org Sat Sep 18 04:58:49 2010 From: python-checkins at python.org (senthil.kumaran) Date: Sat, 18 Sep 2010 04:58:49 +0200 (CEST) Subject: [Python-checkins] r84873 - in python/branches/release27-maint: Lib/distutils/tests/test_dir_util.py Message-ID: <20100918025849.CDAD4EE9D6@mail.python.org> Author: senthil.kumaran Date: Sat Sep 18 04:58:49 2010 New Revision: 84873 Log: Merged revisions 84871 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84871 | senthil.kumaran | 2010-09-18 08:25:03 +0530 (Sat, 18 Sep 2010) | 3 lines Skip the distutils mode test on Windows OS. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py Modified: python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py (original) +++ python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py Sat Sep 18 04:58:49 2010 @@ -3,6 +3,7 @@ import os import stat import shutil +import sys from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, ensure_relative) @@ -49,6 +50,8 @@ wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + @unittest.skipIf(sys.platform.startswith('win'), + "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): mkpath(self.target, 0o700) self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) From solipsis at pitrou.net Sat Sep 18 05:12:00 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 18 Sep 2010 05:12:00 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84870): sum=9 Message-ID: py3k results for svn r84870 (hg cset fa9aff23fd37) -------------------------------------------------- test_logging leaked [3, 3, 3] references, sum=9 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogk0K95s', '-x'] From python-checkins at python.org Sat Sep 18 05:54:32 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sat, 18 Sep 2010 05:54:32 +0200 (CEST) Subject: [Python-checkins] r84874 - python/branches/py3k/Lib/test/test_logging.py Message-ID: <20100918035432.AEC83EE9C9@mail.python.org> Author: hirokazu.yamamoto Date: Sat Sep 18 05:54:32 2010 New Revision: 84874 Log: Added missing BaseTest.tearDown(self). Fixed refleak. Modified: python/branches/py3k/Lib/test/test_logging.py Modified: python/branches/py3k/Lib/test/test_logging.py ============================================================================== --- python/branches/py3k/Lib/test/test_logging.py (original) +++ python/branches/py3k/Lib/test/test_logging.py Sat Sep 18 05:54:32 2010 @@ -1802,6 +1802,7 @@ def tearDown(self): for fn in self.rmfiles: os.unlink(fn) + BaseTest.tearDown(self) def assertLogFile(self, filename): "Assert a log file is there and register it for deletion" From python-checkins at python.org Sat Sep 18 06:02:52 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sat, 18 Sep 2010 06:02:52 +0200 (CEST) Subject: [Python-checkins] r84875 - in python/branches/py3k/PC: VS7.1/rt.bat VS8.0/rt.bat Message-ID: <20100918040252.F26F8EEA76@mail.python.org> Author: hirokazu.yamamoto Date: Sat Sep 18 06:02:52 2010 New Revision: 84875 Log: Applied r84870 to older compilers. Modified: python/branches/py3k/PC/VS7.1/rt.bat python/branches/py3k/PC/VS8.0/rt.bat Modified: python/branches/py3k/PC/VS7.1/rt.bat ============================================================================== --- python/branches/py3k/PC/VS7.1/rt.bat (original) +++ python/branches/py3k/PC/VS7.1/rt.bat Sat Sep 18 06:02:52 2010 @@ -27,7 +27,7 @@ set exe=python set qmode= set dashO= -PATH %PATH%;..\..\..\tcltk\bin +PATH %PATH%;%~dp0..\..\..\tcltk\bin :CheckOpts if "%1"=="-O" (set dashO=-O) & shift & goto CheckOpts Modified: python/branches/py3k/PC/VS8.0/rt.bat ============================================================================== --- python/branches/py3k/PC/VS8.0/rt.bat (original) +++ python/branches/py3k/PC/VS8.0/rt.bat Sat Sep 18 06:02:52 2010 @@ -27,7 +27,7 @@ set exe=python set qmode= set dashO= -PATH %PATH%;..\..\..\tcltk\bin +PATH %PATH%;%~dp0..\..\..\tcltk\bin :CheckOpts if "%1"=="-O" (set dashO=-O) & shift & goto CheckOpts From python-checkins at python.org Sat Sep 18 06:42:42 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sat, 18 Sep 2010 06:42:42 +0200 (CEST) Subject: [Python-checkins] r84876 - python/branches/py3k/Lib/test/test_tcl.py Message-ID: <20100918044242.3E230EE9A3@mail.python.org> Author: hirokazu.yamamoto Date: Sat Sep 18 06:42:41 2010 New Revision: 84876 Log: In Python3000, Tkinter was renamed to tkinter. And print is now function. Modified: python/branches/py3k/Lib/test/test_tcl.py Modified: python/branches/py3k/Lib/test/test_tcl.py ============================================================================== --- python/branches/py3k/Lib/test/test_tcl.py (original) +++ python/branches/py3k/Lib/test/test_tcl.py Sat Sep 18 06:42:41 2010 @@ -145,9 +145,9 @@ with support.EnvironmentVarGuard() as env: env.unset("TCL_LIBRARY") - f = os.popen('%s -c "import Tkinter; print Tkinter"' % (unc_name,)) + f = os.popen('%s -c "import tkinter; print(tkinter)"' % (unc_name,)) - self.assert_('Tkinter.py' in f.read()) + self.assertIn('tkinter', f.read()) # exit code must be zero self.assertEqual(f.close(), None) From python-checkins at python.org Sat Sep 18 07:40:44 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sat, 18 Sep 2010 07:40:44 +0200 (CEST) Subject: [Python-checkins] r84877 - python/branches/release27-maint/Lib/posixpath.py Message-ID: <20100918054044.DF551EE9C9@mail.python.org> Author: hirokazu.yamamoto Date: Sat Sep 18 07:40:44 2010 New Revision: 84877 Log: Added missing import. Modified: python/branches/release27-maint/Lib/posixpath.py Modified: python/branches/release27-maint/Lib/posixpath.py ============================================================================== --- python/branches/release27-maint/Lib/posixpath.py (original) +++ python/branches/release27-maint/Lib/posixpath.py Sat Sep 18 07:40:44 2010 @@ -11,6 +11,7 @@ """ import os +import sys import stat import genericpath import warnings From python-checkins at python.org Sat Sep 18 19:56:03 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 18 Sep 2010 19:56:03 +0200 (CEST) Subject: [Python-checkins] r84878 - in python/branches/py3k: Lib/test/test_subprocess.py Misc/NEWS Message-ID: <20100918175603.25450EEA16@mail.python.org> Author: antoine.pitrou Date: Sat Sep 18 19:56:02 2010 New Revision: 84878 Log: Issue #9894: Do not hardcode ENOENT in test_subprocess. (GNU/Hurd is not dead) Modified: python/branches/py3k/Lib/test/test_subprocess.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/test/test_subprocess.py ============================================================================== --- python/branches/py3k/Lib/test/test_subprocess.py (original) +++ python/branches/py3k/Lib/test/test_subprocess.py Sat Sep 18 19:56:02 2010 @@ -530,7 +530,7 @@ subprocess.Popen(['nonexisting_i_hope'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if c.exception.errno != 2: # ignore "no such file" + if c.exception.errno != errno.ENOENT: # ignore "no such file" raise c.exception def test_issue8780(self): Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Sep 18 19:56:02 2010 @@ -181,6 +181,8 @@ Tests ----- +- Issue #9894: Do not hardcode ENOENT in test_subprocess. + - Issue #9315: Added tests for the trace module. Patch by Eli Bendersky. - Issue #9323: Make test.regrtest.__file__ absolute, this was not always the From python-checkins at python.org Sat Sep 18 20:15:33 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 18 Sep 2010 20:15:33 +0200 (CEST) Subject: [Python-checkins] r84879 - in python/branches/release27-maint: Lib/test/test_subprocess.py Misc/NEWS Message-ID: <20100918181533.86DDDEE994@mail.python.org> Author: antoine.pitrou Date: Sat Sep 18 20:15:33 2010 New Revision: 84879 Log: Merged revisions 84878 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84878 | antoine.pitrou | 2010-09-18 19:56:02 +0200 (sam., 18 sept. 2010) | 5 lines Issue #9894: Do not hardcode ENOENT in test_subprocess. (GNU/Hurd is not dead) ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_subprocess.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/test/test_subprocess.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_subprocess.py (original) +++ python/branches/release27-maint/Lib/test/test_subprocess.py Sat Sep 18 20:15:33 2010 @@ -530,7 +530,7 @@ subprocess.Popen(['nonexisting_i_hope'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if c.exception.errno != 2: # ignore "no such file" + if c.exception.errno != errno.ENOENT: # ignore "no such file" raise c.exception def test_handles_closed_on_exception(self): Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sat Sep 18 20:15:33 2010 @@ -357,6 +357,8 @@ Tests ----- +- Issue #9894: Do not hardcode ENOENT in test_subprocess. + - Issue #9323: Make test.regrtest.__file__ absolute, this was not always the case when running profile or trace, for example. From python-checkins at python.org Sat Sep 18 20:16:39 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 18 Sep 2010 20:16:39 +0200 (CEST) Subject: [Python-checkins] r84880 - in python/branches/release31-maint: Lib/test/test_subprocess.py Misc/NEWS Message-ID: <20100918181639.D2989EE999@mail.python.org> Author: antoine.pitrou Date: Sat Sep 18 20:16:39 2010 New Revision: 84880 Log: Merged revisions 84878 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84878 | antoine.pitrou | 2010-09-18 19:56:02 +0200 (sam., 18 sept. 2010) | 5 lines Issue #9894: Do not hardcode ENOENT in test_subprocess. (GNU/Hurd is not dead) ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_subprocess.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/test/test_subprocess.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_subprocess.py (original) +++ python/branches/release31-maint/Lib/test/test_subprocess.py Sat Sep 18 20:16:39 2010 @@ -535,7 +535,7 @@ stderr=subprocess.PIPE) # Windows raises IOError except (IOError, OSError) as err: - if err.errno != 2: # ignore "no such file" + if err.errno != errno.ENOENT: # ignore "no such file" raise def test_issue8780(self): Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sat Sep 18 20:16:39 2010 @@ -590,6 +590,8 @@ Tests ----- +- Issue #9894: Do not hardcode ENOENT in test_subprocess. + - Issue #9315: Added tests for the trace module. Patch by Eli Bendersky. - Issue #7564: Skip test_ioctl if another process is attached to /dev/tty. From python-checkins at python.org Sun Sep 19 00:33:33 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sun, 19 Sep 2010 00:33:33 +0200 (CEST) Subject: [Python-checkins] r84881 - in python/branches/release27-maint: Misc/NEWS PCbuild/bz2.vcproj Message-ID: <20100918223333.CC513EE990@mail.python.org> Author: hirokazu.yamamoto Date: Sun Sep 19 00:33:33 2010 New Revision: 84881 Log: Merged revisions 84851 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84851 | hirokazu.yamamoto | 2010-09-17 02:50:57 +0900 | 3 lines Issue #9810: Compile bzip2 source files in python's project file directly. It used to be built with bzip2's makefile. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/PCbuild/bz2.vcproj Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Sep 19 00:33:33 2010 @@ -332,6 +332,9 @@ Build ----- +- Issue #9810: Compile bzip2 source files in python's project file + directly. It used to be built with bzip2's makefile. + - Issue #941346: Improve the build process under AIX and allow Python to be built as a shared library. Patch by S?bastien Sabl?. Modified: python/branches/release27-maint/PCbuild/bz2.vcproj ============================================================================== --- python/branches/release27-maint/PCbuild/bz2.vcproj (original) +++ python/branches/release27-maint/PCbuild/bz2.vcproj Sun Sep 19 00:33:33 2010 @@ -43,6 +43,7 @@ + + + + + + + + + + + + + + + + + + + + + + From python-checkins at python.org Sun Sep 19 00:35:02 2010 From: python-checkins at python.org (brian.quinlan) Date: Sun, 19 Sep 2010 00:35:02 +0200 (CEST) Subject: [Python-checkins] r84882 - in python/branches/py3k: Doc/library/concurrent.futures.rst Doc/library/someos.rst Lib/concurrent Lib/concurrent/__init__.py Lib/concurrent/futures Lib/concurrent/futures/__init__.py Lib/concurrent/futures/_base.py Lib/concurrent/futures/process.py Lib/concurrent/futures/thread.py Lib/test/test_concurrent_futures.py Message-ID: <20100918223502.661ADEE990@mail.python.org> Author: brian.quinlan Date: Sun Sep 19 00:35:02 2010 New Revision: 84882 Log: Initial implementation of PEP 3148 Added: python/branches/py3k/Doc/library/concurrent.futures.rst python/branches/py3k/Lib/concurrent/ python/branches/py3k/Lib/concurrent/__init__.py python/branches/py3k/Lib/concurrent/futures/ python/branches/py3k/Lib/concurrent/futures/__init__.py python/branches/py3k/Lib/concurrent/futures/_base.py python/branches/py3k/Lib/concurrent/futures/process.py python/branches/py3k/Lib/concurrent/futures/thread.py python/branches/py3k/Lib/test/test_concurrent_futures.py Modified: python/branches/py3k/Doc/library/someos.rst Added: python/branches/py3k/Doc/library/concurrent.futures.rst ============================================================================== --- (empty file) +++ python/branches/py3k/Doc/library/concurrent.futures.rst Sun Sep 19 00:35:02 2010 @@ -0,0 +1,366 @@ +:mod:`concurrent.futures` --- Concurrent computation +==================================================== + +.. module:: concurrent.futures + :synopsis: Execute computations concurrently using threads or processes. + +The :mod:`concurrent.futures` module provides a high-level interface for +asynchronously executing callables. + +The asynchronous execution can be be performed by threads using +:class:`ThreadPoolExecutor` or seperate processes using +:class:`ProcessPoolExecutor`. Both implement the same interface, which is +defined by the abstract :class:`Executor` class. + +Executor Objects +^^^^^^^^^^^^^^^^ + +:class:`Executor` is an abstract class that provides methods to execute calls +asynchronously. It should not be used directly, but through its two +subclasses: :class:`ThreadPoolExecutor` and :class:`ProcessPoolExecutor`. + +.. class:: Executor() + + An abstract class that provides methods to execute calls asynchronously. It + should not be used directly, but through its two subclasses: + :class:`ThreadPoolExecutor` and :class:`ProcessPoolExecutor`. + + .. method:: submit(fn, *args, **kwargs) + + Schedules the callable to be executed as *fn*(*\*args*, *\*\*kwargs*) and + returns a :class:`Future` representing the execution of the callable. + + :: + + with ThreadPoolExecutor(max_workers=1) as executor: + future = executor.submit(pow, 323, 1235) + print(future.result()) + + .. method:: map(func, *iterables, timeout=None) + + Equivalent to `map(*func*, *\*iterables*)` but func is executed + asynchronously and several calls to *func* may be made concurrently. The + returned iterator raises a :exc:`TimeoutError` if :meth:`__next__()` is + called and the result isn't available after *timeout* seconds from the + original call to :meth:`Executor.map()`. *timeout* can be an int or + float. If *timeout* is not specified or ``None`` then there is no limit + to the wait time. If a call raises an exception then that exception will + be raised when its value is retrieved from the iterator. + + .. method:: shutdown(wait=True) + + Signal the executor that it should free any resources that it is using + when the currently pending futures are done executing. Calls to + :meth:`Executor.submit` and :meth:`Executor.map` made after shutdown will + raise :exc:`RuntimeError`. + + If *wait* is `True` then this method will not return until all the + pending futures are done executing and the resources associated with the + executor have been freed. If *wait* is `False` then this method will + return immediately and the resources associated with the executor will + be freed when all pending futures are done executing. Regardless of the + value of *wait*, the entire Python program will not exit until all + pending futures are done executing. + + You can avoid having to call this method explicitly if you use the `with` + statement, which will shutdown the `Executor` (waiting as if + `Executor.shutdown` were called with *wait* set to `True`): + + :: + + import shutil + with ThreadPoolExecutor(max_workers=4) as e: + e.submit(shutil.copy, 'src1.txt', 'dest1.txt') + e.submit(shutil.copy, 'src2.txt', 'dest2.txt') + e.submit(shutil.copy, 'src3.txt', 'dest3.txt') + e.submit(shutil.copy, 'src3.txt', 'dest4.txt') + +ThreadPoolExecutor +^^^^^^^^^^^^^^^^^^ + +The :class:`ThreadPoolExecutor` class is an :class:`Executor` subclass that uses +a pool of threads to execute calls asynchronously. + +Deadlock can occur when the callable associated with a :class:`Future` waits on +the results of another :class:`Future`. For example: + +:: + + import time + def wait_on_b(): + time.sleep(5) + print(b.result()) # b will never complete because it is waiting on a. + return 5 + + def wait_on_a(): + time.sleep(5) + print(a.result()) # a will never complete because it is waiting on b. + return 6 + + + executor = ThreadPoolExecutor(max_workers=2) + a = executor.submit(wait_on_b) + b = executor.submit(wait_on_a) + +And: + +:: + + def wait_on_future(): + f = executor.submit(pow, 5, 2) + # This will never complete because there is only one worker thread and + # it is executing this function. + print(f.result()) + + executor = ThreadPoolExecutor(max_workers=1) + executor.submit(wait_on_future) + + +.. class:: ThreadPoolExecutor(max_workers) + + An :class:`Executor` subclass that uses a pool of at most *max_workers* + threads to execute calls asynchronously. + + Deadlock can occur when the callable associated with a :class:`Future` waits + on the results of another :class:`Future`. + +.. _threadpoolexecutor-example: + +ThreadPoolExecutor Example +^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + import concurrent.futures + import urllib.request + + URLS = ['http://www.foxnews.com/', + 'http://www.cnn.com/', + 'http://europe.wsj.com/', + 'http://www.bbc.co.uk/', + 'http://some-made-up-domain.com/'] + + def load_url(url, timeout): + return urllib.request.urlopen(url, timeout=timeout).read() + + with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: + future_to_url = dict((executor.submit(load_url, url, 60), url) + for url in URLS) + + for future in concurrent.futures.as_completed(future_to_url): + url = future_to_url[future] + if future.exception() is not None: + print('%r generated an exception: %s' % (url, + future.exception())) + else: + print('%r page is %d bytes' % (url, len(future.result()))) + + +ProcessPoolExecutor +^^^^^^^^^^^^^^^^^^^ + +The :class:`ProcessPoolExecutor` class is an :class:`Executor` subclass that +uses a pool of processes to execute calls asynchronously. +:class:`ProcessPoolExecutor` uses the :mod:`multiprocessing` module, which +allows it to side-step the :term:`Global Interpreter Lock` but also means that +only picklable objects can be executed and returned. + +Calling :class:`Executor` or :class:`Future` methods from a callable submitted +to a :class:`ProcessPoolExecutor` will result in deadlock. + +.. class:: ProcessPoolExecutor(max_workers=None) + + An :class:`Executor` subclass that executes calls asynchronously using a + pool of at most *max_workers* processes. If *max_workers* is ``None`` or + not given then as many worker processes will be created as the machine has + processors. + +.. _processpoolexecutor-example: + +ProcessPoolExecutor Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + import concurrent.futures + import math + + PRIMES = [ + 112272535095293, + 112582705942171, + 112272535095293, + 115280095190773, + 115797848077099, + 1099726899285419] + + def is_prime(n): + if n % 2 == 0: + return False + + sqrt_n = int(math.floor(math.sqrt(n))) + for i in range(3, sqrt_n + 1, 2): + if n % i == 0: + return False + return True + + def main(): + with concurrent.futures.ProcessPoolExecutor() as executor: + for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)): + print('%d is prime: %s' % (number, prime)) + + if __name__ == '__main__': + main() + +Future Objects +^^^^^^^^^^^^^^ + +The :class:`Future` class encapulates the asynchronous execution of a callable. +:class:`Future` instances are created by :meth:`Executor.submit`. + +.. class:: Future() + + Encapulates the asynchronous execution of a callable. :class:`Future` + instances are created by :meth:`Executor.submit` and should not be created + directly except for testing. + + .. method:: cancel() + + Attempt to cancel the call. If the call is currently being executed then + it cannot be cancelled and the method will return `False`, otherwise the + call will be cancelled and the method will return `True`. + + .. method:: cancelled() + + Return `True` if the call was successfully cancelled. + + .. method:: running() + + Return `True` if the call is currently being executed and cannot be + cancelled. + + .. method:: done() + + Return `True` if the call was successfully cancelled or finished running. + + .. method:: result(timeout=None) + + Return the value returned by the call. If the call hasn't yet completed + then this method will wait up to *timeout* seconds. If the call hasn't + completed in *timeout* seconds then a :exc:`TimeoutError` will be + raised. *timeout* can be an int or float.If *timeout* is not specified + or ``None`` then there is no limit to the wait time. + + If the future is cancelled before completing then :exc:`CancelledError` + will be raised. + + If the call raised then this method will raise the same exception. + + .. method:: exception(timeout=None) + + Return the exception raised by the call. If the call hasn't yet completed + then this method will wait up to *timeout* seconds. If the call hasn't + completed in *timeout* seconds then a :exc:`TimeoutError` will be raised. + *timeout* can be an int or float. If *timeout* is not specified or + ``None`` then there is no limit to the wait time. + + If the future is cancelled before completing then :exc:`CancelledError` + will be raised. + + If the call completed without raising then ``None`` is returned. + + .. method:: add_done_callback(fn) + + Attaches the callable *fn* to the future. *fn* will be called, with the + future as its only argument, when the future is cancelled or finishes + running. + + Added callables are called in the order that they were added and are + always called in a thread belonging to the process that added them. If + the callable raises an :exc:`Exception` then it will be logged and + ignored. If the callable raises another :exc:`BaseException` then the + behavior is not defined. + + If the future has already completed or been cancelled then *fn* will be + called immediately. + + The following :class:`Future` methods are meant for use in unit tests and + :class:`Executor` implementations. + + .. method:: set_running_or_notify_cancel() + + This method should only be called by :class:`Executor` implementations + before executing the work associated with the :class:`Future` and by + unit tests. + + If the method returns `False` then the :class:`Future` was cancelled i.e. + :meth:`Future.cancel` was called and returned `True`. Any threads waiting + on the :class:`Future` completing (i.e. through :func:`as_completed` or + :func:`wait`) will be woken up. + + If the method returns `True` then the :class:`Future` was not cancelled + and has been put in the running state i.e. calls to + :meth:`Future.running` will return `True`. + + This method can only be called once and cannot be called after + :meth:`Future.set_result` or :meth:`Future.set_exception` have been + called. + + .. method:: set_result(result) + + Sets the result of the work associated with the :class:`Future` to + *result*. + + This method should only be used by :class:`Executor` implementations and + unit tests. + + .. method:: set_exception(exception) + + Sets the result of the work associated with the :class:`Future` to the + :class:`Exception` *exception*. + + This method should only be used by :class:`Executor` implementations and + unit tests. + + +Module Functions +^^^^^^^^^^^^^^^^ + +.. function:: wait(fs, timeout=None, return_when=ALL_COMPLETED) + + Wait for the :class:`Future` instances (possibly created by different + :class:`Executor` instances) given by *fs* to complete. Returns a named + 2-tuple of sets. The first set, named "done", contains the futures that + completed (finished or were cancelled) before the wait completed. The second + set, named "not_done", contains uncompleted futures. + + *timeout* can be used to control the maximum number of seconds to wait before + returning. *timeout* can be an int or float. If *timeout* is not specified or + ``None`` then there is no limit to the wait time. + + *return_when* indicates when this function should return. It must be one of + the following constants: + + +-----------------------------+----------------------------------------+ + | Constant | Description | + +=============================+========================================+ + | :const:`FIRST_COMPLETED` | The function will return when any | + | | future finishes or is cancelled. | + +-----------------------------+----------------------------------------+ + | :const:`FIRST_EXCEPTION` | The function will return when any | + | | future finishes by raising an | + | | exception. If no future raises an | + | | exception then it is equivalent to | + | | `ALL_COMPLETED`. | + +-----------------------------+----------------------------------------+ + | :const:`ALL_COMPLETED` | The function will return when all | + | | futures finish or are cancelled. | + +-----------------------------+----------------------------------------+ + +.. function:: as_completed(fs, timeout=None) + + Returns an iterator over the :class:`Future` instances (possibly created + by different :class:`Executor` instances) given by *fs* that yields futures + as they complete (finished or were cancelled). Any futures that completed + before :func:`as_completed()` was called will be yielded first. The returned + iterator raises a :exc:`TimeoutError` if :meth:`__next__()` is called and + the result isn't available after *timeout* seconds from the original call + to :func:`as_completed()`. *timeout* can be an int or float. If *timeout* + is not specified or ``None`` then there is no limit to the wait time. Modified: python/branches/py3k/Doc/library/someos.rst ============================================================================== --- python/branches/py3k/Doc/library/someos.rst (original) +++ python/branches/py3k/Doc/library/someos.rst Sun Sep 19 00:35:02 2010 @@ -17,6 +17,7 @@ dummy_threading.rst _thread.rst _dummy_thread.rst + concurrent.futures.rst multiprocessing.rst mmap.rst readline.rst Added: python/branches/py3k/Lib/concurrent/__init__.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/concurrent/__init__.py Sun Sep 19 00:35:02 2010 @@ -0,0 +1 @@ +# This directory is a Python package. Added: python/branches/py3k/Lib/concurrent/futures/__init__.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/concurrent/futures/__init__.py Sun Sep 19 00:35:02 2010 @@ -0,0 +1,18 @@ +# Copyright 2009 Brian Quinlan. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Execute computations asynchronously using threads or processes.""" + +__author__ = 'Brian Quinlan (brian at sweetapp.com)' + +from concurrent.futures._base import (FIRST_COMPLETED, + FIRST_EXCEPTION, + ALL_COMPLETED, + CancelledError, + TimeoutError, + Future, + Executor, + wait, + as_completed) +from concurrent.futures.process import ProcessPoolExecutor +from concurrent.futures.thread import ThreadPoolExecutor Added: python/branches/py3k/Lib/concurrent/futures/_base.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/concurrent/futures/_base.py Sun Sep 19 00:35:02 2010 @@ -0,0 +1,541 @@ +# Copyright 2009 Brian Quinlan. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +__author__ = 'Brian Quinlan (brian at sweetapp.com)' + +import collections +import functools +import logging +import threading +import time + +FIRST_COMPLETED = 'FIRST_COMPLETED' +FIRST_EXCEPTION = 'FIRST_EXCEPTION' +ALL_COMPLETED = 'ALL_COMPLETED' + +# Possible future states (for internal use by the futures package). +PENDING = 'PENDING' +RUNNING = 'RUNNING' +# The future was cancelled by the user... +CANCELLED = 'CANCELLED' +# ...and _Waiter.add_cancelled() was called by a worker. +CANCELLED_AND_NOTIFIED = 'CANCELLED_AND_NOTIFIED' +FINISHED = 'FINISHED' + +_FUTURE_STATES = [ + PENDING, + RUNNING, + CANCELLED, + CANCELLED_AND_NOTIFIED, + FINISHED +] + +_STATE_TO_DESCRIPTION_MAP = { + PENDING: "pending", + RUNNING: "running", + CANCELLED: "cancelled", + CANCELLED_AND_NOTIFIED: "cancelled", + FINISHED: "finished" +} + +# Logger for internal use by the futures package. +LOGGER = logging.getLogger("concurrent.futures") +_handler = logging.StreamHandler() +LOGGER.addHandler(_handler) +del _handler + +class Error(Exception): + """Base class for all future-related exceptions.""" + pass + +class CancelledError(Error): + """The Future was cancelled.""" + pass + +class TimeoutError(Error): + """The operation exceeded the given deadline.""" + pass + +class _Waiter(object): + """Provides the event that wait() and as_completed() block on.""" + def __init__(self): + self.event = threading.Event() + self.finished_futures = [] + + def add_result(self, future): + self.finished_futures.append(future) + + def add_exception(self, future): + self.finished_futures.append(future) + + def add_cancelled(self, future): + self.finished_futures.append(future) + +class _FirstCompletedWaiter(_Waiter): + """Used by wait(return_when=FIRST_COMPLETED) and as_completed().""" + + def add_result(self, future): + super().add_result(future) + self.event.set() + + def add_exception(self, future): + super().add_exception(future) + self.event.set() + + def add_cancelled(self, future): + super().add_cancelled(future) + self.event.set() + +class _AllCompletedWaiter(_Waiter): + """Used by wait(return_when=FIRST_EXCEPTION and ALL_COMPLETED).""" + + def __init__(self, num_pending_calls, stop_on_exception): + self.num_pending_calls = num_pending_calls + self.stop_on_exception = stop_on_exception + super().__init__() + + def _decrement_pending_calls(self): + self.num_pending_calls -= 1 + if not self.num_pending_calls: + self.event.set() + + def add_result(self, future): + super().add_result(future) + self._decrement_pending_calls() + + def add_exception(self, future): + super().add_exception(future) + if self.stop_on_exception: + self.event.set() + else: + self._decrement_pending_calls() + + def add_cancelled(self, future): + super().add_cancelled(future) + self._decrement_pending_calls() + +class _AcquireFutures(object): + """A context manager that does an ordered acquire of Future conditions.""" + + def __init__(self, futures): + self.futures = sorted(futures, key=id) + + def __enter__(self): + for future in self.futures: + future._condition.acquire() + + def __exit__(self, *args): + for future in self.futures: + future._condition.release() + +def _create_and_install_waiters(fs, return_when): + if return_when == FIRST_COMPLETED: + waiter = _FirstCompletedWaiter() + else: + pending_count = sum( + f._state not in [CANCELLED_AND_NOTIFIED, FINISHED] for f in fs) + + if return_when == FIRST_EXCEPTION: + waiter = _AllCompletedWaiter(pending_count, stop_on_exception=True) + elif return_when == ALL_COMPLETED: + waiter = _AllCompletedWaiter(pending_count, stop_on_exception=False) + else: + raise ValueError("Invalid return condition: %r" % return_when) + + for f in fs: + f._waiters.append(waiter) + + return waiter + +def as_completed(fs, timeout=None): + """An iterator over the given futures that yields each as it completes. + + Args: + fs: The sequence of Futures (possibly created by different Executors) to + iterate over. + timeout: The maximum number of seconds to wait. If None, then there + is no limit on the wait time. + + Returns: + An iterator that yields the given Futures as they complete (finished or + cancelled). + + Raises: + TimeoutError: If the entire result iterator could not be generated + before the given timeout. + """ + if timeout is not None: + end_time = timeout + time.time() + + with _AcquireFutures(fs): + finished = set( + f for f in fs + if f._state in [CANCELLED_AND_NOTIFIED, FINISHED]) + pending = set(fs) - finished + waiter = _create_and_install_waiters(fs, FIRST_COMPLETED) + + try: + for future in finished: + yield future + + while pending: + if timeout is None: + wait_timeout = None + else: + wait_timeout = end_time - time.time() + if wait_timeout < 0: + raise TimeoutError( + '%d (of %d) futures unfinished' % ( + len(pending), len(fs))) + + waiter.event.wait(timeout) + + for future in waiter.finished_futures[:]: + yield future + waiter.finished_futures.remove(future) + pending.remove(future) + + finally: + for f in fs: + f._waiters.remove(waiter) + +DoneAndNotDoneFutures = collections.namedtuple( + 'DoneAndNotDoneFutures', 'done not_done') +def wait(fs, timeout=None, return_when=ALL_COMPLETED): + """Wait for the futures in the given sequence to complete. + + Args: + fs: The sequence of Futures (possibly created by different Executors) to + wait upon. + timeout: The maximum number of seconds to wait. If None, then there + is no limit on the wait time. + return_when: Indicates when this function should return. The options + are: + + FIRST_COMPLETED - Return when any future finishes or is + cancelled. + FIRST_EXCEPTION - Return when any future finishes by raising an + exception. If no future raises an exception + then it is equivalent to ALL_COMPLETED. + ALL_COMPLETED - Return when all futures finish or are cancelled. + + Returns: + A named 2-tuple of sets. The first set, named 'done', contains the + futures that completed (is finished or cancelled) before the wait + completed. The second set, named 'not_done', contains uncompleted + futures. + """ + with _AcquireFutures(fs): + done = set(f for f in fs + if f._state in [CANCELLED_AND_NOTIFIED, FINISHED]) + not_done = set(fs) - done + + if (return_when == FIRST_COMPLETED) and done: + return DoneAndNotDoneFutures(done, not_done) + elif (return_when == FIRST_EXCEPTION) and done: + if any(f for f in done + if not f.cancelled() and f.exception() is not None): + return DoneAndNotDoneFutures(done, not_done) + + if len(done) == len(fs): + return DoneAndNotDoneFutures(done, not_done) + + waiter = _create_and_install_waiters(fs, return_when) + + waiter.event.wait(timeout) + for f in fs: + f._waiters.remove(waiter) + + done.update(waiter.finished_futures) + return DoneAndNotDoneFutures(done, set(fs) - done) + +class Future(object): + """Represents the result of an asynchronous computation.""" + + def __init__(self): + """Initializes the future. Should not be called by clients.""" + self._condition = threading.Condition() + self._state = PENDING + self._result = None + self._exception = None + self._waiters = [] + self._done_callbacks = [] + + def _invoke_callbacks(self): + for callback in self._done_callbacks: + try: + callback(self) + except Exception: + LOGGER.exception('exception calling callback for %r', self) + + def __repr__(self): + with self._condition: + if self._state == FINISHED: + if self._exception: + return '' % ( + hex(id(self)), + _STATE_TO_DESCRIPTION_MAP[self._state], + self._exception.__class__.__name__) + else: + return '' % ( + hex(id(self)), + _STATE_TO_DESCRIPTION_MAP[self._state], + self._result.__class__.__name__) + return '' % ( + hex(id(self)), + _STATE_TO_DESCRIPTION_MAP[self._state]) + + def cancel(self): + """Cancel the future if possible. + + Returns True if the future was cancelled, False otherwise. A future + cannot be cancelled if it is running or has already completed. + """ + with self._condition: + if self._state in [RUNNING, FINISHED]: + return False + + if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: + return True + + self._state = CANCELLED + self._condition.notify_all() + + self._invoke_callbacks() + return True + + def cancelled(self): + """Return True if the future has cancelled.""" + with self._condition: + return self._state in [CANCELLED, CANCELLED_AND_NOTIFIED] + + def running(self): + """Return True if the future is currently executing.""" + with self._condition: + return self._state == RUNNING + + def done(self): + """Return True of the future was cancelled or finished executing.""" + with self._condition: + return self._state in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED] + + def __get_result(self): + if self._exception: + raise self._exception + else: + return self._result + + def add_done_callback(self, fn): + """Attaches a callable that will be called when the future finishes. + + Args: + fn: A callable that will be called with this future as its only + argument when the future completes or is cancelled. The callable + will always be called by a thread in the same process in which + it was added. If the future has already completed or been + cancelled then the callable will be called immediately. These + callables are called in the order that they were added. + """ + with self._condition: + if self._state not in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]: + self._done_callbacks.append(fn) + return + fn(self) + + def result(self, timeout=None): + """Return the result of the call that the future represents. + + Args: + timeout: The number of seconds to wait for the result if the future + isn't done. If None, then there is no limit on the wait time. + + Returns: + The result of the call that the future represents. + + Raises: + CancelledError: If the future was cancelled. + TimeoutError: If the future didn't finish executing before the given + timeout. + Exception: If the call raised then that exception will be raised. + """ + with self._condition: + if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: + raise CancelledError() + elif self._state == FINISHED: + return self.__get_result() + + self._condition.wait(timeout) + + if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: + raise CancelledError() + elif self._state == FINISHED: + return self.__get_result() + else: + raise TimeoutError() + + def exception(self, timeout=None): + """Return the exception raised by the call that the future represents. + + Args: + timeout: The number of seconds to wait for the exception if the + future isn't done. If None, then there is no limit on the wait + time. + + Returns: + The exception raised by the call that the future represents or None + if the call completed without raising. + + Raises: + CancelledError: If the future was cancelled. + TimeoutError: If the future didn't finish executing before the given + timeout. + """ + + with self._condition: + if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: + raise CancelledError() + elif self._state == FINISHED: + return self._exception + + self._condition.wait(timeout) + + if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: + raise CancelledError() + elif self._state == FINISHED: + return self._exception + else: + raise TimeoutError() + + # The following methods should only be used by Executors and in tests. + def set_running_or_notify_cancel(self): + """Mark the future as running or process any cancel notifications. + + Should only be used by Executor implementations and unit tests. + + If the future has been cancelled (cancel() was called and returned + True) then any threads waiting on the future completing (though calls + to as_completed() or wait()) are notified and False is returned. + + If the future was not cancelled then it is put in the running state + (future calls to running() will return True) and True is returned. + + This method should be called by Executor implementations before + executing the work associated with this future. If this method returns + False then the work should not be executed. + + Returns: + False if the Future was cancelled, True otherwise. + + Raises: + RuntimeError: if this method was already called or if set_result() + or set_exception() was called. + """ + with self._condition: + if self._state == CANCELLED: + self._state = CANCELLED_AND_NOTIFIED + for waiter in self._waiters: + waiter.add_cancelled(self) + # self._condition.notify_all() is not necessary because + # self.cancel() triggers a notification. + return False + elif self._state == PENDING: + self._state = RUNNING + return True + else: + LOGGER.critical('Future %s in unexpected state: %s', + id(self.future), + self.future._state) + raise RuntimeError('Future in unexpected state') + + def set_result(self, result): + """Sets the return value of work associated with the future. + + Should only be used by Executor implementations and unit tests. + """ + with self._condition: + self._result = result + self._state = FINISHED + for waiter in self._waiters: + waiter.add_result(self) + self._condition.notify_all() + self._invoke_callbacks() + + def set_exception(self, exception): + """Sets the result of the future as being the given exception. + + Should only be used by Executor implementations and unit tests. + """ + with self._condition: + self._exception = exception + self._state = FINISHED + for waiter in self._waiters: + waiter.add_exception(self) + self._condition.notify_all() + self._invoke_callbacks() + +class Executor(object): + """This is an abstract base class for concrete asynchronous executors.""" + + def submit(self, fn, *args, **kwargs): + """Submits a callable to be executed with the given arguments. + + Schedules the callable to be executed as fn(*args, **kwargs) and returns + a Future instance representing the execution of the callable. + + Returns: + A Future representing the given call. + """ + raise NotImplementedError() + + def map(self, fn, *iterables, timeout=None): + """Returns a iterator equivalent to map(fn, iter). + + Args: + fn: A callable that will take take as many arguments as there are + passed iterables. + timeout: The maximum number of seconds to wait. If None, then there + is no limit on the wait time. + + Returns: + An iterator equivalent to: map(func, *iterables) but the calls may + be evaluated out-of-order. + + Raises: + TimeoutError: If the entire result iterator could not be generated + before the given timeout. + Exception: If fn(*args) raises for any values. + """ + if timeout is not None: + end_time = timeout + time.time() + + fs = [self.submit(fn, *args) for args in zip(*iterables)] + + try: + for future in fs: + if timeout is None: + yield future.result() + else: + yield future.result(end_time - time.time()) + finally: + for future in fs: + future.cancel() + + def shutdown(self, wait=True): + """Clean-up the resources associated with the Executor. + + It is safe to call this method several times. Otherwise, no other + methods can be called after this one. + + Args: + wait: If True then shutdown will not return until all running + futures have finished executing and the resources used by the + executor have been reclaimed. + """ + pass + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.shutdown(wait=True) + return False Added: python/branches/py3k/Lib/concurrent/futures/process.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/concurrent/futures/process.py Sun Sep 19 00:35:02 2010 @@ -0,0 +1,337 @@ +# Copyright 2009 Brian Quinlan. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Implements ProcessPoolExecutor. + +The follow diagram and text describe the data-flow through the system: + +|======================= In-process =====================|== Out-of-process ==| + ++----------+ +----------+ +--------+ +-----------+ +---------+ +| | => | Work Ids | => | | => | Call Q | => | | +| | +----------+ | | +-----------+ | | +| | | ... | | | | ... | | | +| | | 6 | | | | 5, call() | | | +| | | 7 | | | | ... | | | +| Process | | ... | | Local | +-----------+ | Process | +| Pool | +----------+ | Worker | | #1..n | +| Executor | | Thread | | | +| | +----------- + | | +-----------+ | | +| | <=> | Work Items | <=> | | <= | Result Q | <= | | +| | +------------+ | | +-----------+ | | +| | | 6: call() | | | | ... | | | +| | | future | | | | 4, result | | | +| | | ... | | | | 3, except | | | ++----------+ +------------+ +--------+ +-----------+ +---------+ + +Executor.submit() called: +- creates a uniquely numbered _WorkItem and adds it to the "Work Items" dict +- adds the id of the _WorkItem to the "Work Ids" queue + +Local worker thread: +- reads work ids from the "Work Ids" queue and looks up the corresponding + WorkItem from the "Work Items" dict: if the work item has been cancelled then + it is simply removed from the dict, otherwise it is repackaged as a + _CallItem and put in the "Call Q". New _CallItems are put in the "Call Q" + until "Call Q" is full. NOTE: the size of the "Call Q" is kept small because + calls placed in the "Call Q" can no longer be cancelled with Future.cancel(). +- reads _ResultItems from "Result Q", updates the future stored in the + "Work Items" dict and deletes the dict entry + +Process #1..n: +- reads _CallItems from "Call Q", executes the calls, and puts the resulting + _ResultItems in "Request Q" +""" + +__author__ = 'Brian Quinlan (brian at sweetapp.com)' + +import atexit +from concurrent.futures import _base +import queue +import multiprocessing +import threading +import weakref + +# Workers are created as daemon threads and processes. This is done to allow the +# interpreter to exit when there are still idle processes in a +# ProcessPoolExecutor's process pool (i.e. shutdown() was not called). However, +# allowing workers to die with the interpreter has two undesirable properties: +# - The workers would still be running during interpretor shutdown, +# meaning that they would fail in unpredictable ways. +# - The workers could be killed while evaluating a work item, which could +# be bad if the callable being evaluated has external side-effects e.g. +# writing to a file. +# +# To work around this problem, an exit handler is installed which tells the +# workers to exit when their work queues are empty and then waits until the +# threads/processes finish. + +_thread_references = set() +_shutdown = False + +def _python_exit(): + global _shutdown + _shutdown = True + for thread_reference in _thread_references: + thread = thread_reference() + if thread is not None: + thread.join() + +def _remove_dead_thread_references(): + """Remove inactive threads from _thread_references. + + Should be called periodically to prevent memory leaks in scenarios such as: + >>> while True: + >>> ... t = ThreadPoolExecutor(max_workers=5) + >>> ... t.map(int, ['1', '2', '3', '4', '5']) + """ + for thread_reference in set(_thread_references): + if thread_reference() is None: + _thread_references.discard(thread_reference) + +# Controls how many more calls than processes will be queued in the call queue. +# A smaller number will mean that processes spend more time idle waiting for +# work while a larger number will make Future.cancel() succeed less frequently +# (Futures in the call queue cannot be cancelled). +EXTRA_QUEUED_CALLS = 1 + +class _WorkItem(object): + def __init__(self, future, fn, args, kwargs): + self.future = future + self.fn = fn + self.args = args + self.kwargs = kwargs + +class _ResultItem(object): + def __init__(self, work_id, exception=None, result=None): + self.work_id = work_id + self.exception = exception + self.result = result + +class _CallItem(object): + def __init__(self, work_id, fn, args, kwargs): + self.work_id = work_id + self.fn = fn + self.args = args + self.kwargs = kwargs + +def _process_worker(call_queue, result_queue, shutdown): + """Evaluates calls from call_queue and places the results in result_queue. + + This worker is run in a seperate process. + + Args: + call_queue: A multiprocessing.Queue of _CallItems that will be read and + evaluated by the worker. + result_queue: A multiprocessing.Queue of _ResultItems that will written + to by the worker. + shutdown: A multiprocessing.Event that will be set as a signal to the + worker that it should exit when call_queue is empty. + """ + while True: + try: + call_item = call_queue.get(block=True, timeout=0.1) + except queue.Empty: + if shutdown.is_set(): + return + else: + try: + r = call_item.fn(*call_item.args, **call_item.kwargs) + except BaseException as e: + result_queue.put(_ResultItem(call_item.work_id, + exception=e)) + else: + result_queue.put(_ResultItem(call_item.work_id, + result=r)) + +def _add_call_item_to_queue(pending_work_items, + work_ids, + call_queue): + """Fills call_queue with _WorkItems from pending_work_items. + + This function never blocks. + + Args: + pending_work_items: A dict mapping work ids to _WorkItems e.g. + {5: <_WorkItem...>, 6: <_WorkItem...>, ...} + work_ids: A queue.Queue of work ids e.g. Queue([5, 6, ...]). Work ids + are consumed and the corresponding _WorkItems from + pending_work_items are transformed into _CallItems and put in + call_queue. + call_queue: A multiprocessing.Queue that will be filled with _CallItems + derived from _WorkItems. + """ + while True: + if call_queue.full(): + return + try: + work_id = work_ids.get(block=False) + except queue.Empty: + return + else: + work_item = pending_work_items[work_id] + + if work_item.future.set_running_or_notify_cancel(): + call_queue.put(_CallItem(work_id, + work_item.fn, + work_item.args, + work_item.kwargs), + block=True) + else: + del pending_work_items[work_id] + continue + +def _queue_manangement_worker(executor_reference, + processes, + pending_work_items, + work_ids_queue, + call_queue, + result_queue, + shutdown_process_event): + """Manages the communication between this process and the worker processes. + + This function is run in a local thread. + + Args: + executor_reference: A weakref.ref to the ProcessPoolExecutor that owns + this thread. Used to determine if the ProcessPoolExecutor has been + garbage collected and that this function can exit. + process: A list of the multiprocessing.Process instances used as + workers. + pending_work_items: A dict mapping work ids to _WorkItems e.g. + {5: <_WorkItem...>, 6: <_WorkItem...>, ...} + work_ids_queue: A queue.Queue of work ids e.g. Queue([5, 6, ...]). + call_queue: A multiprocessing.Queue that will be filled with _CallItems + derived from _WorkItems for processing by the process workers. + result_queue: A multiprocessing.Queue of _ResultItems generated by the + process workers. + shutdown_process_event: A multiprocessing.Event used to signal the + process workers that they should exit when their work queue is + empty. + """ + while True: + _add_call_item_to_queue(pending_work_items, + work_ids_queue, + call_queue) + + try: + result_item = result_queue.get(block=True, timeout=0.1) + except queue.Empty: + executor = executor_reference() + # No more work items can be added if: + # - The interpreter is shutting down OR + # - The executor that owns this worker has been collected OR + # - The executor that owns this worker has been shutdown. + if _shutdown or executor is None or executor._shutdown_thread: + # Since no new work items can be added, it is safe to shutdown + # this thread if there are no pending work items. + if not pending_work_items: + shutdown_process_event.set() + + # If .join() is not called on the created processes then + # some multiprocessing.Queue methods may deadlock on Mac OS + # X. + for p in processes: + p.join() + return + del executor + else: + work_item = pending_work_items[result_item.work_id] + del pending_work_items[result_item.work_id] + + if result_item.exception: + work_item.future.set_exception(result_item.exception) + else: + work_item.future.set_result(result_item.result) + +class ProcessPoolExecutor(_base.Executor): + def __init__(self, max_workers=None): + """Initializes a new ProcessPoolExecutor instance. + + Args: + max_workers: The maximum number of processes that can be used to + execute the given calls. If None or not given then as many + worker processes will be created as the machine has processors. + """ + _remove_dead_thread_references() + + if max_workers is None: + self._max_workers = multiprocessing.cpu_count() + else: + self._max_workers = max_workers + + # Make the call queue slightly larger than the number of processes to + # prevent the worker processes from idling. But don't make it too big + # because futures in the call queue cannot be cancelled. + self._call_queue = multiprocessing.Queue(self._max_workers + + EXTRA_QUEUED_CALLS) + self._result_queue = multiprocessing.Queue() + self._work_ids = queue.Queue() + self._queue_management_thread = None + self._processes = set() + + # Shutdown is a two-step process. + self._shutdown_thread = False + self._shutdown_process_event = multiprocessing.Event() + self._shutdown_lock = threading.Lock() + self._queue_count = 0 + self._pending_work_items = {} + + def _start_queue_management_thread(self): + if self._queue_management_thread is None: + self._queue_management_thread = threading.Thread( + target=_queue_manangement_worker, + args=(weakref.ref(self), + self._processes, + self._pending_work_items, + self._work_ids, + self._call_queue, + self._result_queue, + self._shutdown_process_event)) + self._queue_management_thread.daemon = True + self._queue_management_thread.start() + _thread_references.add(weakref.ref(self._queue_management_thread)) + + def _adjust_process_count(self): + for _ in range(len(self._processes), self._max_workers): + p = multiprocessing.Process( + target=_process_worker, + args=(self._call_queue, + self._result_queue, + self._shutdown_process_event)) + p.start() + self._processes.add(p) + + def submit(self, fn, *args, **kwargs): + with self._shutdown_lock: + if self._shutdown_thread: + raise RuntimeError('cannot schedule new futures after shutdown') + + f = _base.Future() + w = _WorkItem(f, fn, args, kwargs) + + self._pending_work_items[self._queue_count] = w + self._work_ids.put(self._queue_count) + self._queue_count += 1 + + self._start_queue_management_thread() + self._adjust_process_count() + return f + submit.__doc__ = _base.Executor.submit.__doc__ + + def shutdown(self, wait=True): + with self._shutdown_lock: + self._shutdown_thread = True + if wait: + if self._queue_management_thread: + self._queue_management_thread.join() + # To reduce the risk of openning too many files, remove references to + # objects that use file descriptors. + self._queue_management_thread = None + self._call_queue = None + self._result_queue = None + self._shutdown_process_event = None + self._processes = None + shutdown.__doc__ = _base.Executor.shutdown.__doc__ + +atexit.register(_python_exit) Added: python/branches/py3k/Lib/concurrent/futures/thread.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/concurrent/futures/thread.py Sun Sep 19 00:35:02 2010 @@ -0,0 +1,136 @@ +# Copyright 2009 Brian Quinlan. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Implements ThreadPoolExecutor.""" + +__author__ = 'Brian Quinlan (brian at sweetapp.com)' + +import atexit +from concurrent.futures import _base +import queue +import threading +import weakref + +# Workers are created as daemon threads. This is done to allow the interpreter +# to exit when there are still idle threads in a ThreadPoolExecutor's thread +# pool (i.e. shutdown() was not called). However, allowing workers to die with +# the interpreter has two undesirable properties: +# - The workers would still be running during interpretor shutdown, +# meaning that they would fail in unpredictable ways. +# - The workers could be killed while evaluating a work item, which could +# be bad if the callable being evaluated has external side-effects e.g. +# writing to a file. +# +# To work around this problem, an exit handler is installed which tells the +# workers to exit when their work queues are empty and then waits until the +# threads finish. + +_thread_references = set() +_shutdown = False + +def _python_exit(): + global _shutdown + _shutdown = True + for thread_reference in _thread_references: + thread = thread_reference() + if thread is not None: + thread.join() + +def _remove_dead_thread_references(): + """Remove inactive threads from _thread_references. + + Should be called periodically to prevent memory leaks in scenarios such as: + >>> while True: + ... t = ThreadPoolExecutor(max_workers=5) + ... t.map(int, ['1', '2', '3', '4', '5']) + """ + for thread_reference in set(_thread_references): + if thread_reference() is None: + _thread_references.discard(thread_reference) + +atexit.register(_python_exit) + +class _WorkItem(object): + def __init__(self, future, fn, args, kwargs): + self.future = future + self.fn = fn + self.args = args + self.kwargs = kwargs + + def run(self): + if not self.future.set_running_or_notify_cancel(): + return + + try: + result = self.fn(*self.args, **self.kwargs) + except BaseException as e: + self.future.set_exception(e) + else: + self.future.set_result(result) + +def _worker(executor_reference, work_queue): + try: + while True: + try: + work_item = work_queue.get(block=True, timeout=0.1) + except queue.Empty: + executor = executor_reference() + # Exit if: + # - The interpreter is shutting down OR + # - The executor that owns the worker has been collected OR + # - The executor that owns the worker has been shutdown. + if _shutdown or executor is None or executor._shutdown: + return + del executor + else: + work_item.run() + except BaseException as e: + _base.LOGGER.critical('Exception in worker', exc_info=True) + +class ThreadPoolExecutor(_base.Executor): + def __init__(self, max_workers): + """Initializes a new ThreadPoolExecutor instance. + + Args: + max_workers: The maximum number of threads that can be used to + execute the given calls. + """ + _remove_dead_thread_references() + + self._max_workers = max_workers + self._work_queue = queue.Queue() + self._threads = set() + self._shutdown = False + self._shutdown_lock = threading.Lock() + + def submit(self, fn, *args, **kwargs): + with self._shutdown_lock: + if self._shutdown: + raise RuntimeError('cannot schedule new futures after shutdown') + + f = _base.Future() + w = _WorkItem(f, fn, args, kwargs) + + self._work_queue.put(w) + self._adjust_thread_count() + return f + submit.__doc__ = _base.Executor.submit.__doc__ + + def _adjust_thread_count(self): + # TODO(bquinlan): Should avoid creating new threads if there are more + # idle threads than items in the work queue. + if len(self._threads) < self._max_workers: + t = threading.Thread(target=_worker, + args=(weakref.ref(self), self._work_queue)) + t.daemon = True + t.start() + self._threads.add(t) + _thread_references.add(weakref.ref(t)) + + def shutdown(self, wait=True): + with self._shutdown_lock: + self._shutdown = True + if wait: + for t in self._threads: + t.join() + shutdown.__doc__ = _base.Executor.shutdown.__doc__ Added: python/branches/py3k/Lib/test/test_concurrent_futures.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/test/test_concurrent_futures.py Sun Sep 19 00:35:02 2010 @@ -0,0 +1,817 @@ +import test.support + +# Skip tests if _multiprocessing wasn't built. +test.support.import_module('_multiprocessing') +# Skip tests if sem_open implementation is broken. +test.support.import_module('multiprocessing.synchronize') +# import threading after _multiprocessing to raise a more revelant error +# message: "No module named _multiprocessing". _multiprocessing is not compiled +# without thread support. +test.support.import_module('threading') + +import multiprocessing +import sys +import threading +import time +import unittest + +if sys.platform.startswith('win'): + import ctypes + import ctypes.wintypes + +from concurrent import futures +from concurrent.futures._base import ( + PENDING, RUNNING, CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED, Future, wait) +import concurrent.futures.process + +def create_future(state=PENDING, exception=None, result=None): + f = Future() + f._state = state + f._exception = exception + f._result = result + return f + +PENDING_FUTURE = create_future(state=PENDING) +RUNNING_FUTURE = create_future(state=RUNNING) +CANCELLED_FUTURE = create_future(state=CANCELLED) +CANCELLED_AND_NOTIFIED_FUTURE = create_future(state=CANCELLED_AND_NOTIFIED) +EXCEPTION_FUTURE = create_future(state=FINISHED, exception=IOError()) +SUCCESSFUL_FUTURE = create_future(state=FINISHED, result=42) + +def mul(x, y): + return x * y + +class Call(object): + """A call that can be submitted to a future.Executor for testing. + + The call signals when it is called and waits for an event before finishing. + """ + CALL_LOCKS = {} + def _create_event(self): + if sys.platform.startswith('win'): + class SECURITY_ATTRIBUTES(ctypes.Structure): + _fields_ = [("nLength", ctypes.wintypes.DWORD), + ("lpSecurityDescriptor", ctypes.wintypes.LPVOID), + ("bInheritHandle", ctypes.wintypes.BOOL)] + + s = SECURITY_ATTRIBUTES() + s.nLength = ctypes.sizeof(s) + s.lpSecurityDescriptor = None + s.bInheritHandle = True + + handle = ctypes.windll.kernel32.CreateEventA(ctypes.pointer(s), + True, + False, + None) + assert handle is not None + return handle + else: + event = multiprocessing.Event() + self.CALL_LOCKS[id(event)] = event + return id(event) + + def _wait_on_event(self, handle): + if sys.platform.startswith('win'): + r = ctypes.windll.kernel32.WaitForSingleObject(handle, 5 * 1000) + assert r == 0 + else: + self.CALL_LOCKS[handle].wait() + + def _signal_event(self, handle): + if sys.platform.startswith('win'): + r = ctypes.windll.kernel32.SetEvent(handle) + assert r != 0 + else: + self.CALL_LOCKS[handle].set() + + def __init__(self, manual_finish=False, result=42): + self._called_event = self._create_event() + self._can_finish = self._create_event() + + self._result = result + + if not manual_finish: + self._signal_event(self._can_finish) + + def wait_on_called(self): + self._wait_on_event(self._called_event) + + def set_can(self): + self._signal_event(self._can_finish) + + def __call__(self): + self._signal_event(self._called_event) + self._wait_on_event(self._can_finish) + + return self._result + + def close(self): + self.set_can() + if sys.platform.startswith('win'): + ctypes.windll.kernel32.CloseHandle(self._called_event) + ctypes.windll.kernel32.CloseHandle(self._can_finish) + else: + del self.CALL_LOCKS[self._called_event] + del self.CALL_LOCKS[self._can_finish] + +class ExceptionCall(Call): + def __call__(self): + self._signal_event(self._called_event) + self._wait_on_event(self._can_finish) + raise ZeroDivisionError() + +class MapCall(Call): + def __init__(self, result=42): + super().__init__(manual_finish=True, result=result) + + def __call__(self, manual_finish): + if manual_finish: + super().__call__() + return self._result + +class ExecutorShutdownTest(unittest.TestCase): + def test_run_after_shutdown(self): + self.executor.shutdown() + self.assertRaises(RuntimeError, + self.executor.submit, + pow, 2, 5) + + + def _start_some_futures(self): + call1 = Call(manual_finish=True) + call2 = Call(manual_finish=True) + call3 = Call(manual_finish=True) + + try: + self.executor.submit(call1) + self.executor.submit(call2) + self.executor.submit(call3) + + call1.wait_on_called() + call2.wait_on_called() + call3.wait_on_called() + + call1.set_can() + call2.set_can() + call3.set_can() + finally: + call1.close() + call2.close() + call3.close() + +class ThreadPoolShutdownTest(ExecutorShutdownTest): + def setUp(self): + self.executor = futures.ThreadPoolExecutor(max_workers=5) + + def tearDown(self): + self.executor.shutdown(wait=True) + + def test_threads_terminate(self): + self._start_some_futures() + self.assertEqual(len(self.executor._threads), 3) + self.executor.shutdown() + for t in self.executor._threads: + t.join() + + def test_context_manager_shutdown(self): + with futures.ThreadPoolExecutor(max_workers=5) as e: + executor = e + self.assertEqual(list(e.map(abs, range(-5, 5))), + [5, 4, 3, 2, 1, 0, 1, 2, 3, 4]) + + for t in executor._threads: + t.join() + + def test_del_shutdown(self): + executor = futures.ThreadPoolExecutor(max_workers=5) + executor.map(abs, range(-5, 5)) + threads = executor._threads + del executor + + for t in threads: + t.join() + +class ProcessPoolShutdownTest(ExecutorShutdownTest): + def setUp(self): + self.executor = futures.ProcessPoolExecutor(max_workers=5) + + def tearDown(self): + self.executor.shutdown(wait=True) + + def test_processes_terminate(self): + self._start_some_futures() + self.assertEqual(len(self.executor._processes), 5) + processes = self.executor._processes + self.executor.shutdown() + + for p in processes: + p.join() + + def test_context_manager_shutdown(self): + with futures.ProcessPoolExecutor(max_workers=5) as e: + executor = e + self.assertEqual(list(e.map(abs, range(-5, 5))), + [5, 4, 3, 2, 1, 0, 1, 2, 3, 4]) + + for p in self.executor._processes: + p.join() + + def test_del_shutdown(self): + executor = futures.ProcessPoolExecutor(max_workers=5) + list(executor.map(abs, range(-5, 5))) + queue_management_thread = executor._queue_management_thread + processes = executor._processes + del executor + + queue_management_thread.join() + for p in processes: + p.join() + +class WaitTests(unittest.TestCase): + def test_first_completed(self): + def wait_test(): + while not future1._waiters: + pass + call1.set_can() + + call1 = Call(manual_finish=True) + call2 = Call(manual_finish=True) + try: + future1 = self.executor.submit(call1) + future2 = self.executor.submit(call2) + + t = threading.Thread(target=wait_test) + t.start() + done, not_done = futures.wait( + [CANCELLED_FUTURE, future1, future2], + return_when=futures.FIRST_COMPLETED) + + self.assertEquals(set([future1]), done) + self.assertEquals(set([CANCELLED_FUTURE, future2]), not_done) + finally: + call1.close() + call2.close() + + def test_first_completed_one_already_completed(self): + call1 = Call(manual_finish=True) + try: + future1 = self.executor.submit(call1) + + finished, pending = futures.wait( + [SUCCESSFUL_FUTURE, future1], + return_when=futures.FIRST_COMPLETED) + + self.assertEquals(set([SUCCESSFUL_FUTURE]), finished) + self.assertEquals(set([future1]), pending) + finally: + call1.close() + + def test_first_exception(self): + def wait_test(): + while not future1._waiters: + pass + call1.set_can() + call2.set_can() + + call1 = Call(manual_finish=True) + call2 = ExceptionCall(manual_finish=True) + call3 = Call(manual_finish=True) + try: + future1 = self.executor.submit(call1) + future2 = self.executor.submit(call2) + future3 = self.executor.submit(call3) + + t = threading.Thread(target=wait_test) + t.start() + finished, pending = futures.wait( + [future1, future2, future3], + return_when=futures.FIRST_EXCEPTION) + + self.assertEquals(set([future1, future2]), finished) + self.assertEquals(set([future3]), pending) + finally: + call1.close() + call2.close() + call3.close() + + def test_first_exception_some_already_complete(self): + def wait_test(): + while not future1._waiters: + pass + call1.set_can() + + call1 = ExceptionCall(manual_finish=True) + call2 = Call(manual_finish=True) + try: + future1 = self.executor.submit(call1) + future2 = self.executor.submit(call2) + + t = threading.Thread(target=wait_test) + t.start() + finished, pending = futures.wait( + [SUCCESSFUL_FUTURE, + CANCELLED_FUTURE, + CANCELLED_AND_NOTIFIED_FUTURE, + future1, future2], + return_when=futures.FIRST_EXCEPTION) + + self.assertEquals(set([SUCCESSFUL_FUTURE, + CANCELLED_AND_NOTIFIED_FUTURE, + future1]), finished) + self.assertEquals(set([CANCELLED_FUTURE, future2]), pending) + + + finally: + call1.close() + call2.close() + + def test_first_exception_one_already_failed(self): + call1 = Call(manual_finish=True) + try: + future1 = self.executor.submit(call1) + + finished, pending = futures.wait( + [EXCEPTION_FUTURE, future1], + return_when=futures.FIRST_EXCEPTION) + + self.assertEquals(set([EXCEPTION_FUTURE]), finished) + self.assertEquals(set([future1]), pending) + finally: + call1.close() + + def test_all_completed(self): + def wait_test(): + while not future1._waiters: + pass + call1.set_can() + call2.set_can() + + call1 = Call(manual_finish=True) + call2 = Call(manual_finish=True) + try: + future1 = self.executor.submit(call1) + future2 = self.executor.submit(call2) + + t = threading.Thread(target=wait_test) + t.start() + finished, pending = futures.wait( + [future1, future2], + return_when=futures.ALL_COMPLETED) + + self.assertEquals(set([future1, future2]), finished) + self.assertEquals(set(), pending) + + + finally: + call1.close() + call2.close() + + def test_all_completed_some_already_completed(self): + def wait_test(): + while not future1._waiters: + pass + + future4.cancel() + call1.set_can() + call2.set_can() + call3.set_can() + + self.assertLessEqual( + futures.process.EXTRA_QUEUED_CALLS, + 1, + 'this test assumes that future4 will be cancelled before it is ' + 'queued to run - which might not be the case if ' + 'ProcessPoolExecutor is too aggresive in scheduling futures') + call1 = Call(manual_finish=True) + call2 = Call(manual_finish=True) + call3 = Call(manual_finish=True) + call4 = Call(manual_finish=True) + try: + future1 = self.executor.submit(call1) + future2 = self.executor.submit(call2) + future3 = self.executor.submit(call3) + future4 = self.executor.submit(call4) + + t = threading.Thread(target=wait_test) + t.start() + finished, pending = futures.wait( + [SUCCESSFUL_FUTURE, + CANCELLED_AND_NOTIFIED_FUTURE, + future1, future2, future3, future4], + return_when=futures.ALL_COMPLETED) + + self.assertEquals(set([SUCCESSFUL_FUTURE, + CANCELLED_AND_NOTIFIED_FUTURE, + future1, future2, future3, future4]), + finished) + self.assertEquals(set(), pending) + finally: + call1.close() + call2.close() + call3.close() + call4.close() + + def test_timeout(self): + def wait_test(): + while not future1._waiters: + pass + call1.set_can() + + call1 = Call(manual_finish=True) + call2 = Call(manual_finish=True) + try: + future1 = self.executor.submit(call1) + future2 = self.executor.submit(call2) + + t = threading.Thread(target=wait_test) + t.start() + finished, pending = futures.wait( + [CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + future1, future2], + timeout=1, + return_when=futures.ALL_COMPLETED) + + self.assertEquals(set([CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + future1]), finished) + self.assertEquals(set([future2]), pending) + + + finally: + call1.close() + call2.close() + + +class ThreadPoolWaitTests(WaitTests): + def setUp(self): + self.executor = futures.ThreadPoolExecutor(max_workers=1) + + def tearDown(self): + self.executor.shutdown(wait=True) + +class ProcessPoolWaitTests(WaitTests): + def setUp(self): + self.executor = futures.ProcessPoolExecutor(max_workers=1) + + def tearDown(self): + self.executor.shutdown(wait=True) + +class AsCompletedTests(unittest.TestCase): + # TODO(brian at sweetapp.com): Should have a test with a non-zero timeout. + def test_no_timeout(self): + def wait_test(): + while not future1._waiters: + pass + call1.set_can() + call2.set_can() + + call1 = Call(manual_finish=True) + call2 = Call(manual_finish=True) + try: + future1 = self.executor.submit(call1) + future2 = self.executor.submit(call2) + + t = threading.Thread(target=wait_test) + t.start() + completed = set(futures.as_completed( + [CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + future1, future2])) + self.assertEquals(set( + [CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + future1, future2]), + completed) + finally: + call1.close() + call2.close() + + def test_zero_timeout(self): + call1 = Call(manual_finish=True) + try: + future1 = self.executor.submit(call1) + completed_futures = set() + try: + for future in futures.as_completed( + [CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE, + future1], + timeout=0): + completed_futures.add(future) + except futures.TimeoutError: + pass + + self.assertEquals(set([CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE]), + completed_futures) + finally: + call1.close() + +class ThreadPoolAsCompletedTests(AsCompletedTests): + def setUp(self): + self.executor = futures.ThreadPoolExecutor(max_workers=1) + + def tearDown(self): + self.executor.shutdown(wait=True) + +class ProcessPoolAsCompletedTests(AsCompletedTests): + def setUp(self): + self.executor = futures.ProcessPoolExecutor(max_workers=1) + + def tearDown(self): + self.executor.shutdown(wait=True) + +class ExecutorTest(unittest.TestCase): + # Executor.shutdown() and context manager usage is tested by + # ExecutorShutdownTest. + def test_submit(self): + future = self.executor.submit(pow, 2, 8) + self.assertEquals(256, future.result()) + + def test_submit_keyword(self): + future = self.executor.submit(mul, 2, y=8) + self.assertEquals(16, future.result()) + + def test_map(self): + self.assertEqual( + list(self.executor.map(pow, range(10), range(10))), + list(map(pow, range(10), range(10)))) + + def test_map_exception(self): + i = self.executor.map(divmod, [1, 1, 1, 1], [2, 3, 0, 5]) + self.assertEqual(i.__next__(), (0, 1)) + self.assertEqual(i.__next__(), (0, 1)) + self.assertRaises(ZeroDivisionError, i.__next__) + + def test_map_timeout(self): + results = [] + timeout_call = MapCall() + try: + try: + for i in self.executor.map(timeout_call, + [False, False, True], + timeout=1): + results.append(i) + except futures.TimeoutError: + pass + else: + self.fail('expected TimeoutError') + finally: + timeout_call.close() + + self.assertEquals([42, 42], results) + +class ThreadPoolExecutorTest(ExecutorTest): + def setUp(self): + self.executor = futures.ThreadPoolExecutor(max_workers=1) + + def tearDown(self): + self.executor.shutdown(wait=True) + +class ProcessPoolExecutorTest(ExecutorTest): + def setUp(self): + self.executor = futures.ProcessPoolExecutor(max_workers=1) + + def tearDown(self): + self.executor.shutdown(wait=True) + +class FutureTests(unittest.TestCase): + def test_done_callback_with_result(self): + callback_result = None + def fn(callback_future): + nonlocal callback_result + callback_result = callback_future.result() + + f = Future() + f.add_done_callback(fn) + f.set_result(5) + self.assertEquals(5, callback_result) + + def test_done_callback_with_exception(self): + callback_exception = None + def fn(callback_future): + nonlocal callback_exception + callback_exception = callback_future.exception() + + f = Future() + f.add_done_callback(fn) + f.set_exception(Exception('test')) + self.assertEquals(('test',), callback_exception.args) + + def test_done_callback_with_cancel(self): + was_cancelled = None + def fn(callback_future): + nonlocal was_cancelled + was_cancelled = callback_future.cancelled() + + f = Future() + f.add_done_callback(fn) + self.assertTrue(f.cancel()) + self.assertTrue(was_cancelled) + + def test_done_callback_raises(self): + raising_was_called = False + fn_was_called = False + + def raising_fn(callback_future): + nonlocal raising_was_called + raising_was_called = True + raise Exception('doh!') + + def fn(callback_future): + nonlocal fn_was_called + fn_was_called = True + + f = Future() + f.add_done_callback(raising_fn) + f.add_done_callback(fn) + f.set_result(5) + self.assertTrue(raising_was_called) + self.assertTrue(fn_was_called) + + def test_done_callback_already_successful(self): + callback_result = None + def fn(callback_future): + nonlocal callback_result + callback_result = callback_future.result() + + f = Future() + f.set_result(5) + f.add_done_callback(fn) + self.assertEquals(5, callback_result) + + def test_done_callback_already_failed(self): + callback_exception = None + def fn(callback_future): + nonlocal callback_exception + callback_exception = callback_future.exception() + + f = Future() + f.set_exception(Exception('test')) + f.add_done_callback(fn) + self.assertEquals(('test',), callback_exception.args) + + def test_done_callback_already_cancelled(self): + was_cancelled = None + def fn(callback_future): + nonlocal was_cancelled + was_cancelled = callback_future.cancelled() + + f = Future() + self.assertTrue(f.cancel()) + f.add_done_callback(fn) + self.assertTrue(was_cancelled) + + def test_repr(self): + self.assertRegexpMatches(repr(PENDING_FUTURE), + '') + self.assertRegexpMatches(repr(RUNNING_FUTURE), + '') + self.assertRegexpMatches(repr(CANCELLED_FUTURE), + '') + self.assertRegexpMatches(repr(CANCELLED_AND_NOTIFIED_FUTURE), + '') + self.assertRegexpMatches( + repr(EXCEPTION_FUTURE), + '') + self.assertRegexpMatches( + repr(SUCCESSFUL_FUTURE), + '') + + + def test_cancel(self): + f1 = create_future(state=PENDING) + f2 = create_future(state=RUNNING) + f3 = create_future(state=CANCELLED) + f4 = create_future(state=CANCELLED_AND_NOTIFIED) + f5 = create_future(state=FINISHED, exception=IOError()) + f6 = create_future(state=FINISHED, result=5) + + self.assertTrue(f1.cancel()) + self.assertEquals(f1._state, CANCELLED) + + self.assertFalse(f2.cancel()) + self.assertEquals(f2._state, RUNNING) + + self.assertTrue(f3.cancel()) + self.assertEquals(f3._state, CANCELLED) + + self.assertTrue(f4.cancel()) + self.assertEquals(f4._state, CANCELLED_AND_NOTIFIED) + + self.assertFalse(f5.cancel()) + self.assertEquals(f5._state, FINISHED) + + self.assertFalse(f6.cancel()) + self.assertEquals(f6._state, FINISHED) + + def test_cancelled(self): + self.assertFalse(PENDING_FUTURE.cancelled()) + self.assertFalse(RUNNING_FUTURE.cancelled()) + self.assertTrue(CANCELLED_FUTURE.cancelled()) + self.assertTrue(CANCELLED_AND_NOTIFIED_FUTURE.cancelled()) + self.assertFalse(EXCEPTION_FUTURE.cancelled()) + self.assertFalse(SUCCESSFUL_FUTURE.cancelled()) + + def test_done(self): + self.assertFalse(PENDING_FUTURE.done()) + self.assertFalse(RUNNING_FUTURE.done()) + self.assertTrue(CANCELLED_FUTURE.done()) + self.assertTrue(CANCELLED_AND_NOTIFIED_FUTURE.done()) + self.assertTrue(EXCEPTION_FUTURE.done()) + self.assertTrue(SUCCESSFUL_FUTURE.done()) + + def test_running(self): + self.assertFalse(PENDING_FUTURE.running()) + self.assertTrue(RUNNING_FUTURE.running()) + self.assertFalse(CANCELLED_FUTURE.running()) + self.assertFalse(CANCELLED_AND_NOTIFIED_FUTURE.running()) + self.assertFalse(EXCEPTION_FUTURE.running()) + self.assertFalse(SUCCESSFUL_FUTURE.running()) + + def test_result_with_timeout(self): + self.assertRaises(futures.TimeoutError, + PENDING_FUTURE.result, timeout=0) + self.assertRaises(futures.TimeoutError, + RUNNING_FUTURE.result, timeout=0) + self.assertRaises(futures.CancelledError, + CANCELLED_FUTURE.result, timeout=0) + self.assertRaises(futures.CancelledError, + CANCELLED_AND_NOTIFIED_FUTURE.result, timeout=0) + self.assertRaises(IOError, EXCEPTION_FUTURE.result, timeout=0) + self.assertEqual(SUCCESSFUL_FUTURE.result(timeout=0), 42) + + def test_result_with_success(self): + # TODO(brian at sweetapp.com): This test is timing dependant. + def notification(): + # Wait until the main thread is waiting for the result. + time.sleep(1) + f1.set_result(42) + + f1 = create_future(state=PENDING) + t = threading.Thread(target=notification) + t.start() + + self.assertEquals(f1.result(timeout=5), 42) + + def test_result_with_cancel(self): + # TODO(brian at sweetapp.com): This test is timing dependant. + def notification(): + # Wait until the main thread is waiting for the result. + time.sleep(1) + f1.cancel() + + f1 = create_future(state=PENDING) + t = threading.Thread(target=notification) + t.start() + + self.assertRaises(futures.CancelledError, f1.result, timeout=5) + + def test_exception_with_timeout(self): + self.assertRaises(futures.TimeoutError, + PENDING_FUTURE.exception, timeout=0) + self.assertRaises(futures.TimeoutError, + RUNNING_FUTURE.exception, timeout=0) + self.assertRaises(futures.CancelledError, + CANCELLED_FUTURE.exception, timeout=0) + self.assertRaises(futures.CancelledError, + CANCELLED_AND_NOTIFIED_FUTURE.exception, timeout=0) + self.assertTrue(isinstance(EXCEPTION_FUTURE.exception(timeout=0), + IOError)) + self.assertEqual(SUCCESSFUL_FUTURE.exception(timeout=0), None) + + def test_exception_with_success(self): + def notification(): + # Wait until the main thread is waiting for the exception. + time.sleep(1) + with f1._condition: + f1._state = FINISHED + f1._exception = IOError() + f1._condition.notify_all() + + f1 = create_future(state=PENDING) + t = threading.Thread(target=notification) + t.start() + + self.assertTrue(isinstance(f1.exception(timeout=5), IOError)) + +def test_main(): + test.support.run_unittest(ProcessPoolExecutorTest, + ThreadPoolExecutorTest, + ProcessPoolWaitTests, + ThreadPoolWaitTests, + ProcessPoolAsCompletedTests, + ThreadPoolAsCompletedTests, + FutureTests, + ProcessPoolShutdownTest, + ThreadPoolShutdownTest) + +if __name__ == "__main__": + test_main() From python-checkins at python.org Sun Sep 19 00:38:48 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 19 Sep 2010 00:38:48 +0200 (CEST) Subject: [Python-checkins] r84883 - python/branches/py3k/Lib/test/test_subprocess.py Message-ID: <20100918223848.56AE8EE99B@mail.python.org> Author: antoine.pitrou Date: Sun Sep 19 00:38:48 2010 New Revision: 84883 Log: Issue #9895: speed up test_subprocess Modified: python/branches/py3k/Lib/test/test_subprocess.py Modified: python/branches/py3k/Lib/test/test_subprocess.py ============================================================================== --- python/branches/py3k/Lib/test/test_subprocess.py (original) +++ python/branches/py3k/Lib/test/test_subprocess.py Sun Sep 19 00:38:48 2010 @@ -445,21 +445,40 @@ def test_no_leaking(self): # Make sure we leak no resources - if (not hasattr(support, "is_resource_enabled") or - support.is_resource_enabled("subprocess") and not mswindows): + if not mswindows: max_handles = 1026 # too much for most UNIX systems else: - max_handles = 65 - for i in range(max_handles): - p = subprocess.Popen([sys.executable, "-c", - "import sys;" - "sys.stdout.write(sys.stdin.read())"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - data = p.communicate(b"lime")[0] - self.assertEqual(data, b"lime") - + max_handles = 2050 # too much for (at least some) Windows setups + handles = [] + try: + for i in range(max_handles): + try: + handles.append(os.open(support.TESTFN, + os.O_WRONLY | os.O_CREAT)) + except OSError as e: + if e.errno != errno.EMFILE: + raise + break + else: + self.skipTest("failed to reach the file descriptor limit " + "(tried %d)" % max_handles) + # Close a couple of them (should be enough for a subprocess) + for i in range(10): + os.close(handles.pop()) + # Loop creating some subprocesses. If one of them leaks some fds, + # the next loop iteration will fail by reaching the max fd limit. + for i in range(15): + p = subprocess.Popen([sys.executable, "-c", + "import sys;" + "sys.stdout.write(sys.stdin.read())"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + data = p.communicate(b"lime")[0] + self.assertEqual(data, b"lime") + finally: + for h in handles: + os.close(h) def test_list2cmdline(self): self.assertEqual(subprocess.list2cmdline(['a b c', 'd', 'e']), From python-checkins at python.org Sun Sep 19 00:40:56 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 19 Sep 2010 00:40:56 +0200 (CEST) Subject: [Python-checkins] r84884 - in python/branches/release31-maint: Lib/test/test_subprocess.py Message-ID: <20100918224056.F085EEE990@mail.python.org> Author: antoine.pitrou Date: Sun Sep 19 00:40:56 2010 New Revision: 84884 Log: Merged revisions 84883 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84883 | antoine.pitrou | 2010-09-19 00:38:48 +0200 (dim., 19 sept. 2010) | 3 lines Issue #9895: speed up test_subprocess ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_subprocess.py Modified: python/branches/release31-maint/Lib/test/test_subprocess.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_subprocess.py (original) +++ python/branches/release31-maint/Lib/test/test_subprocess.py Sun Sep 19 00:40:56 2010 @@ -445,21 +445,40 @@ def test_no_leaking(self): # Make sure we leak no resources - if (not hasattr(support, "is_resource_enabled") or - support.is_resource_enabled("subprocess") and not mswindows): + if not mswindows: max_handles = 1026 # too much for most UNIX systems else: - max_handles = 65 - for i in range(max_handles): - p = subprocess.Popen([sys.executable, "-c", - "import sys;" - "sys.stdout.write(sys.stdin.read())"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - data = p.communicate(b"lime")[0] - self.assertEqual(data, b"lime") - + max_handles = 2050 # too much for (at least some) Windows setups + handles = [] + try: + for i in range(max_handles): + try: + handles.append(os.open(support.TESTFN, + os.O_WRONLY | os.O_CREAT)) + except OSError as e: + if e.errno != errno.EMFILE: + raise + break + else: + self.skipTest("failed to reach the file descriptor limit " + "(tried %d)" % max_handles) + # Close a couple of them (should be enough for a subprocess) + for i in range(10): + os.close(handles.pop()) + # Loop creating some subprocesses. If one of them leaks some fds, + # the next loop iteration will fail by reaching the max fd limit. + for i in range(15): + p = subprocess.Popen([sys.executable, "-c", + "import sys;" + "sys.stdout.write(sys.stdin.read())"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + data = p.communicate(b"lime")[0] + self.assertEqual(data, b"lime") + finally: + for h in handles: + os.close(h) def test_list2cmdline(self): self.assertEqual(subprocess.list2cmdline(['a b c', 'd', 'e']), From python-checkins at python.org Sun Sep 19 00:42:30 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 19 Sep 2010 00:42:30 +0200 (CEST) Subject: [Python-checkins] r84885 - in python/branches/release27-maint: Lib/test/test_subprocess.py Message-ID: <20100918224230.C8695EE990@mail.python.org> Author: antoine.pitrou Date: Sun Sep 19 00:42:30 2010 New Revision: 84885 Log: Merged revisions 84883 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84883 | antoine.pitrou | 2010-09-19 00:38:48 +0200 (dim., 19 sept. 2010) | 3 lines Issue #9895: speed up test_subprocess ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_subprocess.py Modified: python/branches/release27-maint/Lib/test/test_subprocess.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_subprocess.py (original) +++ python/branches/release27-maint/Lib/test/test_subprocess.py Sun Sep 19 00:42:30 2010 @@ -454,20 +454,40 @@ def test_no_leaking(self): # Make sure we leak no resources - if not hasattr(test_support, "is_resource_enabled") \ - or test_support.is_resource_enabled("subprocess") and not mswindows: + if not mswindows: max_handles = 1026 # too much for most UNIX systems else: - max_handles = 65 - for i in range(max_handles): - p = subprocess.Popen([sys.executable, "-c", - "import sys;sys.stdout.write(sys.stdin.read())"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - data = p.communicate("lime")[0] - self.assertEqual(data, "lime") - + max_handles = 2050 # too much for (at least some) Windows setups + handles = [] + try: + for i in range(max_handles): + try: + handles.append(os.open(test_support.TESTFN, + os.O_WRONLY | os.O_CREAT)) + except OSError as e: + if e.errno != errno.EMFILE: + raise + break + else: + self.skipTest("failed to reach the file descriptor limit " + "(tried %d)" % max_handles) + # Close a couple of them (should be enough for a subprocess) + for i in range(10): + os.close(handles.pop()) + # Loop creating some subprocesses. If one of them leaks some fds, + # the next loop iteration will fail by reaching the max fd limit. + for i in range(15): + p = subprocess.Popen([sys.executable, "-c", + "import sys;" + "sys.stdout.write(sys.stdin.read())"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + data = p.communicate(b"lime")[0] + self.assertEqual(data, b"lime") + finally: + for h in handles: + os.close(h) def test_list2cmdline(self): self.assertEqual(subprocess.list2cmdline(['a b c', 'd', 'e']), From python-checkins at python.org Sun Sep 19 00:53:25 2010 From: python-checkins at python.org (hirokazu.yamamoto) Date: Sun, 19 Sep 2010 00:53:25 +0200 (CEST) Subject: [Python-checkins] r84886 - in python/branches/release31-maint: Misc/NEWS PCbuild/bz2.vcproj Message-ID: <20100918225325.9D0D0EE992@mail.python.org> Author: hirokazu.yamamoto Date: Sun Sep 19 00:53:25 2010 New Revision: 84886 Log: Merged revisions 84851 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84851 | hirokazu.yamamoto | 2010-09-17 02:50:57 +0900 | 3 lines Issue #9810: Compile bzip2 source files in python's project file directly. It used to be built with bzip2's makefile. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/PCbuild/bz2.vcproj Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Sep 19 00:53:25 2010 @@ -565,6 +565,9 @@ Build ----- +- Issue #9810: Compile bzip2 source files in python's project file + directly. It used to be built with bzip2's makefile. + - Issue #941346: Improve the build process under AIX and allow Python to be built as a shared library. Patch by S?bastien Sabl?. Modified: python/branches/release31-maint/PCbuild/bz2.vcproj ============================================================================== --- python/branches/release31-maint/PCbuild/bz2.vcproj (original) +++ python/branches/release31-maint/PCbuild/bz2.vcproj Sun Sep 19 00:53:25 2010 @@ -43,6 +43,7 @@ + + + + + + + + + + + + + + + + + + + + + + From python-checkins at python.org Sun Sep 19 00:59:00 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 19 Sep 2010 00:59:00 +0200 (CEST) Subject: [Python-checkins] r84887 - in python/branches/py3k: Lib/socket.py Lib/test/test_socket.py Misc/NEWS Message-ID: <20100918225900.8CD79EE992@mail.python.org> Author: antoine.pitrou Date: Sun Sep 19 00:59:00 2010 New Revision: 84887 Log: Issue #9854: SocketIO objects now observe the RawIOBase interface in non-blocking mode: they return None when an operation would block (instead of raising an exception). Modified: python/branches/py3k/Lib/socket.py python/branches/py3k/Lib/test/test_socket.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/socket.py ============================================================================== --- python/branches/py3k/Lib/socket.py (original) +++ python/branches/py3k/Lib/socket.py Sun Sep 19 00:59:00 2010 @@ -54,6 +54,8 @@ errno = None EBADF = getattr(errno, 'EBADF', 9) EINTR = getattr(errno, 'EINTR', 4) +EAGAIN = getattr(errno, 'EAGAIN', 11) +EWOULDBLOCK = getattr(errno, 'EWOULDBLOCK', 11) __all__ = ["getfqdn", "create_connection"] __all__.extend(os._get_exports_list(_socket)) @@ -220,6 +222,8 @@ return a, b +_blocking_errnos = { EAGAIN, EWOULDBLOCK } + class SocketIO(io.RawIOBase): """Raw I/O implementation for stream sockets. @@ -262,8 +266,11 @@ try: return self._sock.recv_into(b) except error as e: - if e.args[0] == EINTR: + n = e.args[0] + if n == EINTR: continue + if n in _blocking_errnos: + return None raise def write(self, b): @@ -274,7 +281,13 @@ """ self._checkClosed() self._checkWritable() - return self._sock.send(b) + try: + return self._sock.send(b) + except error as e: + # XXX what about EINTR? + if e.args[0] in _blocking_errnos: + return None + raise def readable(self): """True if the SocketIO is open for reading. Modified: python/branches/py3k/Lib/test/test_socket.py ============================================================================== --- python/branches/py3k/Lib/test/test_socket.py (original) +++ python/branches/py3k/Lib/test/test_socket.py Sun Sep 19 00:59:00 2010 @@ -137,8 +137,8 @@ self.done.wait() if self.queue.qsize(): - msg = self.queue.get() - self.fail(msg) + exc = self.queue.get() + raise exc def clientRun(self, test_func): self.server_ready.wait() @@ -148,9 +148,10 @@ raise TypeError("test_func must be a callable function") try: test_func() - except Exception as strerror: - self.queue.put(strerror) - self.clientTearDown() + except BaseException as e: + self.queue.put(e) + finally: + self.clientTearDown() def clientSetUp(self): raise NotImplementedError("clientSetUp must be implemented.") @@ -932,10 +933,13 @@ SocketConnectedTest.__init__(self, methodName=methodName) def setUp(self): + self.evt1, self.evt2, self.serv_finished, self.cli_finished = [ + threading.Event() for i in range(4)] SocketConnectedTest.setUp(self) self.serv_file = self.cli_conn.makefile('rb', self.bufsize) def tearDown(self): + self.serv_finished.set() self.serv_file.close() self.assertTrue(self.serv_file.closed) self.serv_file = None @@ -943,9 +947,10 @@ def clientSetUp(self): SocketConnectedTest.clientSetUp(self) - self.cli_file = self.serv_conn.makefile('wb') + self.cli_file = self.serv_conn.makefile('wb', self.bufsize) def clientTearDown(self): + self.cli_finished.set() self.cli_file.close() self.assertTrue(self.cli_file.closed) self.cli_file = None @@ -1196,6 +1201,62 @@ def _testMakefileCloseSocketDestroy(self): pass + # Non-blocking ops + # NOTE: to set `serv_file` as non-blocking, we must call + # `cli_conn.setblocking` and vice-versa (see setUp / clientSetUp). + + def testSmallReadNonBlocking(self): + self.cli_conn.setblocking(False) + self.assertEqual(self.serv_file.readinto(bytearray(10)), None) + self.assertEqual(self.serv_file.read(len(MSG) - 3), None) + self.evt1.set() + self.evt2.wait(1.0) + first_seg = self.serv_file.read(len(MSG) - 3) + buf = bytearray(10) + n = self.serv_file.readinto(buf) + self.assertEqual(n, 3) + msg = first_seg + buf[:n] + self.assertEqual(msg, MSG) + self.assertEqual(self.serv_file.readinto(bytearray(16)), None) + self.assertEqual(self.serv_file.read(1), None) + + def _testSmallReadNonBlocking(self): + self.evt1.wait(1.0) + self.cli_file.write(MSG) + self.cli_file.flush() + self.evt2.set() + # Avoid cloding the socket before the server test has finished, + # otherwise system recv() will return 0 instead of EWOULDBLOCK. + self.serv_finished.wait(5.0) + + def testWriteNonBlocking(self): + self.cli_finished.wait(5.0) + # The client thread can't skip directly - the SkipTest exception + # would appear as a failure. + if self.serv_skipped: + self.skipTest(self.serv_skipped) + + def _testWriteNonBlocking(self): + self.serv_skipped = None + self.serv_conn.setblocking(False) + # Try to saturate the socket buffer pipe with repeated large writes. + BIG = b"x" * (1024 ** 2) + LIMIT = 10 + # The first write() succeeds since a chunk of data can be buffered + n = self.cli_file.write(BIG) + self.assertGreater(n, 0) + for i in range(LIMIT): + n = self.cli_file.write(BIG) + if n is None: + # Succeeded + break + self.assertGreater(n, 0) + else: + # Let us know that this test didn't manage to establish + # the expected conditions. This is not a failure in itself but, + # if it happens repeatedly, the test should be fixed. + self.serv_skipped = "failed to saturate the socket buffer" + class LineBufferedFileObjectClassTestCase(FileObjectClassTestCase): Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 19 00:59:00 2010 @@ -52,6 +52,10 @@ Library ------- +- Issue #9854: SocketIO objects now observe the RawIOBase interface in + non-blocking mode: they return None when an operation would block (instead + of raising an exception). + - Issue #1730136: Fix the comparison between a tk.font.Font and an object of another kind. From python-checkins at python.org Sun Sep 19 01:34:07 2010 From: python-checkins at python.org (florent.xicluna) Date: Sun, 19 Sep 2010 01:34:07 +0200 (CEST) Subject: [Python-checkins] r84888 - in python/branches/py3k: Lib/string.py Lib/test/test_pep292.py Misc/NEWS Message-ID: <20100918233407.A2703EE9CF@mail.python.org> Author: florent.xicluna Date: Sun Sep 19 01:34:07 2010 New Revision: 84888 Log: Issue #1686: Fix string.Template when overriding the pattern attribute. Modified: python/branches/py3k/Lib/string.py python/branches/py3k/Lib/test/test_pep292.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/string.py ============================================================================== --- python/branches/py3k/Lib/string.py (original) +++ python/branches/py3k/Lib/string.py Sun Sep 19 01:34:07 2010 @@ -145,24 +145,18 @@ mapping = args[0] # Helper function for .sub() def convert(mo): - named = mo.group('named') + named = mo.group('named') or mo.group('braced') if named is not None: try: # We use this idiom instead of str() because the latter # will fail if val is a Unicode containing non-ASCII return '%s' % (mapping[named],) except KeyError: - return self.delimiter + named - braced = mo.group('braced') - if braced is not None: - try: - return '%s' % (mapping[braced],) - except KeyError: - return self.delimiter + '{' + braced + '}' + return mo.group() if mo.group('escaped') is not None: return self.delimiter if mo.group('invalid') is not None: - return self.delimiter + return mo.group() raise ValueError('Unrecognized named group in pattern', self.pattern) return self.pattern.sub(convert, self.template) Modified: python/branches/py3k/Lib/test/test_pep292.py ============================================================================== --- python/branches/py3k/Lib/test/test_pep292.py (original) +++ python/branches/py3k/Lib/test/test_pep292.py Sun Sep 19 01:34:07 2010 @@ -125,6 +125,40 @@ self.assertRaises(ValueError, s.substitute, {}) self.assertRaises(ValueError, s.safe_substitute, {}) + def test_braced_override(self): + class MyTemplate(Template): + pattern = r""" + \$(?: + (?P$) | + (?P[_a-z][_a-z0-9]*) | + @@(?P[_a-z][_a-z0-9]*)@@ | + (?P) | + ) + """ + + tmpl = 'PyCon in $@@location@@' + t = MyTemplate(tmpl) + self.assertRaises(KeyError, t.substitute, {}) + val = t.substitute({'location': 'Cleveland'}) + self.assertEqual(val, 'PyCon in Cleveland') + + def test_braced_override_safe(self): + class MyTemplate(Template): + pattern = r""" + \$(?: + (?P$) | + (?P[_a-z][_a-z0-9]*) | + @@(?P[_a-z][_a-z0-9]*)@@ | + (?P) | + ) + """ + + tmpl = 'PyCon in $@@location@@' + t = MyTemplate(tmpl) + self.assertEqual(t.safe_substitute(), tmpl) + val = t.safe_substitute({'location': 'Cleveland'}) + self.assertEqual(val, 'PyCon in Cleveland') + def test_unicode_values(self): s = Template('$who likes $what') d = dict(who='t\xffm', what='f\xfe\fed') Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Sep 19 01:34:07 2010 @@ -52,6 +52,8 @@ Library ------- +- Issue #1686: Fix string.Template when overriding the pattern attribute. + - Issue #9854: SocketIO objects now observe the RawIOBase interface in non-blocking mode: they return None when an operation would block (instead of raising an exception). From solipsis at pitrou.net Sun Sep 19 04:58:14 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 19 Sep 2010 04:58:14 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r84888): sum=0 Message-ID: py3k results for svn r84888 (hg cset 375584ceac56) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflog9PN0Gc', '-x'] From python-checkins at python.org Sun Sep 19 05:09:54 2010 From: python-checkins at python.org (senthil.kumaran) Date: Sun, 19 Sep 2010 05:09:54 +0200 (CEST) Subject: [Python-checkins] r84889 - python/branches/py3k/Lib/distutils/tests/test_dir_util.py Message-ID: <20100919030954.5C976F97A@mail.python.org> Author: senthil.kumaran Date: Sun Sep 19 05:09:54 2010 New Revision: 84889 Log: Update the test_distutils mode test to test with umask value properly. Modified: python/branches/py3k/Lib/distutils/tests/test_dir_util.py Modified: python/branches/py3k/Lib/distutils/tests/test_dir_util.py ============================================================================== --- python/branches/py3k/Lib/distutils/tests/test_dir_util.py (original) +++ python/branches/py3k/Lib/distutils/tests/test_dir_util.py Sun Sep 19 05:09:54 2010 @@ -53,10 +53,15 @@ @unittest.skipIf(sys.platform.startswith('win'), "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): + # Get and set the current umask value for testing mode bits. + umask = os.umask(0o002) + os.umask(umask) mkpath(self.target, 0o700) - self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + self.assertEqual( + stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) mkpath(self.target2, 0o555) - self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + self.assertEqual( + stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) def test_create_tree_verbosity(self): From python-checkins at python.org Sun Sep 19 05:12:28 2010 From: python-checkins at python.org (senthil.kumaran) Date: Sun, 19 Sep 2010 05:12:28 +0200 (CEST) Subject: [Python-checkins] r84890 - in python/branches/release27-maint: Lib/distutils/tests/test_dir_util.py Message-ID: <20100919031228.80518EE986@mail.python.org> Author: senthil.kumaran Date: Sun Sep 19 05:12:28 2010 New Revision: 84890 Log: Merged revisions 84889 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84889 | senthil.kumaran | 2010-09-19 08:39:54 +0530 (Sun, 19 Sep 2010) | 3 lines Update the test_distutils mode test to test with umask value properly. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py Modified: python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py (original) +++ python/branches/release27-maint/Lib/distutils/tests/test_dir_util.py Sun Sep 19 05:12:28 2010 @@ -53,10 +53,15 @@ @unittest.skipIf(sys.platform.startswith('win'), "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): + # Get and set the current umask value for testing mode bits. + umask = os.umask(0o002) + os.umask(umask) mkpath(self.target, 0o700) - self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + self.assertEqual( + stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) mkpath(self.target2, 0o555) - self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + self.assertEqual( + stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) def test_create_tree_verbosity(self): From python-checkins at python.org Sun Sep 19 05:12:35 2010 From: python-checkins at python.org (senthil.kumaran) Date: Sun, 19 Sep 2010 05:12:35 +0200 (CEST) Subject: [Python-checkins] r84891 - in python/branches/release31-maint: Lib/distutils/tests/test_dir_util.py Message-ID: <20100919031235.D47C5EE9A3@mail.python.org> Author: senthil.kumaran Date: Sun Sep 19 05:12:35 2010 New Revision: 84891 Log: Merged revisions 84889 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84889 | senthil.kumaran | 2010-09-19 08:39:54 +0530 (Sun, 19 Sep 2010) | 3 lines Update the test_distutils mode test to test with umask value properly. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py Modified: python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py (original) +++ python/branches/release31-maint/Lib/distutils/tests/test_dir_util.py Sun Sep 19 05:12:35 2010 @@ -53,10 +53,15 @@ @unittest.skipIf(sys.platform.startswith('win'), "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): + # Get and set the current umask value for testing mode bits. + umask = os.umask(0o002) + os.umask(umask) mkpath(self.target, 0o700) - self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + self.assertEqual( + stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) mkpath(self.target2, 0o555) - self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + self.assertEqual( + stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) def test_create_tree_verbosity(self): From python-checkins at python.org Sun Sep 19 05:54:31 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 19 Sep 2010 05:54:31 +0200 (CEST) Subject: [Python-checkins] r84892 - in python/branches/py3k/Lib/test/tracedmodules: __init__.py testmod.py Message-ID: <20100919035431.23E6BEE987@mail.python.org> Author: benjamin.peterson Date: Sun Sep 19 05:54:31 2010 New Revision: 84892 Log: set svn:eol-style on tracedmodules Modified: python/branches/py3k/Lib/test/tracedmodules/__init__.py (props changed) python/branches/py3k/Lib/test/tracedmodules/testmod.py (props changed) From python-checkins at python.org Sun Sep 19 05:55:10 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 19 Sep 2010 05:55:10 +0200 (CEST) Subject: [Python-checkins] r84893 - in python/branches/py3k: Doc/library/concurrent.futures.rst Lib/concurrent/__init__.py Lib/concurrent/futures/__init__.py Lib/concurrent/futures/_base.py Lib/concurrent/futures/process.py Lib/concurrent/futures/thread.py Lib/test/test_concurrent_futures.py Message-ID: <20100919035510.6DA99EE987@mail.python.org> Author: benjamin.peterson Date: Sun Sep 19 05:55:10 2010 New Revision: 84893 Log: set svn:eol-style on concurrent package files Modified: python/branches/py3k/Doc/library/concurrent.futures.rst (props changed) python/branches/py3k/Lib/concurrent/__init__.py (props changed) python/branches/py3k/Lib/concurrent/futures/__init__.py (props changed) python/branches/py3k/Lib/concurrent/futures/_base.py (props changed) python/branches/py3k/Lib/concurrent/futures/process.py (props changed) python/branches/py3k/Lib/concurrent/futures/thread.py (props changed) python/branches/py3k/Lib/test/test_concurrent_futures.py (props changed) From python-checkins at python.org Sun Sep 19 06:23:07 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 19 Sep 2010 06:23:07 +0200 (CEST) Subject: [Python-checkins] r84894 - in python/branches/release31-maint: Lib/test/tracedmodules/__init__.py Lib/test/tracedmodules/testmod.py Message-ID: <20100919042307.90C9FEE996@mail.python.org> Author: benjamin.peterson Date: Sun Sep 19 06:23:07 2010 New Revision: 84894 Log: Merged revisions 84892 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84892 | benjamin.peterson | 2010-09-18 22:54:31 -0500 (Sat, 18 Sep 2010) | 1 line set svn:eol-style on tracedmodules ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/tracedmodules/__init__.py (props changed) python/branches/release31-maint/Lib/test/tracedmodules/testmod.py (props changed) From python-checkins at python.org Sun Sep 19 06:23:17 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 19 Sep 2010 06:23:17 +0200 (CEST) Subject: [Python-checkins] r84895 - python/branches/py3k/Doc/library/concurrent.futures.rst Message-ID: <20100919042317.96B1DEE9B6@mail.python.org> Author: benjamin.peterson Date: Sun Sep 19 06:23:17 2010 New Revision: 84895 Log: edit concurrent.future docs Modified: python/branches/py3k/Doc/library/concurrent.futures.rst Modified: python/branches/py3k/Doc/library/concurrent.futures.rst ============================================================================== --- python/branches/py3k/Doc/library/concurrent.futures.rst (original) +++ python/branches/py3k/Doc/library/concurrent.futures.rst Sun Sep 19 06:23:17 2010 @@ -7,28 +7,24 @@ The :mod:`concurrent.futures` module provides a high-level interface for asynchronously executing callables. -The asynchronous execution can be be performed by threads using -:class:`ThreadPoolExecutor` or seperate processes using +The asynchronous execution can be be performed with threads, using +:class:`ThreadPoolExecutor`, or seperate processes, using :class:`ProcessPoolExecutor`. Both implement the same interface, which is defined by the abstract :class:`Executor` class. Executor Objects ^^^^^^^^^^^^^^^^ -:class:`Executor` is an abstract class that provides methods to execute calls -asynchronously. It should not be used directly, but through its two -subclasses: :class:`ThreadPoolExecutor` and :class:`ProcessPoolExecutor`. - -.. class:: Executor() +.. class:: Executor An abstract class that provides methods to execute calls asynchronously. It - should not be used directly, but through its two subclasses: - :class:`ThreadPoolExecutor` and :class:`ProcessPoolExecutor`. + should not be used directly, but through its concrete subclasses. .. method:: submit(fn, *args, **kwargs) - Schedules the callable to be executed as *fn*(*\*args*, *\*\*kwargs*) and - returns a :class:`Future` representing the execution of the callable. + Schedules the callable, *fn*, to be executed as ``fn(*args **kwargs)`` + and returns a :class:`Future` object representing the execution of the + callable. :: @@ -38,14 +34,14 @@ .. method:: map(func, *iterables, timeout=None) - Equivalent to `map(*func*, *\*iterables*)` but func is executed + Equivalent to ``map(func, *iterables)`` except *func* is executed asynchronously and several calls to *func* may be made concurrently. The returned iterator raises a :exc:`TimeoutError` if :meth:`__next__()` is called and the result isn't available after *timeout* seconds from the - original call to :meth:`Executor.map()`. *timeout* can be an int or - float. If *timeout* is not specified or ``None`` then there is no limit - to the wait time. If a call raises an exception then that exception will - be raised when its value is retrieved from the iterator. + original call to :meth:`Executor.map`. *timeout* can be an int or a + float. If *timeout* is not specified or ``None``, there is no limit to + the wait time. If a call raises an exception, then that exception will be + raised when its value is retrieved from the iterator. .. method:: shutdown(wait=True) @@ -54,11 +50,11 @@ :meth:`Executor.submit` and :meth:`Executor.map` made after shutdown will raise :exc:`RuntimeError`. - If *wait* is `True` then this method will not return until all the + If *wait* is ``True`` then this method will not return until all the pending futures are done executing and the resources associated with the - executor have been freed. If *wait* is `False` then this method will - return immediately and the resources associated with the executor will - be freed when all pending futures are done executing. Regardless of the + executor have been freed. If *wait* is ``False`` then this method will + return immediately and the resources associated with the executor will be + freed when all pending futures are done executing. Regardless of the value of *wait*, the entire Python program will not exit until all pending futures are done executing. @@ -78,10 +74,10 @@ ThreadPoolExecutor ^^^^^^^^^^^^^^^^^^ -The :class:`ThreadPoolExecutor` class is an :class:`Executor` subclass that uses -a pool of threads to execute calls asynchronously. +:class:`ThreadPoolExecutor` is a :class:`Executor` subclass that uses a pool of +threads to execute calls asynchronously. -Deadlock can occur when the callable associated with a :class:`Future` waits on +Deadlocks can occur when the callable associated with a :class:`Future` waits on the results of another :class:`Future`. For example: :: @@ -121,9 +117,6 @@ An :class:`Executor` subclass that uses a pool of at most *max_workers* threads to execute calls asynchronously. - Deadlock can occur when the callable associated with a :class:`Future` waits - on the results of another :class:`Future`. - .. _threadpoolexecutor-example: ThreadPoolExecutor Example @@ -169,10 +162,9 @@ .. class:: ProcessPoolExecutor(max_workers=None) - An :class:`Executor` subclass that executes calls asynchronously using a - pool of at most *max_workers* processes. If *max_workers* is ``None`` or - not given then as many worker processes will be created as the machine has - processors. + An :class:`Executor` subclass that executes calls asynchronously using a pool + of at most *max_workers* processes. If *max_workers* is ``None`` or not + given, it will default to the number of processors on the machine. .. _processpoolexecutor-example: @@ -215,7 +207,7 @@ The :class:`Future` class encapulates the asynchronous execution of a callable. :class:`Future` instances are created by :meth:`Executor.submit`. -.. class:: Future() +.. class:: Future Encapulates the asynchronous execution of a callable. :class:`Future` instances are created by :meth:`Executor.submit` and should not be created @@ -223,48 +215,49 @@ .. method:: cancel() - Attempt to cancel the call. If the call is currently being executed then - it cannot be cancelled and the method will return `False`, otherwise the - call will be cancelled and the method will return `True`. + Attempt to cancel the call. If the call is currently being executed and + cannot be cancelled and the method will return ``False``, otherwise the + call will be cancelled and the method will return ``True``. .. method:: cancelled() - Return `True` if the call was successfully cancelled. + Return ``True`` if the call was successfully cancelled. .. method:: running() - Return `True` if the call is currently being executed and cannot be + Return ``True`` if the call is currently being executed and cannot be cancelled. .. method:: done() - Return `True` if the call was successfully cancelled or finished running. + Return ``True`` if the call was successfully cancelled or finished + running. .. method:: result(timeout=None) Return the value returned by the call. If the call hasn't yet completed then this method will wait up to *timeout* seconds. If the call hasn't - completed in *timeout* seconds then a :exc:`TimeoutError` will be - raised. *timeout* can be an int or float.If *timeout* is not specified - or ``None`` then there is no limit to the wait time. + completed in *timeout* seconds, then a :exc:`TimeoutError` will be + raised. *timeout* can be an int or float. If *timeout* is not specified + or ``None``, there is no limit to the wait time. If the future is cancelled before completing then :exc:`CancelledError` will be raised. - If the call raised then this method will raise the same exception. + If the call raised, this method will raise the same exception. .. method:: exception(timeout=None) Return the exception raised by the call. If the call hasn't yet completed then this method will wait up to *timeout* seconds. If the call hasn't - completed in *timeout* seconds then a :exc:`TimeoutError` will be raised. - *timeout* can be an int or float. If *timeout* is not specified or - ``None`` then there is no limit to the wait time. + completed in *timeout* seconds, then a :exc:`TimeoutError` will be + raised. *timeout* can be an int or float. If *timeout* is not specified + or ``None``, there is no limit to the wait time. If the future is cancelled before completing then :exc:`CancelledError` will be raised. - If the call completed without raising then ``None`` is returned. + If the call completed without raising, ``None`` is returned. .. method:: add_done_callback(fn) @@ -274,11 +267,11 @@ Added callables are called in the order that they were added and are always called in a thread belonging to the process that added them. If - the callable raises an :exc:`Exception` then it will be logged and - ignored. If the callable raises another :exc:`BaseException` then the - behavior is not defined. + the callable raises a :exc:`Exception` subclass, it will be logged and + ignored. If the callable raises a :exc:`BaseException` subclass, the + behavior is undefined. - If the future has already completed or been cancelled then *fn* will be + If the future has already completed or been cancelled, *fn* will be called immediately. The following :class:`Future` methods are meant for use in unit tests and @@ -326,14 +319,14 @@ .. function:: wait(fs, timeout=None, return_when=ALL_COMPLETED) Wait for the :class:`Future` instances (possibly created by different - :class:`Executor` instances) given by *fs* to complete. Returns a named - 2-tuple of sets. The first set, named "done", contains the futures that + :class:`Executor` instances) given by *fs* to complete. Returns a named + 2-tuple of sets. The first set, named ``done``, contains the futures that completed (finished or were cancelled) before the wait completed. The second - set, named "not_done", contains uncompleted futures. + set, named ``not_done``, contains uncompleted futures. *timeout* can be used to control the maximum number of seconds to wait before returning. *timeout* can be an int or float. If *timeout* is not specified or - ``None`` then there is no limit to the wait time. + ``None``, there is no limit to the wait time. *return_when* indicates when this function should return. It must be one of the following constants: @@ -348,7 +341,7 @@ | | future finishes by raising an | | | exception. If no future raises an | | | exception then it is equivalent to | - | | `ALL_COMPLETED`. | + | | :const:`ALL_COMPLETED`. | +-----------------------------+----------------------------------------+ | :const:`ALL_COMPLETED` | The function will return when all | | | futures finish or are cancelled. | @@ -356,11 +349,11 @@ .. function:: as_completed(fs, timeout=None) - Returns an iterator over the :class:`Future` instances (possibly created - by different :class:`Executor` instances) given by *fs* that yields futures - as they complete (finished or were cancelled). Any futures that completed - before :func:`as_completed()` was called will be yielded first. The returned - iterator raises a :exc:`TimeoutError` if :meth:`__next__()` is called and - the result isn't available after *timeout* seconds from the original call - to :func:`as_completed()`. *timeout* can be an int or float. If *timeout* - is not specified or ``None`` then there is no limit to the wait time. + Returns an iterator over the :class:`Future` instances (possibly created by + different :class:`Executor` instances) given by *fs* that yields futures as + they complete (finished or were cancelled). Any futures that completed before + :func:`as_completed` is called will be yielded first. The returned iterator + raises a :exc:`TimeoutError` if :meth:`__next__` is called and the result + isn't available after *timeout* seconds from the original call to + :func:`as_completed`. *timeout* can be an int or float. If *timeout* is not + specified or ``None``, there is no limit to the wait time. From python-checkins at python.org Sun Sep 19 06:26:40 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 19 Sep 2010 06:26:40 +0200 (CEST) Subject: [Python-checkins] r84896 - in python/branches/release27-maint: Lib/test/tracedmodules/__init__.py Lib/test/tracedmodules/testmod.py Message-ID: <20100919042640.03AEEEE996@mail.python.org> Author: benjamin.peterson Date: Sun Sep 19 06:26:39 2010 New Revision: 84896 Log: Merged revisions 84892 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r84892 | benjamin.peterson | 2010-09-18 22:54:31 -0500 (Sat, 18 Sep 2010) | 1 line set svn:eol-style on tracedmodules ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/tracedmodules/__init__.py (props changed) python/branches/release27-maint/Lib/test/tracedmodules/testmod.py (props changed) From python-checkins at python.org Sun Sep 19 10:20:20 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:20 +0200 Subject: [Python-checkins] distutils2: basic functional test for the test command Message-ID: tarek.ziade pushed d476e0ad4e15 to distutils2: http://hg.python.org/distutils2/rev/d476e0ad4e15 changeset: 595:d476e0ad4e15 user: Konrad Delong date: Tue Jul 27 14:31:15 2010 +0200 summary: basic functional test for the test command files: src/distutils2/command/test.py, src/distutils2/tests/test_test.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py new file mode 100644 --- /dev/null +++ b/src/distutils2/command/test.py @@ -0,0 +1,30 @@ +import os, sys +from distutils2.core import Command +import unittest + +class test(Command): + + description = "" # TODO + user_options = [ + ('test-suite=','s', + "Test suite to run (e.g. 'some_module.test_suite')"), + ] + + def initialize_options(self): + self.test_suite = None + + def finalize_options(self): + pass + + def distpath(self): + self.run_command('build') + build_cmd = self.get_finalized_command("build") + return os.path.join(build_cmd.build_base, "lib") + + def run(self): + orig_path = sys.path[:] + try: + sys.path.insert(0, self.distpath()) + unittest.main(module=self.test_suite, argv=sys.argv[:1]) + finally: + sys.path[:] = orig_path diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_test.py @@ -0,0 +1,73 @@ +import os, sys +from copy import copy +from os.path import join +from StringIO import StringIO +from distutils2.tests.support import unittest, TempdirManager +from distutils2.command.test import test +import subprocess + +TEST_BODY = '''\ +import unittest +class SomeTest(unittest.TestCase): + def test_blah(self): + self.fail("horribly") +testSuite = unittest.makeSuite(SomeTest) +''' + +SETUP_PY = '''\ +from distutils2.core import setup +setup(name='somedist', + version='0.1', + py_modules=['myowntestmodule', 'somemod'], +) +''' + +EXPECTED_OUTPUT = '''\ +FAIL: test_blah (myowntestmodule.SomeTest) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "build/lib/myowntestmodule.py", line 4, in test_blah + self.fail("horribly") +AssertionError: horribly +''' + +class TestTest(TempdirManager, unittest.TestCase): + + def setUp(self): + super(TestTest, self).setUp() + self.pkg_dir = self.prepare_dist() + self.cwd = os.getcwd() + os.chdir(self.pkg_dir) + self.orig_environ = copy(os.environ) + distutils2path = join(__file__, '..', '..', '..') + distutils2path = os.path.abspath(distutils2path) + self.old_pythonpath = os.environ.get('PYTHONPATH', '') + os.environ['PYTHONPATH'] = distutils2path + ":" + self.old_pythonpath + + def tearDown(self): + super(TestTest, self).tearDown() + os.chdir(self.cwd) + os.environ['PYTHONPATH'] = self.old_pythonpath + + def prepare_dist(self): + # prepare distribution + pkg_dir = self.mkdtemp() + self.write_file(join(pkg_dir, "setup.py"), SETUP_PY) + self.write_file(join(pkg_dir, "somemod.py"), "") + self.write_file(join(pkg_dir, "myowntestmodule.py"), TEST_BODY) + return pkg_dir + + def test_runs_simple_tests(self): + command = [sys.executable, "setup.py", "test"] + command += ['--test-suite', 'myowntestmodule'] + test_proc = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + _, errors = test_proc.communicate() + + # ensure right output + self.assertIn(EXPECTED_OUTPUT, errors) + + def _test_setup_py_accepts_options(self): + pass + + def _test_options_preparation(self): + pass -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:20 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:20 +0200 Subject: [Python-checkins] distutils2: updated docs for the test command Message-ID: tarek.ziade pushed 05b3f45d94f6 to distutils2: http://hg.python.org/distutils2/rev/05b3f45d94f6 changeset: 596:05b3f45d94f6 user: Konrad Delong date: Tue Jul 27 14:33:40 2010 +0200 summary: updated docs for the test command files: docs/source/new_commands.rst diff --git a/docs/source/new_commands.rst b/docs/source/new_commands.rst --- a/docs/source/new_commands.rst +++ b/docs/source/new_commands.rst @@ -6,6 +6,56 @@ You might recognize some of them from other projects, like Distribute or Setuptools. + +``test`` - Build package and run a unittest suite +================================================= + +When doing test-driven development, or running automated builds that need +testing before they are deployed for downloading or use, it's often useful +to be able to run a project's unit tests without actually deploying the project +anywhere, even using the ``develop`` command. The ``test`` command runs a +project's unit tests without actually deploying it, by temporarily putting the +project's source on ``sys.path``, after first running ``build_ext -i`` and +``egg_info`` to ensure that any C extensions and project metadata are +up-to-date. + +To use this command, your project's tests must be wrapped in a ``unittest`` +test suite by either a function, a ``TestCase`` class or method, or a module +or package containing ``TestCase`` classes. If the named suite is a module, +and the module has an ``additional_tests()`` function, it is called and the +result (which must be a ``unittest.TestSuite``) is added to the tests to be +run. If the named suite is a package, any submodules and subpackages are +recursively added to the overall test suite. (Note: if your project specifies +a ``test_loader``, the rules for processing the chosen ``test_suite`` may +differ; see the `test_loader`_ documentation for more details.) + +Note that many test systems including ``doctest`` support wrapping their +non-``unittest`` tests in ``TestSuite`` objects. So, if you are using a test +package that does not support this, we suggest you encourage its developers to +implement test suite support, as this is a convenient and standard way to +aggregate a collection of tests to be run under a common test harness. + +By default, tests will be run in the "verbose" mode of the ``unittest`` +package's text test runner, but you can get the "quiet" mode (just dots) if +you supply the ``-q`` or ``--quiet`` option, either as a global option to +the setup script (e.g. ``setup.py -q test``) or as an option for the ``test`` +command itself (e.g. ``setup.py test -q``). There is one other option +available: + +``--test-suite=NAME, -s NAME`` + Specify the test suite (or module, class, or method) to be run + (e.g. ``some_module.test_suite``). The default for this option can be + set by giving a ``test_suite`` argument to the ``setup()`` function, e.g.:: + + setup( + # ... + test_suite = "my_package.tests.test_all" + ) + + If you did not set a ``test_suite`` in your ``setup()`` call, and do not + provide a ``--test-suite`` option, an error will occur. + + ``upload_docs`` - Upload package documentation to PyPI ====================================================== -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:20 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:20 +0200 Subject: [Python-checkins] distutils2: broken test_suite option passing Message-ID: tarek.ziade pushed 18cb03289181 to distutils2: http://hg.python.org/distutils2/rev/18cb03289181 changeset: 597:18cb03289181 user: Konrad Delong date: Tue Jul 27 14:36:13 2010 +0200 summary: broken test_suite option passing files: src/distutils2/tests/test_test.py diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -19,6 +19,7 @@ setup(name='somedist', version='0.1', py_modules=['myowntestmodule', 'somemod'], + test_suite='myowntestmodule', ) ''' @@ -59,7 +60,6 @@ def test_runs_simple_tests(self): command = [sys.executable, "setup.py", "test"] - command += ['--test-suite', 'myowntestmodule'] test_proc = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) _, errors = test_proc.communicate() -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:20 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:20 +0200 Subject: [Python-checkins] distutils2: added an alias to dist.py Message-ID: tarek.ziade pushed 594442792a93 to distutils2: http://hg.python.org/distutils2/rev/594442792a93 changeset: 598:594442792a93 user: Konrad Delong date: Tue Jul 27 14:39:17 2010 +0200 summary: added an alias to dist.py files: src/distutils2/command/test.py, src/distutils2/dist.py, src/distutils2/tests/test_test.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -14,7 +14,7 @@ self.test_suite = None def finalize_options(self): - pass + self.test_suite = self.distribution.test_suite def distpath(self): self.run_command('build') diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -204,6 +204,7 @@ self.scripts = None self.data_files = None self.password = '' + self.test_suite = None # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -64,7 +64,17 @@ _, errors = test_proc.communicate() # ensure right output - self.assertIn(EXPECTED_OUTPUT, errors) + self.assert_multiline_substring(EXPECTED_OUTPUT, errors) + + def assert_multiline_substring(self, a, b): + def quote(s): + lines = ['## ' + line for line in s.split('\n')] + sep = ["#" * 60] + return [''] + sep + lines + sep + msg = quote(a) + ['not found in:'] + quote(b) + msg = "\n".join(msg) + if a not in b: + self.fail(msg) def _test_setup_py_accepts_options(self): pass -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: refactored test command tests and added another functional test Message-ID: tarek.ziade pushed 9e054e62e8d6 to distutils2: http://hg.python.org/distutils2/rev/9e054e62e8d6 changeset: 600:9e054e62e8d6 user: Konrad Delong date: Tue Jul 27 14:53:37 2010 +0200 summary: refactored test command tests and added another functional test files: src/distutils2/_backport/__init__.py, src/distutils2/command/test.py, src/distutils2/tests/dists/extensions_test/myowntestmodule.py, src/distutils2/tests/dists/extensions_test/setup.cfg, src/distutils2/tests/dists/extensions_test/setup.py, src/distutils2/tests/dists/extensions_test/spammodule.c, src/distutils2/tests/dists/simple_test/myowntestmodule.py, src/distutils2/tests/dists/simple_test/setup.cfg, src/distutils2/tests/dists/simple_test/setup.py, src/distutils2/tests/test_test.py diff --git a/src/distutils2/_backport/__init__.py b/src/distutils2/_backport/__init__.py --- a/src/distutils2/_backport/__init__.py +++ b/src/distutils2/_backport/__init__.py @@ -1,2 +1,8 @@ """Things that will land in the Python 3.3 std lib but which we must drag along with us for now to support 2.x.""" + +def any(seq): + for elem in seq: + if elem: + return elem + return False diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -14,17 +14,16 @@ self.test_suite = None def finalize_options(self): - pass - - def distpath(self): - self.run_command('build') - build_cmd = self.get_finalized_command("build") - return os.path.join(build_cmd.build_base, "lib") + self.build_lib = self.get_finalized_command("build").build_lib def run(self): - orig_path = sys.path[:] + prev_cwd = os.getcwd() try: - sys.path.insert(0, self.distpath()) + if self.distribution.has_ext_modules(): + build = self.get_reinitialized_command('build') + build.inplace = 1 + self.run_command('build') + os.chdir(self.build_lib) unittest.main(module=self.test_suite, argv=sys.argv[:1]) finally: - sys.path[:] = orig_path + os.chdir(prev_cwd) diff --git a/src/distutils2/tests/dists/extensions_test/myowntestmodule.py b/src/distutils2/tests/dists/extensions_test/myowntestmodule.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/myowntestmodule.py @@ -0,0 +1,4 @@ +import unittest +class SomeTest(unittest.TestCase): + def test_blah(self): + self.fail("horribly") diff --git a/src/distutils2/tests/dists/extensions_test/setup.cfg b/src/distutils2/tests/dists/extensions_test/setup.cfg new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/setup.cfg @@ -0,0 +1,2 @@ +[test] +test-suite = myowntestmodule diff --git a/src/distutils2/tests/dists/extensions_test/setup.py b/src/distutils2/tests/dists/extensions_test/setup.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/setup.py @@ -0,0 +1,6 @@ +from distutils2.core import setup, Extension +setup(name='somedist', + version='0.1', + py_modules=['myowntestmodule', 'somemod'], + ext_modules=[Extension('spam', ['spammodule.c'])], +) diff --git a/src/distutils2/tests/dists/extensions_test/spammodule.c b/src/distutils2/tests/dists/extensions_test/spammodule.c new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/spammodule.c @@ -0,0 +1,11 @@ +#include + +static PyMethodDef SpamMethods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyMODINIT_FUNC +initspam(void) +{ + (void) Py_InitModule("spam", SpamMethods); +} diff --git a/src/distutils2/tests/dists/simple_test/myowntestmodule.py b/src/distutils2/tests/dists/simple_test/myowntestmodule.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/simple_test/myowntestmodule.py @@ -0,0 +1,4 @@ +import unittest +class SomeTest(unittest.TestCase): + def test_blah(self): + self.fail("horribly") diff --git a/src/distutils2/tests/dists/simple_test/setup.cfg b/src/distutils2/tests/dists/simple_test/setup.cfg new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/simple_test/setup.cfg @@ -0,0 +1,2 @@ +[test] +test-suite = myowntestmodule diff --git a/src/distutils2/tests/dists/simple_test/setup.py b/src/distutils2/tests/dists/simple_test/setup.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/simple_test/setup.py @@ -0,0 +1,5 @@ +from distutils2.core import setup +setup(name='somedist', + version='0.1', + py_modules=['myowntestmodule', 'somemod'], +) diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -1,4 +1,4 @@ -import os, sys +import os, sys, shutil, re from copy import copy from os.path import join from StringIO import StringIO @@ -6,33 +6,17 @@ from distutils2.command.test import test import subprocess -TEST_BODY = '''\ -import unittest -class SomeTest(unittest.TestCase): - def test_blah(self): - self.fail("horribly") -testSuite = unittest.makeSuite(SomeTest) -''' +try: + any +except NameError: + from distutils2._backport import any -SETUP_PY = '''\ -from distutils2.core import setup -setup(name='somedist', - version='0.1', - py_modules=['myowntestmodule', 'somemod'], -) -''' - -SETUP_CFG = '''\ -[test] -test-suite = myowntestmodule -''' - -EXPECTED_OUTPUT = '''\ -FAIL: test_blah (myowntestmodule.SomeTest) +EXPECTED_OUTPUT_RE = '''\ +FAIL: test_blah \\(myowntestmodule.SomeTest\\) ---------------------------------------------------------------------- -Traceback (most recent call last): - File "build/lib/myowntestmodule.py", line 4, in test_blah - self.fail("horribly") +Traceback \\(most recent call last\\): + File ".+/myowntestmodule.py", line \\d+, in test_blah + self.fail\\("horribly"\\) AssertionError: horribly ''' @@ -40,10 +24,7 @@ def setUp(self): super(TestTest, self).setUp() - self.pkg_dir = self.prepare_dist() - self.cwd = os.getcwd() - os.chdir(self.pkg_dir) - self.orig_environ = copy(os.environ) + distutils2path = join(__file__, '..', '..', '..') distutils2path = os.path.abspath(distutils2path) self.old_pythonpath = os.environ.get('PYTHONPATH', '') @@ -51,38 +32,54 @@ def tearDown(self): super(TestTest, self).tearDown() - os.chdir(self.cwd) os.environ['PYTHONPATH'] = self.old_pythonpath - def prepare_dist(self): - # prepare distribution - pkg_dir = self.mkdtemp() - self.write_file(join(pkg_dir, "setup.py"), SETUP_PY) - self.write_file(join(pkg_dir, "setup.cfg"), SETUP_CFG) - self.write_file(join(pkg_dir, "somemod.py"), "") - self.write_file(join(pkg_dir, "myowntestmodule.py"), TEST_BODY) - return pkg_dir - - def test_runs_simple_tests(self): - command = [sys.executable, "setup.py", "test"] - test_proc = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) - _, errors = test_proc.communicate() - - # ensure right output - self.assert_multiline_substring(EXPECTED_OUTPUT, errors) - - def assert_multiline_substring(self, a, b): + def assert_re_match(self, pattern, string): def quote(s): - lines = ['## ' + line for line in s.split('\n')] + lines = ['## ' + line for line in s.split('\n')] sep = ["#" * 60] return [''] + sep + lines + sep - msg = quote(a) + ['not found in:'] + quote(b) + msg = quote(pattern) + ["didn't match"] + quote(string) msg = "\n".join(msg) - if a not in b: + if not re.search(pattern, string): self.fail(msg) - def _test_setup_py_accepts_options(self): + def run_with_dist_cwd(self, pkg_dir): + cwd = os.getcwd() + command = [sys.executable, "setup.py", "test"] + try: + os.chdir(pkg_dir) + test_proc = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + _, errors = test_proc.communicate() + return errors + finally: + os.chdir(cwd) + + def prepare_dist(self, dist_name): + pkg_dir = join(os.path.dirname(__file__), "dists", dist_name) + temp_pkg_dir = join(self.mkdtemp(), dist_name) + shutil.copytree(pkg_dir, temp_pkg_dir) + return temp_pkg_dir + + def test_runs_simple_tests(self): + self.pkg_dir = self.prepare_dist('simple_test') + output = self.run_with_dist_cwd(self.pkg_dir) + self.assert_re_match(EXPECTED_OUTPUT_RE, output) + self.assertFalse(os.path.exists(join(self.pkg_dir, 'build'))) + + def test_builds_extensions(self): + self.pkg_dir = self.prepare_dist("extensions_test") + output = self.run_with_dist_cwd(self.pkg_dir) + self.assert_re_match(EXPECTED_OUTPUT_RE, output) + self.assertTrue(os.path.exists(join(self.pkg_dir, 'build'))) + self.assertTrue(any(x.startswith('lib') for x in os.listdir(join(self.pkg_dir, 'build')))) + + def _test_custom_test_loader(self): pass - def _test_options_preparation(self): + def _test_downloads_test_requires(self): pass + +def test_suite(): + suite = [unittest.makeSuite(TestTest)] + return unittest.TestSuite(suite) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: switched back to "the right way" of setting test_suite Message-ID: tarek.ziade pushed 80798d49fff0 to distutils2: http://hg.python.org/distutils2/rev/80798d49fff0 changeset: 599:80798d49fff0 user: Konrad Delong date: Tue Jul 27 14:41:20 2010 +0200 summary: switched back to "the right way" of setting test_suite files: src/distutils2/command/test.py, src/distutils2/dist.py, src/distutils2/tests/test_test.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -14,7 +14,7 @@ self.test_suite = None def finalize_options(self): - self.test_suite = self.distribution.test_suite + pass def distpath(self): self.run_command('build') diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -204,7 +204,6 @@ self.scripts = None self.data_files = None self.password = '' - self.test_suite = None # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -19,10 +19,14 @@ setup(name='somedist', version='0.1', py_modules=['myowntestmodule', 'somemod'], - test_suite='myowntestmodule', ) ''' +SETUP_CFG = '''\ +[test] +test-suite = myowntestmodule +''' + EXPECTED_OUTPUT = '''\ FAIL: test_blah (myowntestmodule.SomeTest) ---------------------------------------------------------------------- @@ -54,6 +58,7 @@ # prepare distribution pkg_dir = self.mkdtemp() self.write_file(join(pkg_dir, "setup.py"), SETUP_PY) + self.write_file(join(pkg_dir, "setup.cfg"), SETUP_CFG) self.write_file(join(pkg_dir, "somemod.py"), "") self.write_file(join(pkg_dir, "myowntestmodule.py"), TEST_BODY) return pkg_dir -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: roadmap Message-ID: tarek.ziade pushed 2632fe1f6e9c to distutils2: http://hg.python.org/distutils2/rev/2632fe1f6e9c changeset: 601:2632fe1f6e9c user: Konrad Delong date: Tue Jul 27 14:55:31 2010 +0200 summary: roadmap files: src/distutils2/tests/test_test.py diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -74,7 +74,13 @@ self.assertTrue(os.path.exists(join(self.pkg_dir, 'build'))) self.assertTrue(any(x.startswith('lib') for x in os.listdir(join(self.pkg_dir, 'build')))) - def _test_custom_test_loader(self): + def test_custom_test_loader(self): + pass + + def _test_works_with_2to3(self): + pass + + def _test_prepares_egg_info(self): pass def _test_downloads_test_requires(self): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:20 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:20 +0200 Subject: [Python-checkins] distutils2: added a version number when uploading Message-ID: tarek.ziade pushed 0a164335cea3 to distutils2: http://hg.python.org/distutils2/rev/0a164335cea3 changeset: 594:0a164335cea3 parent: 303:f26151026663 user: Konrad Delong date: Tue Jul 27 14:28:17 2010 +0200 summary: added a version number when uploading files: src/distutils2/command/upload_docs.py, src/distutils2/tests/test_upload_docs.py diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -83,9 +83,10 @@ def run(self): tmp_dir = tempfile.mkdtemp() name = self.distribution.metadata['Name'] + version = self.distribution.metadata['Version'] zip_file = zip_dir(self.upload_dir) - fields = {':action': 'doc_upload', 'name': name}.items() + fields = [(':action', 'doc_upload'), ('name', name), ('version', version)] files = [('content', name, zip_file.getvalue())] content_type, body = encode_multipart(fields, files) diff --git a/src/distutils2/tests/test_upload_docs.py b/src/distutils2/tests/test_upload_docs.py --- a/src/distutils2/tests/test_upload_docs.py +++ b/src/distutils2/tests/test_upload_docs.py @@ -107,12 +107,13 @@ self.assertTrue(handler.headers.dict['content-type'] .startswith('multipart/form-data;')) - action, name, content =\ - request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:4] + action, name, version, content =\ + request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:5] # check that we picked the right chunks self.assertIn('name=":action"', action) self.assertIn('name="name"', name) + self.assertIn('name="version"', version) self.assertIn('name="content"', content) # check their contents -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: test loader with dotted syntax now working Message-ID: tarek.ziade pushed 10530a984ad7 to distutils2: http://hg.python.org/distutils2/rev/10530a984ad7 changeset: 602:10530a984ad7 user: Konrad Delong date: Tue Jul 27 14:56:41 2010 +0200 summary: test loader with dotted syntax now working files: src/distutils2/command/test.py, src/distutils2/tests/dists/custom_loader/myowntestmodule.py, src/distutils2/tests/dists/custom_loader/setup.cfg, src/distutils2/tests/dists/custom_loader/setup.py, src/distutils2/tests/test_test.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -2,16 +2,39 @@ from distutils2.core import Command import unittest +def get_loader_instance(dotted_path): + if dotted_path is None: + return None + module_name, rest = dotted_path.split('.')[0], dotted_path.split('.')[1:] + while True: + try: + ret = __import__(module_name) + break + except ImportError: + if rest == []: + return None + module_name += ('.' + rest[0]) + rest = rest[1:] + while rest: + try: + ret = getattr(ret, rest.pop(0)) + except AttributeError: + return None + return ret() + class test(Command): description = "" # TODO user_options = [ - ('test-suite=','s', + ('test-suite=', 's', "Test suite to run (e.g. 'some_module.test_suite')"), + ('test-loader=', None, + "Test loader to be used to load the test suite."), ] def initialize_options(self): - self.test_suite = None + self.test_suite = None + self.test_loader = None def finalize_options(self): self.build_lib = self.get_finalized_command("build").build_lib @@ -21,9 +44,15 @@ try: if self.distribution.has_ext_modules(): build = self.get_reinitialized_command('build') - build.inplace = 1 + build.inplace = 1 # TODO - remove make sure it's needed self.run_command('build') os.chdir(self.build_lib) - unittest.main(module=self.test_suite, argv=sys.argv[:1]) + args = {"module": self.test_suite, + "argv": sys.argv[:1], + "testLoader": get_loader_instance(self.test_loader) + } + if args['testLoader'] is None: + del args['testLoader'] + unittest.main(**args) finally: os.chdir(prev_cwd) diff --git a/src/distutils2/tests/dists/custom_loader/myowntestmodule.py b/src/distutils2/tests/dists/custom_loader/myowntestmodule.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/custom_loader/myowntestmodule.py @@ -0,0 +1,9 @@ +import unittest +class Loader(object): + def loadTestsFromModule(self, names, module=None): + class SomeTest(unittest.TestCase): + def test_blah(self): + self.fail("horribly") + return unittest.makeSuite(SomeTest) + def __repr__(self): + return 'YO' diff --git a/src/distutils2/tests/dists/custom_loader/setup.cfg b/src/distutils2/tests/dists/custom_loader/setup.cfg new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/custom_loader/setup.cfg @@ -0,0 +1,2 @@ +[test] +test-loader = myowntestmodule.Loader diff --git a/src/distutils2/tests/dists/custom_loader/setup.py b/src/distutils2/tests/dists/custom_loader/setup.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/custom_loader/setup.py @@ -0,0 +1,5 @@ +from distutils2.core import setup +setup(name='somedist', + version='0.1', + py_modules=['myowntestmodule', 'somemod'], +) diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -75,7 +75,9 @@ self.assertTrue(any(x.startswith('lib') for x in os.listdir(join(self.pkg_dir, 'build')))) def test_custom_test_loader(self): - pass + self.pkg_dir = self.prepare_dist("custom_loader") + output = self.run_with_dist_cwd(self.pkg_dir) + self.assert_re_match(EXPECTED_OUTPUT_RE, output) def _test_works_with_2to3(self): pass -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: removed unnecessary test Message-ID: tarek.ziade pushed 3fd1d3761c9f to distutils2: http://hg.python.org/distutils2/rev/3fd1d3761c9f changeset: 603:3fd1d3761c9f user: Konrad Delong date: Tue Jul 27 14:57:46 2010 +0200 summary: removed unnecessary test files: src/distutils2/tests/test_test.py diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -82,9 +82,6 @@ def _test_works_with_2to3(self): pass - def _test_prepares_egg_info(self): - pass - def _test_downloads_test_requires(self): pass -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: fdrake's suggested improvements implemented Message-ID: tarek.ziade pushed 8fbadd7511f7 to distutils2: http://hg.python.org/distutils2/rev/8fbadd7511f7 changeset: 604:8fbadd7511f7 user: Konrad Delong date: Tue Jul 27 14:59:11 2010 +0200 summary: fdrake's suggested improvements implemented files: src/distutils2/tests/test_test.py diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -11,28 +11,29 @@ except NameError: from distutils2._backport import any -EXPECTED_OUTPUT_RE = '''\ -FAIL: test_blah \\(myowntestmodule.SomeTest\\) +EXPECTED_OUTPUT_RE = r'''FAIL: test_blah \(myowntestmodule.SomeTest\) ---------------------------------------------------------------------- -Traceback \\(most recent call last\\): - File ".+/myowntestmodule.py", line \\d+, in test_blah - self.fail\\("horribly"\\) +Traceback \(most recent call last\): + File ".+/myowntestmodule.py", line \d+, in test_blah + self.fail\("horribly"\) AssertionError: horribly ''' +here = os.path.dirname(os.path.abspath(__file__)) + + class TestTest(TempdirManager, unittest.TestCase): def setUp(self): super(TestTest, self).setUp() - distutils2path = join(__file__, '..', '..', '..') - distutils2path = os.path.abspath(distutils2path) + distutils2path = os.path.dirname(os.path.dirname(here)) self.old_pythonpath = os.environ.get('PYTHONPATH', '') - os.environ['PYTHONPATH'] = distutils2path + ":" + self.old_pythonpath + os.environ['PYTHONPATH'] = distutils2path + os.pathsep + self.old_pythonpath def tearDown(self): + os.environ['PYTHONPATH'] = self.old_pythonpath super(TestTest, self).tearDown() - os.environ['PYTHONPATH'] = self.old_pythonpath def assert_re_match(self, pattern, string): def quote(s): @@ -86,5 +87,4 @@ pass def test_suite(): - suite = [unittest.makeSuite(TestTest)] - return unittest.TestSuite(suite) + return unittest.makeSuite(TestTest) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: emitting warnings instead of installing distributions from option Message-ID: tarek.ziade pushed cbcd1ec808dc to distutils2: http://hg.python.org/distutils2/rev/cbcd1ec808dc changeset: 605:cbcd1ec808dc user: Konrad Delong date: Tue Jul 27 15:06:44 2010 +0200 summary: emitting warnings instead of installing distributions from option files: src/distutils2/command/test.py, src/distutils2/dist.py, src/distutils2/tests/test_test.py, src/distutils2/tests/with_support.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -1,6 +1,8 @@ import os, sys from distutils2.core import Command +from distutils2._backport.pkgutil import get_distribution import unittest +import warnings def get_loader_instance(dotted_path): if dotted_path is None: @@ -38,6 +40,11 @@ def finalize_options(self): self.build_lib = self.get_finalized_command("build").build_lib + if self.distribution.tests_require: + for requirement in self.distribution.tests_require: + if get_distribution(requirement) is None: + warnings.warn("The test dependency %s is not installed which may couse the tests to fail.", + RuntimeWarning) def run(self): prev_cwd = os.getcwd() diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -109,6 +109,8 @@ "print the list of packages/modules provided"), ('requires', None, "print the list of packages/modules required"), + ('tests-require', None, + "print the list of packages/modules required to run the test suite"), ('obsoletes', None, "print the list of packages/modules made obsolete") ] diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -4,6 +4,7 @@ from StringIO import StringIO from distutils2.tests.support import unittest, TempdirManager from distutils2.command.test import test +from distutils2.dist import Distribution import subprocess try: @@ -83,8 +84,19 @@ def _test_works_with_2to3(self): pass - def _test_downloads_test_requires(self): - pass + @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + def test_checks_requires(self): + from distutils2.tests.with_support import examine_warnings + dist = Distribution() + dist.tests_require = ['ohno_ohno-impossible_1234-name_stop-that!'] + cmd = test(dist) + def examinator(ws): + cmd.ensure_finalized() + self.assertEqual(1, len(ws)) + warning = ws[0] + self.assertIs(warning.category, RuntimeWarning) + + examine_warnings(examinator) def test_suite(): return unittest.makeSuite(TestTest) diff --git a/src/distutils2/tests/with_support.py b/src/distutils2/tests/with_support.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/with_support.py @@ -0,0 +1,7 @@ +def examine_warnings(examinator): + """Given an examinator function calls it as if the code was under with + catch_warnings block. Useful for testing older Python versions""" + import warnings + warnings.simplefilter('default') + with warnings.catch_warnings(record=True) as ws: + examinator(ws) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: merged from upstream Message-ID: tarek.ziade pushed 59d10bf5a35c to distutils2: http://hg.python.org/distutils2/rev/59d10bf5a35c changeset: 607:59d10bf5a35c parent: 436:70cc02f1c720 parent: 606:4e01a65bfa99 user: Konrad Delong date: Thu Aug 05 18:17:38 2010 +0200 summary: merged from upstream files: src/distutils2/command/upload_docs.py, src/distutils2/dist.py, src/distutils2/tests/test_upload_docs.py diff --git a/docs/source/new_commands.rst b/docs/source/new_commands.rst --- a/docs/source/new_commands.rst +++ b/docs/source/new_commands.rst @@ -6,6 +6,56 @@ You might recognize some of them from other projects, like Distribute or Setuptools. + +``test`` - Build package and run a unittest suite +================================================= + +When doing test-driven development, or running automated builds that need +testing before they are deployed for downloading or use, it's often useful +to be able to run a project's unit tests without actually deploying the project +anywhere, even using the ``develop`` command. The ``test`` command runs a +project's unit tests without actually deploying it, by temporarily putting the +project's source on ``sys.path``, after first running ``build_ext -i`` and +``egg_info`` to ensure that any C extensions and project metadata are +up-to-date. + +To use this command, your project's tests must be wrapped in a ``unittest`` +test suite by either a function, a ``TestCase`` class or method, or a module +or package containing ``TestCase`` classes. If the named suite is a module, +and the module has an ``additional_tests()`` function, it is called and the +result (which must be a ``unittest.TestSuite``) is added to the tests to be +run. If the named suite is a package, any submodules and subpackages are +recursively added to the overall test suite. (Note: if your project specifies +a ``test_loader``, the rules for processing the chosen ``test_suite`` may +differ; see the `test_loader`_ documentation for more details.) + +Note that many test systems including ``doctest`` support wrapping their +non-``unittest`` tests in ``TestSuite`` objects. So, if you are using a test +package that does not support this, we suggest you encourage its developers to +implement test suite support, as this is a convenient and standard way to +aggregate a collection of tests to be run under a common test harness. + +By default, tests will be run in the "verbose" mode of the ``unittest`` +package's text test runner, but you can get the "quiet" mode (just dots) if +you supply the ``-q`` or ``--quiet`` option, either as a global option to +the setup script (e.g. ``setup.py -q test``) or as an option for the ``test`` +command itself (e.g. ``setup.py test -q``). There is one other option +available: + +``--test-suite=NAME, -s NAME`` + Specify the test suite (or module, class, or method) to be run + (e.g. ``some_module.test_suite``). The default for this option can be + set by giving a ``test_suite`` argument to the ``setup()`` function, e.g.:: + + setup( + # ... + test_suite = "my_package.tests.test_all" + ) + + If you did not set a ``test_suite`` in your ``setup()`` call, and do not + provide a ``--test-suite`` option, an error will occur. + + ``upload_docs`` - Upload package documentation to PyPI ====================================================== diff --git a/src/distutils2/_backport/__init__.py b/src/distutils2/_backport/__init__.py --- a/src/distutils2/_backport/__init__.py +++ b/src/distutils2/_backport/__init__.py @@ -1,2 +1,8 @@ """Things that will land in the Python 3.3 std lib but which we must drag along with us for now to support 2.x.""" + +def any(seq): + for elem in seq: + if elem: + return elem + return False diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py new file mode 100644 --- /dev/null +++ b/src/distutils2/command/test.py @@ -0,0 +1,65 @@ +import os, sys +from distutils2.core import Command +from distutils2._backport.pkgutil import get_distribution +import unittest +import warnings + +def get_loader_instance(dotted_path): + if dotted_path is None: + return None + module_name, rest = dotted_path.split('.')[0], dotted_path.split('.')[1:] + while True: + try: + ret = __import__(module_name) + break + except ImportError: + if rest == []: + return None + module_name += ('.' + rest[0]) + rest = rest[1:] + while rest: + try: + ret = getattr(ret, rest.pop(0)) + except AttributeError: + return None + return ret() + +class test(Command): + + description = "" # TODO + user_options = [ + ('test-suite=', 's', + "Test suite to run (e.g. 'some_module.test_suite')"), + ('test-loader=', None, + "Test loader to be used to load the test suite."), + ] + + def initialize_options(self): + self.test_suite = None + self.test_loader = None + + def finalize_options(self): + self.build_lib = self.get_finalized_command("build").build_lib + if self.distribution.tests_require: + for requirement in self.distribution.tests_require: + if get_distribution(requirement) is None: + warnings.warn("The test dependency %s is not installed which may couse the tests to fail.", + RuntimeWarning) + + def run(self): + prev_cwd = os.getcwd() + try: + if self.distribution.has_ext_modules(): + build = self.get_reinitialized_command('build') + build.inplace = 1 # TODO - remove make sure it's needed + self.run_command('build') + os.chdir(self.build_lib) + args = {"module": self.test_suite, + "argv": sys.argv[:1], + "testLoader": get_loader_instance(self.test_loader) + } + if args['testLoader'] is None: + del args['testLoader'] + unittest.main(**args) + finally: + os.chdir(prev_cwd) diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -94,9 +94,10 @@ def run(self): name = self.distribution.metadata['Name'] + version = self.distribution.metadata['Version'] zip_file = zip_dir(self.upload_dir) - fields = {':action': 'doc_upload', 'name': name}.items() + fields = [(':action', 'doc_upload'), ('name', name), ('version', version)] files = [('content', name, zip_file.getvalue())] content_type, body = encode_multipart(fields, files) diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -111,6 +111,8 @@ "print the list of packages/modules provided"), ('requires', None, "print the list of packages/modules required"), + ('tests-require', None, + "print the list of packages/modules required to run the test suite"), ('obsoletes', None, "print the list of packages/modules made obsolete"), ('use-2to3', None, diff --git a/src/distutils2/tests/dists/custom_loader/myowntestmodule.py b/src/distutils2/tests/dists/custom_loader/myowntestmodule.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/custom_loader/myowntestmodule.py @@ -0,0 +1,9 @@ +import unittest +class Loader(object): + def loadTestsFromModule(self, names, module=None): + class SomeTest(unittest.TestCase): + def test_blah(self): + self.fail("horribly") + return unittest.makeSuite(SomeTest) + def __repr__(self): + return 'YO' diff --git a/src/distutils2/tests/dists/custom_loader/setup.cfg b/src/distutils2/tests/dists/custom_loader/setup.cfg new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/custom_loader/setup.cfg @@ -0,0 +1,2 @@ +[test] +test-loader = myowntestmodule.Loader diff --git a/src/distutils2/tests/dists/custom_loader/setup.py b/src/distutils2/tests/dists/custom_loader/setup.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/custom_loader/setup.py @@ -0,0 +1,5 @@ +from distutils2.core import setup +setup(name='somedist', + version='0.1', + py_modules=['myowntestmodule', 'somemod'], +) diff --git a/src/distutils2/tests/dists/extensions_test/myowntestmodule.py b/src/distutils2/tests/dists/extensions_test/myowntestmodule.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/myowntestmodule.py @@ -0,0 +1,4 @@ +import unittest +class SomeTest(unittest.TestCase): + def test_blah(self): + self.fail("horribly") diff --git a/src/distutils2/tests/dists/extensions_test/setup.cfg b/src/distutils2/tests/dists/extensions_test/setup.cfg new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/setup.cfg @@ -0,0 +1,2 @@ +[test] +test-suite = myowntestmodule diff --git a/src/distutils2/tests/dists/extensions_test/setup.py b/src/distutils2/tests/dists/extensions_test/setup.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/setup.py @@ -0,0 +1,6 @@ +from distutils2.core import setup, Extension +setup(name='somedist', + version='0.1', + py_modules=['myowntestmodule', 'somemod'], + ext_modules=[Extension('spam', ['spammodule.c'])], +) diff --git a/src/distutils2/tests/dists/extensions_test/spammodule.c b/src/distutils2/tests/dists/extensions_test/spammodule.c new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/spammodule.c @@ -0,0 +1,11 @@ +#include + +static PyMethodDef SpamMethods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyMODINIT_FUNC +initspam(void) +{ + (void) Py_InitModule("spam", SpamMethods); +} diff --git a/src/distutils2/tests/dists/simple_test/myowntestmodule.py b/src/distutils2/tests/dists/simple_test/myowntestmodule.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/simple_test/myowntestmodule.py @@ -0,0 +1,4 @@ +import unittest +class SomeTest(unittest.TestCase): + def test_blah(self): + self.fail("horribly") diff --git a/src/distutils2/tests/dists/simple_test/setup.cfg b/src/distutils2/tests/dists/simple_test/setup.cfg new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/simple_test/setup.cfg @@ -0,0 +1,2 @@ +[test] +test-suite = myowntestmodule diff --git a/src/distutils2/tests/dists/simple_test/setup.py b/src/distutils2/tests/dists/simple_test/setup.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/simple_test/setup.py @@ -0,0 +1,5 @@ +from distutils2.core import setup +setup(name='somedist', + version='0.1', + py_modules=['myowntestmodule', 'somemod'], +) diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_test.py @@ -0,0 +1,102 @@ +import os, sys, shutil, re +from copy import copy +from os.path import join +from StringIO import StringIO +from distutils2.tests.support import unittest, TempdirManager +from distutils2.command.test import test +from distutils2.dist import Distribution +import subprocess + +try: + any +except NameError: + from distutils2._backport import any + +EXPECTED_OUTPUT_RE = r'''FAIL: test_blah \(myowntestmodule.SomeTest\) +---------------------------------------------------------------------- +Traceback \(most recent call last\): + File ".+/myowntestmodule.py", line \d+, in test_blah + self.fail\("horribly"\) +AssertionError: horribly +''' + +here = os.path.dirname(os.path.abspath(__file__)) + + +class TestTest(TempdirManager, unittest.TestCase): + + def setUp(self): + super(TestTest, self).setUp() + + distutils2path = os.path.dirname(os.path.dirname(here)) + self.old_pythonpath = os.environ.get('PYTHONPATH', '') + os.environ['PYTHONPATH'] = distutils2path + os.pathsep + self.old_pythonpath + + def tearDown(self): + os.environ['PYTHONPATH'] = self.old_pythonpath + super(TestTest, self).tearDown() + + def assert_re_match(self, pattern, string): + def quote(s): + lines = ['## ' + line for line in s.split('\n')] + sep = ["#" * 60] + return [''] + sep + lines + sep + msg = quote(pattern) + ["didn't match"] + quote(string) + msg = "\n".join(msg) + if not re.search(pattern, string): + self.fail(msg) + + def run_with_dist_cwd(self, pkg_dir): + cwd = os.getcwd() + command = [sys.executable, "setup.py", "test"] + try: + os.chdir(pkg_dir) + test_proc = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + _, errors = test_proc.communicate() + return errors + finally: + os.chdir(cwd) + + def prepare_dist(self, dist_name): + pkg_dir = join(os.path.dirname(__file__), "dists", dist_name) + temp_pkg_dir = join(self.mkdtemp(), dist_name) + shutil.copytree(pkg_dir, temp_pkg_dir) + return temp_pkg_dir + + def test_runs_simple_tests(self): + self.pkg_dir = self.prepare_dist('simple_test') + output = self.run_with_dist_cwd(self.pkg_dir) + self.assert_re_match(EXPECTED_OUTPUT_RE, output) + self.assertFalse(os.path.exists(join(self.pkg_dir, 'build'))) + + def test_builds_extensions(self): + self.pkg_dir = self.prepare_dist("extensions_test") + output = self.run_with_dist_cwd(self.pkg_dir) + self.assert_re_match(EXPECTED_OUTPUT_RE, output) + self.assertTrue(os.path.exists(join(self.pkg_dir, 'build'))) + self.assertTrue(any(x.startswith('lib') for x in os.listdir(join(self.pkg_dir, 'build')))) + + def test_custom_test_loader(self): + self.pkg_dir = self.prepare_dist("custom_loader") + output = self.run_with_dist_cwd(self.pkg_dir) + self.assert_re_match(EXPECTED_OUTPUT_RE, output) + + def _test_works_with_2to3(self): + pass + + @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + def test_checks_requires(self): + from distutils2.tests.with_support import examine_warnings + dist = Distribution() + dist.tests_require = ['ohno_ohno-impossible_1234-name_stop-that!'] + cmd = test(dist) + def examinator(ws): + cmd.ensure_finalized() + self.assertEqual(1, len(ws)) + warning = ws[0] + self.assertIs(warning.category, RuntimeWarning) + + examine_warnings(examinator) + +def test_suite(): + return unittest.makeSuite(TestTest) diff --git a/src/distutils2/tests/test_upload_docs.py b/src/distutils2/tests/test_upload_docs.py --- a/src/distutils2/tests/test_upload_docs.py +++ b/src/distutils2/tests/test_upload_docs.py @@ -116,12 +116,13 @@ self.assertTrue(handler.headers.dict['content-type'] .startswith('multipart/form-data;')) - action, name, content =\ - request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:4] + action, name, version, content =\ + request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:5] # check that we picked the right chunks self.assertIn('name=":action"', action) self.assertIn('name="name"', name) + self.assertIn('name="version"', version) self.assertIn('name="content"', content) # check their contents diff --git a/src/distutils2/tests/with_support.py b/src/distutils2/tests/with_support.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/with_support.py @@ -0,0 +1,7 @@ +def examine_warnings(examinator): + """Given an examinator function calls it as if the code was under with + catch_warnings block. Useful for testing older Python versions""" + import warnings + warnings.simplefilter('default') + with warnings.catch_warnings(record=True) as ws: + examinator(ws) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: merged from upstream Message-ID: tarek.ziade pushed 10da588ede3e to distutils2: http://hg.python.org/distutils2/rev/10da588ede3e changeset: 608:10da588ede3e parent: 479:7debb069fc21 parent: 607:59d10bf5a35c user: Konrad Delong date: Mon Aug 09 13:09:23 2010 +0200 summary: merged from upstream files: docs/source/commands.rst, src/distutils2/command/upload_docs.py, src/distutils2/dist.py diff --git a/docs/source/commands.rst b/docs/source/commands.rst --- a/docs/source/commands.rst +++ b/docs/source/commands.rst @@ -6,6 +6,55 @@ You might recognize some of them from other projects, like Distribute or Setuptools. +``test`` - Build package and run a unittest suite +================================================= + +When doing test-driven development, or running automated builds that need +testing before they are deployed for downloading or use, it's often useful +to be able to run a project's unit tests without actually deploying the project +anywhere, even using the ``develop`` command. The ``test`` command runs a +project's unit tests without actually deploying it, by temporarily putting the +project's source on ``sys.path``, after first running ``build_ext -i`` and +``egg_info`` to ensure that any C extensions and project metadata are +up-to-date. + +To use this command, your project's tests must be wrapped in a ``unittest`` +test suite by either a function, a ``TestCase`` class or method, or a module +or package containing ``TestCase`` classes. If the named suite is a module, +and the module has an ``additional_tests()`` function, it is called and the +result (which must be a ``unittest.TestSuite``) is added to the tests to be +run. If the named suite is a package, any submodules and subpackages are +recursively added to the overall test suite. (Note: if your project specifies +a ``test_loader``, the rules for processing the chosen ``test_suite`` may +differ; see the `test_loader`_ documentation for more details.) + +Note that many test systems including ``doctest`` support wrapping their +non-``unittest`` tests in ``TestSuite`` objects. So, if you are using a test +package that does not support this, we suggest you encourage its developers to +implement test suite support, as this is a convenient and standard way to +aggregate a collection of tests to be run under a common test harness. + +By default, tests will be run in the "verbose" mode of the ``unittest`` +package's text test runner, but you can get the "quiet" mode (just dots) if +you supply the ``-q`` or ``--quiet`` option, either as a global option to +the setup script (e.g. ``setup.py -q test``) or as an option for the ``test`` +command itself (e.g. ``setup.py test -q``). There is one other option +available: + +``--test-suite=NAME, -s NAME`` + Specify the test suite (or module, class, or method) to be run + (e.g. ``some_module.test_suite``). The default for this option can be + set by giving a ``test_suite`` argument to the ``setup()`` function, e.g.:: + + setup( + # ... + test_suite = "my_package.tests.test_all" + ) + + If you did not set a ``test_suite`` in your ``setup()`` call, and do not + provide a ``--test-suite`` option, an error will occur. + + ``upload`` - Upload source and/or binary distributions to PyPI ============================================================== @@ -84,7 +133,7 @@ python setup.py upload_docs --upload-dir=docs/build/html -As with any other command, you can define useful +As with any other ``setuptools`` based command, you can define useful defaults in the ``setup.cfg`` of your Python project, e.g.: .. code-block:: ini diff --git a/src/distutils2/_backport/__init__.py b/src/distutils2/_backport/__init__.py --- a/src/distutils2/_backport/__init__.py +++ b/src/distutils2/_backport/__init__.py @@ -1,2 +1,8 @@ """Things that will land in the Python 3.3 std lib but which we must drag along with us for now to support 2.x.""" + +def any(seq): + for elem in seq: + if elem: + return elem + return False diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py new file mode 100644 --- /dev/null +++ b/src/distutils2/command/test.py @@ -0,0 +1,65 @@ +import os, sys +from distutils2.core import Command +from distutils2._backport.pkgutil import get_distribution +import unittest +import warnings + +def get_loader_instance(dotted_path): + if dotted_path is None: + return None + module_name, rest = dotted_path.split('.')[0], dotted_path.split('.')[1:] + while True: + try: + ret = __import__(module_name) + break + except ImportError: + if rest == []: + return None + module_name += ('.' + rest[0]) + rest = rest[1:] + while rest: + try: + ret = getattr(ret, rest.pop(0)) + except AttributeError: + return None + return ret() + +class test(Command): + + description = "" # TODO + user_options = [ + ('test-suite=', 's', + "Test suite to run (e.g. 'some_module.test_suite')"), + ('test-loader=', None, + "Test loader to be used to load the test suite."), + ] + + def initialize_options(self): + self.test_suite = None + self.test_loader = None + + def finalize_options(self): + self.build_lib = self.get_finalized_command("build").build_lib + if self.distribution.tests_require: + for requirement in self.distribution.tests_require: + if get_distribution(requirement) is None: + warnings.warn("The test dependency %s is not installed which may couse the tests to fail.", + RuntimeWarning) + + def run(self): + prev_cwd = os.getcwd() + try: + if self.distribution.has_ext_modules(): + build = self.get_reinitialized_command('build') + build.inplace = 1 # TODO - remove make sure it's needed + self.run_command('build') + os.chdir(self.build_lib) + args = {"module": self.test_suite, + "argv": sys.argv[:1], + "testLoader": get_loader_instance(self.test_loader) + } + if args['testLoader'] is None: + del args['testLoader'] + unittest.main(**args) + finally: + os.chdir(prev_cwd) diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -94,9 +94,10 @@ def run(self): name = self.distribution.metadata['Name'] + version = self.distribution.metadata['Version'] zip_file = zip_dir(self.upload_dir) - fields = {':action': 'doc_upload', 'name': name}.items() + fields = [(':action', 'doc_upload'), ('name', name), ('version', version)] files = [('content', name, zip_file.getvalue())] content_type, body = encode_multipart(fields, files) diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -110,6 +110,8 @@ "print the list of packages/modules provided"), ('requires', None, "print the list of packages/modules required"), + ('tests-require', None, + "print the list of packages/modules required to run the test suite"), ('obsoletes', None, "print the list of packages/modules made obsolete"), ('use-2to3', None, diff --git a/src/distutils2/tests/dists/custom_loader/myowntestmodule.py b/src/distutils2/tests/dists/custom_loader/myowntestmodule.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/custom_loader/myowntestmodule.py @@ -0,0 +1,9 @@ +import unittest +class Loader(object): + def loadTestsFromModule(self, names, module=None): + class SomeTest(unittest.TestCase): + def test_blah(self): + self.fail("horribly") + return unittest.makeSuite(SomeTest) + def __repr__(self): + return 'YO' diff --git a/src/distutils2/tests/dists/custom_loader/setup.cfg b/src/distutils2/tests/dists/custom_loader/setup.cfg new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/custom_loader/setup.cfg @@ -0,0 +1,2 @@ +[test] +test-loader = myowntestmodule.Loader diff --git a/src/distutils2/tests/dists/custom_loader/setup.py b/src/distutils2/tests/dists/custom_loader/setup.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/custom_loader/setup.py @@ -0,0 +1,5 @@ +from distutils2.core import setup +setup(name='somedist', + version='0.1', + py_modules=['myowntestmodule', 'somemod'], +) diff --git a/src/distutils2/tests/dists/extensions_test/myowntestmodule.py b/src/distutils2/tests/dists/extensions_test/myowntestmodule.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/myowntestmodule.py @@ -0,0 +1,4 @@ +import unittest +class SomeTest(unittest.TestCase): + def test_blah(self): + self.fail("horribly") diff --git a/src/distutils2/tests/dists/extensions_test/setup.cfg b/src/distutils2/tests/dists/extensions_test/setup.cfg new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/setup.cfg @@ -0,0 +1,2 @@ +[test] +test-suite = myowntestmodule diff --git a/src/distutils2/tests/dists/extensions_test/setup.py b/src/distutils2/tests/dists/extensions_test/setup.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/setup.py @@ -0,0 +1,6 @@ +from distutils2.core import setup, Extension +setup(name='somedist', + version='0.1', + py_modules=['myowntestmodule', 'somemod'], + ext_modules=[Extension('spam', ['spammodule.c'])], +) diff --git a/src/distutils2/tests/dists/extensions_test/spammodule.c b/src/distutils2/tests/dists/extensions_test/spammodule.c new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/extensions_test/spammodule.c @@ -0,0 +1,11 @@ +#include + +static PyMethodDef SpamMethods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyMODINIT_FUNC +initspam(void) +{ + (void) Py_InitModule("spam", SpamMethods); +} diff --git a/src/distutils2/tests/dists/simple_test/myowntestmodule.py b/src/distutils2/tests/dists/simple_test/myowntestmodule.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/simple_test/myowntestmodule.py @@ -0,0 +1,4 @@ +import unittest +class SomeTest(unittest.TestCase): + def test_blah(self): + self.fail("horribly") diff --git a/src/distutils2/tests/dists/simple_test/setup.cfg b/src/distutils2/tests/dists/simple_test/setup.cfg new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/simple_test/setup.cfg @@ -0,0 +1,2 @@ +[test] +test-suite = myowntestmodule diff --git a/src/distutils2/tests/dists/simple_test/setup.py b/src/distutils2/tests/dists/simple_test/setup.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/dists/simple_test/setup.py @@ -0,0 +1,5 @@ +from distutils2.core import setup +setup(name='somedist', + version='0.1', + py_modules=['myowntestmodule', 'somemod'], +) diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_test.py @@ -0,0 +1,102 @@ +import os, sys, shutil, re +from copy import copy +from os.path import join +from StringIO import StringIO +from distutils2.tests.support import unittest, TempdirManager +from distutils2.command.test import test +from distutils2.dist import Distribution +import subprocess + +try: + any +except NameError: + from distutils2._backport import any + +EXPECTED_OUTPUT_RE = r'''FAIL: test_blah \(myowntestmodule.SomeTest\) +---------------------------------------------------------------------- +Traceback \(most recent call last\): + File ".+/myowntestmodule.py", line \d+, in test_blah + self.fail\("horribly"\) +AssertionError: horribly +''' + +here = os.path.dirname(os.path.abspath(__file__)) + + +class TestTest(TempdirManager, unittest.TestCase): + + def setUp(self): + super(TestTest, self).setUp() + + distutils2path = os.path.dirname(os.path.dirname(here)) + self.old_pythonpath = os.environ.get('PYTHONPATH', '') + os.environ['PYTHONPATH'] = distutils2path + os.pathsep + self.old_pythonpath + + def tearDown(self): + os.environ['PYTHONPATH'] = self.old_pythonpath + super(TestTest, self).tearDown() + + def assert_re_match(self, pattern, string): + def quote(s): + lines = ['## ' + line for line in s.split('\n')] + sep = ["#" * 60] + return [''] + sep + lines + sep + msg = quote(pattern) + ["didn't match"] + quote(string) + msg = "\n".join(msg) + if not re.search(pattern, string): + self.fail(msg) + + def run_with_dist_cwd(self, pkg_dir): + cwd = os.getcwd() + command = [sys.executable, "setup.py", "test"] + try: + os.chdir(pkg_dir) + test_proc = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + _, errors = test_proc.communicate() + return errors + finally: + os.chdir(cwd) + + def prepare_dist(self, dist_name): + pkg_dir = join(os.path.dirname(__file__), "dists", dist_name) + temp_pkg_dir = join(self.mkdtemp(), dist_name) + shutil.copytree(pkg_dir, temp_pkg_dir) + return temp_pkg_dir + + def test_runs_simple_tests(self): + self.pkg_dir = self.prepare_dist('simple_test') + output = self.run_with_dist_cwd(self.pkg_dir) + self.assert_re_match(EXPECTED_OUTPUT_RE, output) + self.assertFalse(os.path.exists(join(self.pkg_dir, 'build'))) + + def test_builds_extensions(self): + self.pkg_dir = self.prepare_dist("extensions_test") + output = self.run_with_dist_cwd(self.pkg_dir) + self.assert_re_match(EXPECTED_OUTPUT_RE, output) + self.assertTrue(os.path.exists(join(self.pkg_dir, 'build'))) + self.assertTrue(any(x.startswith('lib') for x in os.listdir(join(self.pkg_dir, 'build')))) + + def test_custom_test_loader(self): + self.pkg_dir = self.prepare_dist("custom_loader") + output = self.run_with_dist_cwd(self.pkg_dir) + self.assert_re_match(EXPECTED_OUTPUT_RE, output) + + def _test_works_with_2to3(self): + pass + + @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + def test_checks_requires(self): + from distutils2.tests.with_support import examine_warnings + dist = Distribution() + dist.tests_require = ['ohno_ohno-impossible_1234-name_stop-that!'] + cmd = test(dist) + def examinator(ws): + cmd.ensure_finalized() + self.assertEqual(1, len(ws)) + warning = ws[0] + self.assertIs(warning.category, RuntimeWarning) + + examine_warnings(examinator) + +def test_suite(): + return unittest.makeSuite(TestTest) diff --git a/src/distutils2/tests/test_upload_docs.py b/src/distutils2/tests/test_upload_docs.py --- a/src/distutils2/tests/test_upload_docs.py +++ b/src/distutils2/tests/test_upload_docs.py @@ -116,12 +116,13 @@ self.assertTrue(handler.headers.dict['content-type'] .startswith('multipart/form-data;')) - action, name, content =\ - request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:4] + action, name, version, content =\ + request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:5] # check that we picked the right chunks self.assertIn('name=":action"', action) self.assertIn('name="name"', name) + self.assertIn('name="version"', version) self.assertIn('name="content"', content) # check their contents diff --git a/src/distutils2/tests/with_support.py b/src/distutils2/tests/with_support.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/with_support.py @@ -0,0 +1,7 @@ +def examine_warnings(examinator): + """Given an examinator function calls it as if the code was under with + catch_warnings block. Useful for testing older Python versions""" + import warnings + warnings.simplefilter('default') + with warnings.catch_warnings(record=True) as ws: + examinator(ws) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: merge Message-ID: tarek.ziade pushed bc408018ca27 to distutils2: http://hg.python.org/distutils2/rev/bc408018ca27 changeset: 609:bc408018ca27 parent: 608:10da588ede3e parent: 521:e51ba85f26cd user: Konrad Delong date: Mon Aug 09 18:22:43 2010 +0200 summary: merge files: src/distutils2/dist.py diff --git a/docs/source/projects-index.dist.rst b/docs/source/projects-index.dist.rst --- a/docs/source/projects-index.dist.rst +++ b/docs/source/projects-index.dist.rst @@ -84,26 +84,27 @@ {'url': 'http://example.org/foobar-1.0.tar.gz', 'hashname': None, 'hashval': None, 'is_external': True} -Attributes Lazy loading ------------------------ +Getting attributes from the dist objects +----------------------------------------- To abstract a maximum the way of querying informations to the indexes, -attributes and releases informations can be retrieved "on demand", in a "lazy" -way. +attributes and releases informations can be retrieved directly from the objects +returned by the indexes. For instance, if you have a release instance that does not contain the metadata -attribute, it can be build directly when accedded:: +attribute, it can be fetched by using the "fetch_metadata" method:: >>> r = Release("FooBar", "1.1") - >>> print r._metadata + >>> print r.metadata None # metadata field is actually set to "None" - >>> r.metadata + >>> r.fetch_metadata() -Like this, it's possible to retrieve project's releases, releases metadata and -releases distributions informations. +Like this, it's possible to retrieve project's releases (`fetch_releases`), +releases metadata (`fetch_metadata` and releases distributions +(`fetch_distributions` informations). Internally, this is possible because while retrieving for the first time informations about projects, releases or distributions, a reference to the -client used is stored in the objects. Then, while trying to access undefined -fields, it will be used if necessary. +client used is stored in the objects (can be accessed using the object +`_index` attribute. diff --git a/docs/source/projects-index.xmlrpc.rst b/docs/source/projects-index.xmlrpc.rst --- a/docs/source/projects-index.xmlrpc.rst +++ b/docs/source/projects-index.xmlrpc.rst @@ -126,24 +126,3 @@ As you see, this does not return a list of distributions, but a release, because a release can be used like a list of distributions. - -Lazy load information from project, releases and distributions. ----------------------------------------------------------------- - -.. note:: The lazy loading feature is not currently available ! - -As :mod:`distutils2.index.dist` classes support "lazy" loading of -informations, you can use it while retrieving informations from XML-RPC. - -For instance, it's possible to get all the releases for a project, and to access -directly the metadata of each release, without making -:class:`distutils2.index.xmlrpc.Client` directly (they will be made, but they're -invisible to the you):: - - >>> client = xmlrpc.Client() - >>> releases = client.get_releases("FooBar") - >>> releases.get_release("1.1").metadata - - -Refer to the :mod:`distutils2.index.dist` documentation for more information -about attributes lazy loading. diff --git a/src/DEVNOTES.txt b/src/DEVNOTES.txt --- a/src/DEVNOTES.txt +++ b/src/DEVNOTES.txt @@ -17,6 +17,5 @@ - DistributionMetadata > Metadata or ReleaseMetadata - pkgutil > pkgutil.__init__ + new pkgutil.database (or better name) - - pypi > index - RationalizedVersion > Version - suggest_rationalized_version > suggest diff --git a/src/distutils2/__init__.py b/src/distutils2/__init__.py --- a/src/distutils2/__init__.py +++ b/src/distutils2/__init__.py @@ -20,7 +20,7 @@ __version__ = "1.0a2" -# when set to True, converts doctests by default too +# when set to True, converts doctests by default too run_2to3_on_doctests = True # Standard package names for fixer packages lib2to3_fixer_packages = ['lib2to3.fixes'] diff --git a/src/distutils2/command/__init__.py b/src/distutils2/command/__init__.py --- a/src/distutils2/command/__init__.py +++ b/src/distutils2/command/__init__.py @@ -5,7 +5,8 @@ __revision__ = "$Id: __init__.py 71473 2009-04-11 14:55:07Z tarek.ziade $" -__all__ = ['build', +__all__ = ['check', + 'build', 'build_py', 'build_ext', 'build_clib', @@ -16,17 +17,11 @@ 'install_headers', 'install_scripts', 'install_data', + 'install_distinfo', 'sdist', - 'register', 'bdist', 'bdist_dumb', 'bdist_wininst', + 'register', 'upload', - 'check', - # These two are reserved for future use: - #'bdist_sdux', - #'bdist_pkgtool', - # Note: - # bdist_packager is not included because it only provides - # an abstract base class ] diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -371,7 +371,12 @@ return modules def get_source_files(self): - return [module[-1] for module in self.find_all_modules()] + sources = [module[-1] for module in self.find_all_modules()] + sources += [ + os.path.join(src_dir, filename) + for package, src_dir, build_dir, filenames in self.data_files + for filename in filenames] + return sources def get_module_outfile(self, build_dir, package, module): outfile_path = [build_dir] + list(package) + [module + ".py"] @@ -393,8 +398,7 @@ outputs += [ os.path.join(build_dir, filename) for package, src_dir, build_dir, filenames in self.data_files - for filename in filenames - ] + for filename in filenames] return outputs diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -18,6 +18,7 @@ from distutils2.util import convert_path, change_root, get_platform from distutils2.errors import DistutilsOptionError + class install(Command): description = "install everything from build directory" @@ -31,7 +32,7 @@ ('home=', None, "(Unix only) home directory to install under"), - # Or, just set the base director(y|ies) + # Or just set the base director(y|ies) ('install-base=', None, "base installation directory (instead of --prefix or --home)"), ('install-platbase=', None, @@ -40,7 +41,7 @@ ('root=', None, "install everything relative to this alternate root directory"), - # Or, explicitly set the installation scheme + # Or explicitly set the installation scheme ('install-purelib=', None, "installation directory for pure Python module distributions"), ('install-platlib=', None, @@ -62,8 +63,8 @@ ('compile', 'c', "compile .py to .pyc [default]"), ('no-compile', None, "don't compile .py files"), ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + 'also compile with optimization: -O1 for "python -O", ' + '-O2 for "python -OO", and -O0 to disable [default: -O0]'), # Miscellaneous control options ('force', 'f', @@ -77,34 +78,45 @@ #('install-html=', None, "directory for HTML documentation"), #('install-info=', None, "directory for GNU info files"), + # XXX use a name that makes clear this is the old format ('record=', None, - "filename in which to record list of installed files"), + "filename in which to record a list of installed files " + "(not PEP 376-compliant)"), # .dist-info related arguments, read by install_dist_info - ('no-distinfo', None, 'do not create a .dist-info directory'), - ('installer=', None, 'the name of the installer'), - ('requested', None, 'generate a REQUESTED file'), - ('no-requested', None, 'do not generate a REQUESTED file'), - ('no-record', None, 'do not generate a RECORD file'), + ('no-distinfo', None, + "do not create a .dist-info directory"), + ('installer=', None, + "the name of the installer"), + ('requested', None, + "generate a REQUESTED file (i.e."), + ('no-requested', None, + "do not generate a REQUESTED file"), + ('no-record', None, + "do not generate a RECORD file"), ] - boolean_options = ['compile', 'force', 'skip-build', 'no-dist-info', - 'requested', 'no-dist-record',] + boolean_options = ['compile', 'force', 'skip-build', 'no-distinfo', + 'requested', 'no-record'] - user_options.append(('user', None, - "install in user site-package '%s'" % \ - get_path('purelib', '%s_user' % os.name))) - boolean_options.append('user') - negative_opt = {'no-compile' : 'compile', 'no-requested': 'requested'} + if sys.version >= '2.6': + user_options.append( + ('user', None, + "install in user site-package [%s]" % + get_path('purelib', '%s_user' % os.name))) + boolean_options.append('user') + + negative_opt = {'no-compile': 'compile', 'no-requested': 'requested'} def initialize_options(self): - """Initializes options.""" # High-level options: these select both an installation base # and scheme. self.prefix = None self.exec_prefix = None self.home = None + # This attribute is used all over the place, so it's best to + # define it even in < 2.6 self.user = 0 # These select only the installation base; it's up to the user to @@ -174,7 +186,6 @@ self.requested = None self.no_record = None - # -- Option finalizing methods ------------------------------------- # (This is rather more involved than for most commands, # because this is where the policy for installing third- @@ -182,7 +193,6 @@ # array of user input is decided. Yes, it's quite complex!) def finalize_options(self): - """Finalizes options.""" # This method (and its pliant slaves, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default # installation directories for modules, extension modules, and @@ -199,18 +209,19 @@ if ((self.prefix or self.exec_prefix or self.home) and (self.install_base or self.install_platbase)): - raise DistutilsOptionError, \ - ("must supply either prefix/exec-prefix/home or " + - "install-base/install-platbase -- not both") + raise DistutilsOptionError( + "must supply either prefix/exec-prefix/home or " + "install-base/install-platbase -- not both") if self.home and (self.prefix or self.exec_prefix): - raise DistutilsOptionError, \ - "must supply either home or prefix/exec-prefix -- not both" + raise DistutilsOptionError( + "must supply either home or prefix/exec-prefix -- not both") if self.user and (self.prefix or self.exec_prefix or self.home or - self.install_base or self.install_platbase): - raise DistutilsOptionError("can't combine user with with prefix/" - "exec_prefix/home or install_(plat)base") + self.install_base or self.install_platbase): + raise DistutilsOptionError( + "can't combine user with prefix/exec_prefix/home or " + "install_base/install_platbase") # Next, stuff that's wrong (or dubious) only on certain platforms. if os.name != "posix": @@ -245,18 +256,19 @@ 'srcdir') metadata = self.distribution.metadata - self.config_vars = {'dist_name': metadata['Name'], - 'dist_version': metadata['Version'], - 'dist_fullname': metadata.get_fullname(), - 'py_version': py_version, - 'py_version_short': py_version[0:3], - 'py_version_nodot': py_version[0] + py_version[2], - 'sys_prefix': prefix, - 'prefix': prefix, - 'sys_exec_prefix': exec_prefix, - 'exec_prefix': exec_prefix, - 'srcdir': srcdir, - } + self.config_vars = { + 'dist_name': metadata['Name'], + 'dist_version': metadata['Version'], + 'dist_fullname': metadata.get_fullname(), + 'py_version': py_version, + 'py_version_short': py_version[:3], + 'py_version_nodot': py_version[:3:2], + 'sys_prefix': prefix, + 'prefix': prefix, + 'sys_exec_prefix': exec_prefix, + 'exec_prefix': exec_prefix, + 'srcdir': srcdir, + } self.config_vars['userbase'] = self.install_userbase self.config_vars['usersite'] = self.install_usersite @@ -284,12 +296,11 @@ # module distribution is pure or not. Of course, if the user # already specified install_lib, use their selection. if self.install_lib is None: - if self.distribution.ext_modules: # has extensions: non-pure + if self.distribution.ext_modules: # has extensions: non-pure self.install_lib = self.install_platlib else: self.install_lib = self.install_purelib - # Convert directories from Unix /-separated syntax to the local # convention. self.convert_paths('lib', 'purelib', 'platlib', @@ -301,7 +312,7 @@ # non-packagized module distributions (hello, Numerical Python!) to # get their own directories. self.handle_extra_path() - self.install_libbase = self.install_lib # needed for .pth file + self.install_libbase = self.install_lib # needed for .pth file self.install_lib = os.path.join(self.install_lib, self.extra_dirs) # If a new root directory was supplied, make all the installation @@ -321,25 +332,8 @@ if self.no_distinfo is None: self.no_distinfo = False - def dump_dirs(self, msg): - """Dumps the list of user options.""" - from distutils2.fancy_getopt import longopt_xlate - log.debug(msg + ":") - for opt in self.user_options: - opt_name = opt[0] - if opt_name[-1] == "=": - opt_name = opt_name[0:-1] - if opt_name in self.negative_opt: - opt_name = self.negative_opt[opt_name] - opt_name = opt_name.translate(longopt_xlate) - val = not getattr(self, opt_name) - else: - opt_name = opt_name.translate(longopt_xlate) - val = getattr(self, opt_name) - log.debug(" %s: %s" % (opt_name, val)) - def finalize_unix(self): - """Finalizes options for posix platforms.""" + """Finalize options for posix platforms.""" if self.install_base is not None or self.install_platbase is not None: if ((self.install_lib is None and self.install_purelib is None and @@ -347,15 +341,15 @@ self.install_headers is None or self.install_scripts is None or self.install_data is None): - raise DistutilsOptionError, \ - ("install-base or install-platbase supplied, but " - "installation scheme is incomplete") + raise DistutilsOptionError( + "install-base or install-platbase supplied, but " + "installation scheme is incomplete") return if self.user: if self.install_userbase is None: raise DistutilsPlatformError( - "User base directory is not specified") + "user base directory is not specified") self.install_base = self.install_platbase = self.install_userbase self.select_scheme("posix_user") elif self.home is not None: @@ -364,8 +358,8 @@ else: if self.prefix is None: if self.exec_prefix is not None: - raise DistutilsOptionError, \ - "must not supply exec-prefix without prefix" + raise DistutilsOptionError( + "must not supply exec-prefix without prefix") self.prefix = os.path.normpath(sys.prefix) self.exec_prefix = os.path.normpath(sys.exec_prefix) @@ -379,11 +373,11 @@ self.select_scheme("posix_prefix") def finalize_other(self): - """Finalizes options for non-posix platforms""" + """Finalize options for non-posix platforms""" if self.user: if self.install_userbase is None: raise DistutilsPlatformError( - "User base directory is not specified") + "user base directory is not specified") self.install_base = self.install_platbase = self.install_userbase self.select_scheme(os.name + "_user") elif self.home is not None: @@ -397,11 +391,27 @@ try: self.select_scheme(os.name) except KeyError: - raise DistutilsPlatformError, \ - "I don't know how to install stuff on '%s'" % os.name + raise DistutilsPlatformError( + "no support for installation on '%s'" % os.name) + + def dump_dirs(self, msg): + """Dump the list of user options.""" + log.debug(msg + ":") + for opt in self.user_options: + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + if opt_name in self.negative_opt: + opt_name = self.negative_opt[opt_name] + opt_name = opt_name.replace('-', '_') + val = not getattr(self, opt_name) + else: + opt_name = opt_name.replace('-', '_') + val = getattr(self, opt_name) + log.debug(" %s: %s" % (opt_name, val)) def select_scheme(self, name): - """Sets the install directories by applying the install schemes.""" + """Set the install directories by applying the install schemes.""" # it's the caller's problem if they supply a bad name! scheme = get_paths(name, expand=False) for key, value in scheme.items(): @@ -424,15 +434,14 @@ setattr(self, attr, val) def expand_basedirs(self): - """Calls `os.path.expanduser` on install_base, install_platbase and - root.""" + """Call `os.path.expanduser` on install_{base,platbase} and root.""" self._expand_attrs(['install_base', 'install_platbase', 'root']) def expand_dirs(self): - """Calls `os.path.expanduser` on install dirs.""" + """Call `os.path.expanduser` on install dirs.""" self._expand_attrs(['install_purelib', 'install_platlib', 'install_lib', 'install_headers', - 'install_scripts', 'install_data',]) + 'install_scripts', 'install_data']) def convert_paths(self, *names): """Call `convert_path` over `names`.""" @@ -454,9 +463,9 @@ elif len(self.extra_path) == 2: path_file, extra_dirs = self.extra_path else: - raise DistutilsOptionError, \ - ("'extra_path' option must be a list, tuple, or " - "comma-separated string with 1 or 2 elements") + raise DistutilsOptionError( + "'extra_path' option must be a list, tuple, or " + "comma-separated string with 1 or 2 elements") # convert to local form in case Unix notation used (as it # should be in setup scripts) @@ -542,7 +551,6 @@ else: self.warn("path file '%s' not created" % filename) - # -- Reporting methods --------------------------------------------- def get_outputs(self): @@ -597,10 +605,10 @@ # 'sub_commands': a list of commands this command might have to run to # get its work done. See cmd.py for more info. - sub_commands = [('install_lib', has_lib), + sub_commands = [('install_lib', has_lib), ('install_headers', has_headers), ('install_scripts', has_scripts), - ('install_data', has_data), + ('install_data', has_data), # keep install_distinfo last, as it needs the record # with files to be completely generated ('install_distinfo', lambda self: not self.no_distinfo), diff --git a/src/distutils2/command/install_data.py b/src/distutils2/command/install_data.py --- a/src/distutils2/command/install_data.py +++ b/src/distutils2/command/install_data.py @@ -72,6 +72,21 @@ (out, _) = self.copy_file(data, dir) self.outfiles.append(out) + def get_source_files(self): + sources = [] + for item in self.data_files: + if isinstance(item, str): # plain file + item = convert_path(item) + if os.path.isfile(item): + sources.append(item) + else: # a (dirname, filenames) tuple + dirname, filenames = item + for f in filenames: + f = convert_path(f) + if os.path.isfile(f): + sources.append(f) + return sources + def get_inputs(self): return self.data_files or [] diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py --- a/src/distutils2/command/install_distinfo.py +++ b/src/distutils2/command/install_distinfo.py @@ -31,18 +31,18 @@ user_options = [ ('distinfo-dir=', None, - 'directory where the the .dist-info directory will ' - 'be installed'), - ('installer=', None, 'the name of the installer'), - ('requested', None, 'generate a REQUESTED file'), - ('no-requested', None, 'do not generate a REQUESTED file'), - ('no-record', None, 'do not generate a RECORD file'), + "directory where the the .dist-info directory will be installed"), + ('installer=', None, + "the name of the installer"), + ('requested', None, + "generate a REQUESTED file"), + ('no-requested', None, + "do not generate a REQUESTED file"), + ('no-record', None, + "do not generate a RECORD file"), ] - boolean_options = [ - 'requested', - 'no-record', - ] + boolean_options = ['requested', 'no-record'] negative_opt = {'no-requested': 'requested'} @@ -54,14 +54,14 @@ def finalize_options(self): self.set_undefined_options('install', - ('installer', 'installer'), - ('requested', 'requested'), - ('no_record', 'no_record')) + 'installer', 'requested', 'no_record') self.set_undefined_options('install_lib', ('install_dir', 'distinfo_dir')) if self.installer is None: + # FIXME distutils or distutils2? + # + document default in the option help text above and in install self.installer = 'distutils' if self.requested is None: self.requested = True @@ -144,10 +144,7 @@ return self.outputs -# The following routines are taken from setuptools' pkg_resources module and -# can be replaced by importing them from pkg_resources once it is included -# in the stdlib. - +# The following functions are taken from setuptools' pkg_resources module. def safe_name(name): """Convert an arbitrary string to a standard distribution name diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -170,14 +170,6 @@ # or zipfile, or whatever. self.make_distribution() - def check_metadata(self): - """Deprecated API.""" - warn("distutils.command.sdist.check_metadata is deprecated, \ - use the check command instead", PendingDeprecationWarning) - check = self.distribution.get_command_obj('check') - check.ensure_finalized() - check.run() - def get_file_list(self): """Figure out the list of files to include in the source distribution, and put it in 'self.filelist'. This might involve @@ -243,47 +235,9 @@ if files: self.filelist.extend(files) - # build_py is used to get: - # - python modules - # - files defined in package_data - build_py = self.get_finalized_command('build_py') - - # getting python files - if self.distribution.has_pure_modules(): - self.filelist.extend(build_py.get_source_files()) - - # getting package_data files - # (computed in build_py.data_files by build_py.finalize_options) - for pkg, src_dir, build_dir, filenames in build_py.data_files: - for filename in filenames: - self.filelist.append(os.path.join(src_dir, filename)) - - # getting distribution.data_files - if self.distribution.has_data_files(): - for item in self.distribution.data_files: - if isinstance(item, str): # plain file - item = convert_path(item) - if os.path.isfile(item): - self.filelist.append(item) - else: # a (dirname, filenames) tuple - dirname, filenames = item - for f in filenames: - f = convert_path(f) - if os.path.isfile(f): - self.filelist.append(f) - - if self.distribution.has_ext_modules(): - build_ext = self.get_finalized_command('build_ext') - self.filelist.extend(build_ext.get_source_files()) - - if self.distribution.has_c_libraries(): - build_clib = self.get_finalized_command('build_clib') - self.filelist.extend(build_clib.get_source_files()) - - if self.distribution.has_scripts(): - build_scripts = self.get_finalized_command('build_scripts') - self.filelist.extend(build_scripts.get_source_files()) - + for cmd_name in self.distribution.get_command_names(): + cmd_obj = self.get_finalized_command(cmd_name) + self.filelist.extend(cmd_obj.get_source_files()) def prune_file_list(self): """Prune off branches that might slip into the file list as created diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -33,6 +33,7 @@ or: %(script)s cmd --help """ + def gen_usage(script_name): script = os.path.basename(script_name) return USAGE % {'script': script} @@ -59,6 +60,7 @@ 'extra_objects', 'extra_compile_args', 'extra_link_args', 'swig_opts', 'export_symbols', 'depends', 'language') + def setup(**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py --- a/src/distutils2/depgraph.py +++ b/src/distutils2/depgraph.py @@ -1,5 +1,5 @@ -"""Analyse the relationships between the distributions in the system and generate -a dependency graph. +"""Analyse the relationships between the distributions in the system +and generate a dependency graph. """ from distutils2.errors import DistutilsError @@ -135,8 +135,7 @@ requires = dist.metadata['Requires-Dist'] + dist.metadata['Requires'] for req in requires: predicate = VersionPredicate(req) - comps = req.strip().rsplit(" ", 1) - name = comps[0] + name = predicate.name if not name in provided: graph.add_missing(dist, req) diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -6,12 +6,10 @@ __revision__ = "$Id: dist.py 77717 2010-01-24 00:33:32Z tarek.ziade $" -import sys, os, re - -try: - import warnings -except ImportError: - warnings = None +import sys +import os +import re +import warnings from ConfigParser import RawConfigParser @@ -26,7 +24,8 @@ # the same as a Python NAME -- I don't allow leading underscores. The fact # that they're very similar is no coincidence; the default naming scheme is # to look for a Python module named after the command. -command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') +command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') + class Distribution(object): """The core of the Distutils. Most of the work hiding behind 'setup' @@ -42,7 +41,6 @@ See the code for 'setup()', in core.py, for details. """ - # 'global_options' describes the command-line options that may be # supplied to the setup script prior to any actual commands. # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of @@ -125,10 +123,8 @@ # negative options are options that exclude other options negative_opt = {'quiet': 'verbose'} - # -- Creation/initialization methods ------------------------------- - - def __init__ (self, attrs=None): + def __init__(self, attrs=None): """Construct a new Distribution instance: initialize all the attributes of a Distribution, and then use 'attrs' (a dictionary mapping attribute names to values) to assign some of those @@ -193,18 +189,18 @@ # These options are really the business of various commands, rather # than of the Distribution itself. We provide aliases for them in # Distribution as a convenience to the developer. - self.packages = None + self.packages = [] self.package_data = {} self.package_dir = None - self.py_modules = None - self.libraries = None - self.headers = None - self.ext_modules = None + self.py_modules = [] + self.libraries = [] + self.headers = [] + self.ext_modules = [] self.ext_package = None - self.include_dirs = None + self.include_dirs = [] self.extra_path = None - self.scripts = None - self.data_files = None + self.scripts = [] + self.data_files = [] self.password = '' self.use_2to3 = False self.convert_2to3_doctests = [] @@ -253,10 +249,7 @@ setattr(self, key, val) else: msg = "Unknown distribution option: %r" % key - if warnings is not None: - warnings.warn(msg) - else: - sys.stderr.write(msg + "\n") + warnings.warn(msg) # no-user-cfg is handled before other command line args # because other args override the config files, and this @@ -282,10 +275,10 @@ and return the new dictionary; otherwise, return the existing option dictionary. """ - dict = self.command_options.get(command) - if dict is None: - dict = self.command_options[command] = {} - return dict + d = self.command_options.get(command) + if d is None: + d = self.command_options[command] = {} + return d def get_fullname(self): return self.metadata.get_fullname() @@ -381,7 +374,7 @@ for opt in options: if opt != '__name__': - val = parser.get(section,opt) + val = parser.get(section, opt) opt = opt.replace('-', '_') # ... although practicality beats purity :( @@ -410,12 +403,12 @@ try: if alias: setattr(self, alias, not strtobool(val)) - elif opt in ('verbose', 'dry_run'): # ugh! + elif opt in ('verbose', 'dry_run'): # ugh! setattr(self, opt, strtobool(val)) else: setattr(self, opt, val) except ValueError, msg: - raise DistutilsOptionError, msg + raise DistutilsOptionError(msg) # -- Command-line parsing methods ---------------------------------- @@ -481,7 +474,7 @@ # Oops, no commands found -- an end-user error if not self.commands: - raise DistutilsArgError, "no commands supplied" + raise DistutilsArgError("no commands supplied") # All is well: return true return 1 @@ -512,7 +505,7 @@ # Pull the current command from the head of the command line command = args[0] if not command_re.match(command): - raise SystemExit, "invalid command name '%s'" % command + raise SystemExit("invalid command name '%s'" % command) self.commands.append(command) # Dig up the command class that implements this command, so we @@ -521,22 +514,21 @@ try: cmd_class = self.get_command_class(command) except DistutilsModuleError, msg: - raise DistutilsArgError, msg + raise DistutilsArgError(msg) # Require that the command class be derived from Command -- want # to be sure that the basic "command" interface is implemented. if not issubclass(cmd_class, Command): - raise DistutilsClassError, \ - "command class %s must subclass Command" % cmd_class + raise DistutilsClassError( + "command class %s must subclass Command" % cmd_class) # Also make sure that the command object provides a list of its # known options. if not (hasattr(cmd_class, 'user_options') and isinstance(cmd_class.user_options, list)): - raise DistutilsClassError, \ - ("command class %s must provide " + - "'user_options' attribute (a list of tuples)") % \ - cmd_class + raise DistutilsClassError( + ("command class %s must provide " + "'user_options' attribute (a list of tuples)") % cmd_class) # If the command class has a list of negative alias options, # merge it in with the global negative aliases. @@ -553,7 +545,6 @@ else: help_options = [] - # All commands support the global options too, just by adding # in 'global_options'. parser.set_option_table(self.global_options + @@ -567,10 +558,10 @@ if (hasattr(cmd_class, 'help_options') and isinstance(cmd_class.help_options, list)): - help_option_found=0 + help_option_found = 0 for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): - help_option_found=1 + help_option_found = 1 if hasattr(func, '__call__'): func() else: @@ -708,25 +699,26 @@ print(" %-*s %s" % (max_length, cmd, description)) + def _get_command_groups(self): + """Helper function to retrieve all the command class names divided + into standard commands (listed in distutils2.command.__all__) + and extra commands (given in self.cmdclass and not standard + commands). + """ + from distutils2.command import __all__ as std_commands + extra_commands = [cmd for cmd in self.cmdclass + if cmd not in std_commands] + return std_commands, extra_commands + def print_commands(self): """Print out a help message listing all available commands with a - description of each. The list is divided into "standard commands" - (listed in distutils2.command.__all__) and "extra commands" - (mentioned in self.cmdclass, but not a standard command). The + description of each. The list is divided into standard commands + (listed in distutils2.command.__all__) and extra commands + (given in self.cmdclass and not standard commands). The descriptions come from the command class attribute 'description'. """ - import distutils2.command - std_commands = distutils2.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append(cmd) - + std_commands, extra_commands = self._get_command_groups() max_length = 0 for cmd in (std_commands + extra_commands): if len(cmd) > max_length: @@ -743,30 +735,17 @@ def get_command_list(self): """Get a list of (command, description) tuples. - The list is divided into "standard commands" (listed in - distutils2.command.__all__) and "extra commands" (mentioned in - self.cmdclass, but not a standard command). The descriptions come + + The list is divided into standard commands (listed in + distutils2.command.__all__) and extra commands (given in + self.cmdclass and not standard commands). The descriptions come from the command class attribute 'description'. """ # Currently this is only used on Mac OS, for the Mac-only GUI # Distutils interface (by Jack Jansen) - import distutils2.command - std_commands = distutils2.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append(cmd) - rv = [] - for cmd in (std_commands + extra_commands): - cls = self.cmdclass.get(cmd) - if not cls: - cls = self.get_command_class(cmd) + for cls in self.get_command_classes(): try: description = cls.description except AttributeError: @@ -788,6 +767,23 @@ self.command_packages = pkgs return pkgs + def get_command_names(self): + """Return a list of all command names.""" + return [getattr(cls, 'command_name', cls.__name__) + for cls in self.get_command_classes()] + + def get_command_classes(self): + """Return a list of all command classes.""" + std_commands, extra_commands = self._get_command_groups() + classes = [] + for cmd in (std_commands + extra_commands): + try: + cls = self.cmdclass[cmd] + except KeyError: + cls = self.get_command_class(cmd) + classes.append(cls) + return classes + def get_command_class(self, command): """Return the class that implements the Distutils command named by 'command'. First we check the 'cmdclass' dictionary; if the @@ -809,7 +805,7 @@ class_name = command try: - __import__ (module_name) + __import__(module_name) module = sys.modules[module_name] except ImportError: continue @@ -817,16 +813,15 @@ try: cls = getattr(module, class_name) except AttributeError: - raise DistutilsModuleError, \ - "invalid command '%s' (no class '%s' in module '%s')" \ - % (command, class_name, module_name) + raise DistutilsModuleError( + "invalid command '%s' (no class '%s' in module '%s')" % + (command, class_name, module_name)) self.cmdclass[command] = cls return cls raise DistutilsModuleError("invalid command '%s'" % command) - def get_command_obj(self, command, create=1): """Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command @@ -889,11 +884,11 @@ elif hasattr(command_obj, option): setattr(command_obj, option, value) else: - raise DistutilsOptionError, \ - ("error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) + raise DistutilsOptionError( + "error in %s: command '%s' has no such option '%s'" % + (source, command_name, option)) except ValueError, msg: - raise DistutilsOptionError, msg + raise DistutilsOptionError(msg) def get_reinitialized_command(self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first @@ -969,7 +964,6 @@ self.run_command_hooks(cmd_obj, 'post_hook') self.have_run[command] = 1 - def run_command_hooks(self, cmd_obj, hook_kind): hooks = getattr(cmd_obj, hook_kind) if hooks is None: @@ -979,7 +973,6 @@ hook_func(cmd_obj) # -- Distribution query methods ------------------------------------ - def has_pure_modules(self): return len(self.packages or self.py_modules or []) > 0 @@ -1006,13 +999,8 @@ not self.has_ext_modules() and not self.has_c_libraries()) - # -- Metadata query methods ---------------------------------------- - # If you're looking for 'get_name()', 'get_version()', and so forth, - # they are defined in a sneaky way: the constructor binds self.get_XXX - # to self.metadata.get_XXX. The actual code is in the - # DistributionMetadata class, below. - +# XXX keep for compat or remove? def fix_help_options(options): """Convert a 4-tuple 'help_options' list as found in various command diff --git a/src/distutils2/errors.py b/src/distutils2/errors.py --- a/src/distutils2/errors.py +++ b/src/distutils2/errors.py @@ -10,31 +10,38 @@ __revision__ = "$Id: errors.py 75901 2009-10-28 06:45:18Z tarek.ziade $" + class DistutilsError(Exception): """The root of all Distutils evil.""" + class DistutilsModuleError(DistutilsError): """Unable to load an expected module, or to find an expected class within some module (in particular, command modules and classes).""" + class DistutilsClassError(DistutilsError): """Some command class (or possibly distribution class, if anyone feels a need to subclass Distribution) is found not to be holding up its end of the bargain, ie. implementing some part of the "command "interface.""" + class DistutilsGetoptError(DistutilsError): """The option table provided to 'fancy_getopt()' is bogus.""" + class DistutilsArgError(DistutilsError): """Raised by fancy_getopt in response to getopt.error -- ie. an error in the command line usage.""" + class DistutilsFileError(DistutilsError): """Any problems in the filesystem: expected file not found, etc. Typically this is for problems that we detect before IOError or OSError could be raised.""" + class DistutilsOptionError(DistutilsError): """Syntactic/semantic errors in command options, such as use of mutually conflicting options, or inconsistent options, @@ -43,60 +50,76 @@ files, or what-have-you -- but if we *know* something originated in the setup script, we'll raise DistutilsSetupError instead.""" + class DistutilsSetupError(DistutilsError): """For errors that can be definitely blamed on the setup script, such as invalid keyword arguments to 'setup()'.""" + class DistutilsPlatformError(DistutilsError): """We don't know how to do something on the current platform (but we do know how to do it on some platform) -- eg. trying to compile C files on a platform not supported by a CCompiler subclass.""" + class DistutilsExecError(DistutilsError): """Any problems executing an external program (such as the C compiler, when compiling C files).""" + class DistutilsInternalError(DistutilsError): """Internal inconsistencies or impossibilities (obviously, this should never be seen if the code is working!).""" + class DistutilsTemplateError(DistutilsError): """Syntax error in a file list template.""" + class DistutilsByteCompileError(DistutilsError): """Byte compile error.""" + # Exception classes used by the CCompiler implementation classes class CCompilerError(Exception): """Some compile/link operation failed.""" + class PreprocessError(CCompilerError): """Failure to preprocess one or more C/C++ files.""" + class CompileError(CCompilerError): """Failure to compile one or more C/C++ source files.""" + class LibError(CCompilerError): """Failure to create a static library from one or more C/C++ object files.""" + class LinkError(CCompilerError): """Failure to link one or more C/C++ object files into an executable or shared library file.""" + class UnknownFileError(CCompilerError): """Attempt to process an unknown file type.""" + class MetadataConflictError(DistutilsError): """Attempt to read or write metadata fields that are conflictual.""" + class MetadataUnrecognizedVersionError(DistutilsError): """Unknown metadata version number.""" + class IrrationalVersionError(Exception): """This is an irrational version.""" pass + class HugeMajorVersionNumError(IrrationalVersionError): """An irrational version because the major version number is huge (often because a year or date was used). @@ -105,4 +128,3 @@ This guard can be disabled by setting that option False. """ pass - diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py --- a/src/distutils2/extension.py +++ b/src/distutils2/extension.py @@ -17,6 +17,7 @@ # import that large-ish module (indirectly, through distutils.core) in # order to do anything. + class Extension(object): """Just a collection of attributes that describes an extension module and everything needed to build it (hopefully in a portable @@ -84,7 +85,7 @@ # When adding arguments to this constructor, be sure to update # setup_keywords in core.py. - def __init__ (self, name, sources, + def __init__(self, name, sources, include_dirs=None, define_macros=None, undef_macros=None, @@ -95,11 +96,11 @@ extra_compile_args=None, extra_link_args=None, export_symbols=None, - swig_opts = None, + swig_opts=None, depends=None, language=None, optional=None, - **kw # To catch unknown keywords + **kw # To catch unknown keywords ): if not isinstance(name, str): raise AssertionError("'name' must be a string") @@ -134,4 +135,3 @@ options = ', '.join(sorted(options)) msg = "Unknown Extension options: %s" % options warnings.warn(msg) - diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py --- a/src/distutils2/fancy_getopt.py +++ b/src/distutils2/fancy_getopt.py @@ -30,6 +30,7 @@ # (for use as attributes of some object). longopt_xlate = string.maketrans('-', '_') + class FancyGetopt(object): """Wrapper around the standard 'getopt()' module that provides some handy extra functionality: @@ -42,7 +43,7 @@ on the command line sets 'verbose' to false """ - def __init__ (self, option_table=None): + def __init__(self, option_table=None): # The option table is (currently) a list of tuples. The # tuples may have 3 or four values: @@ -180,7 +181,8 @@ self.long_opts.append(long) if long[-1] == '=': # option takes an argument? - if short: short = short + ':' + if short: + short = short + ':' long = long[0:-1] self.takes_arg[long] = 1 else: diff --git a/src/distutils2/index/base.py b/src/distutils2/index/base.py --- a/src/distutils2/index/base.py +++ b/src/distutils2/index/base.py @@ -1,4 +1,3 @@ -from distutils2.version import VersionPredicate from distutils2.index.dist import ReleasesList @@ -10,14 +9,6 @@ self._prefer_source = prefer_source self._index = self - def _get_version_predicate(self, requirements): - """Return a VersionPredicate object, from a string or an already - existing object. - """ - if isinstance(requirements, str): - requirements = VersionPredicate(requirements) - return requirements - def _get_prefer_final(self, prefer_final=None): """Return the prefer_final internal parameter or the specified one if provided""" diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -26,7 +26,8 @@ from distutils2.errors import IrrationalVersionError from distutils2.index.errors import (HashDoesNotMatch, UnsupportedHashName, CantParseArchiveName) -from distutils2.version import suggest_normalized_version, NormalizedVersion +from distutils2.version import (suggest_normalized_version, NormalizedVersion, + get_version_predicate) from distutils2.metadata import DistributionMetadata from distutils2.util import untar_file, unzip_file, splitext @@ -38,6 +39,7 @@ class IndexReference(object): + """Mixin used to store the index reference""" def set_index(self, index=None): self._index = index @@ -64,10 +66,10 @@ self._version = None self.version = version if metadata: - self._metadata = DistributionMetadata(mapping=metadata) + self.metadata = DistributionMetadata(mapping=metadata) else: - self._metadata = None - self._dists = {} + self.metadata = None + self.dists = {} self.hidden = hidden if 'dist_type' in kwargs: @@ -89,25 +91,23 @@ version = property(get_version, set_version) - @property - def metadata(self): + def fetch_metadata(self): """If the metadata is not set, use the indexes to get it""" - if not self._metadata: + if not self.metadata: self._index.get_metadata(self.name, '%s' % self.version) - return self._metadata + return self.metadata @property def is_final(self): """proxy to version.is_final""" return self.version.is_final - @property - def dists(self): - if self._dists is None: + def fetch_distributions(self): + if self.dists is None: self._index.get_distributions(self.name, '%s' % self.version) - if self._dists is None: - self._dists = {} - return self._dists + if self.dists is None: + self.dists = {} + return self.dists def add_distribution(self, dist_type='sdist', python_version=None, **params): """Add distribution informations to this release. @@ -122,12 +122,12 @@ if dist_type not in DIST_TYPES: raise ValueError(dist_type) if dist_type in self.dists: - self._dists[dist_type].add_url(**params) + self.dists[dist_type].add_url(**params) else: - self._dists[dist_type] = DistInfo(self, dist_type, + self.dists[dist_type] = DistInfo(self, dist_type, index=self._index, **params) if python_version: - self._dists[dist_type].python_version = python_version + self.dists[dist_type].python_version = python_version def get_distribution(self, dist_type=None, prefer_source=True): """Return a distribution. @@ -163,9 +163,9 @@ .download(path=temp_path) def set_metadata(self, metadata): - if not self._metadata: - self._metadata = DistributionMetadata() - self._metadata.update(metadata) + if not self.metadata: + self.metadata = DistributionMetadata() + self.metadata.update(metadata) def __getitem__(self, item): """distributions are available using release["sdist"]""" @@ -351,18 +351,12 @@ """ def __init__(self, name, releases=None, contains_hidden=False, index=None): self.set_index(index) - self._releases = [] + self.releases = [] self.name = name self.contains_hidden = contains_hidden if releases: self.add_releases(releases) - @property - def releases(self): - if not self._releases: - self.fetch_releases() - return self._releases - def fetch_releases(self): self._index.get_releases(self.name) return self.releases @@ -374,12 +368,13 @@ if predicate.match(release.version)], index=self._index) - def get_last(self, predicate, prefer_final=None): + def get_last(self, requirements, prefer_final=None): """Return the "last" release, that satisfy the given predicates. "last" is defined by the version number of the releases, you also could set prefer_final parameter to True or False to change the order results """ + predicate = get_version_predicate(requirements) releases = self.filter(predicate) releases.sort_releases(prefer_final, reverse=True) return releases[0] @@ -412,16 +407,16 @@ if not version in self.get_versions(): # append only if not already exists - self._releases.append(release) + self.releases.append(release) for dist in release.dists.values(): for url in dist.urls: self.add_release(version, dist.dist_type, **url) else: - matches = [r for r in self._releases if '%s' % r.version == version + matches = [r for r in self.releases if '%s' % r.version == version and r.name == self.name] if not matches: release = ReleaseInfo(self.name, version, index=self._index) - self._releases.append(release) + self.releases.append(release) else: release = matches[0] @@ -459,7 +454,7 @@ def get_versions(self): """Return a list of releases versions contained""" - return ["%s" % r.version for r in self._releases] + return ["%s" % r.version for r in self.releases] def __getitem__(self, key): return self.releases[key] diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -22,6 +22,7 @@ ReleaseNotFound, ProjectNotFound) from distutils2.index.mirrors import get_mirrors from distutils2.metadata import DistributionMetadata +from distutils2.version import get_version_predicate from distutils2 import __version__ as __distutils2_version__ __all__ = ['Crawler', 'DEFAULT_SIMPLE_INDEX_URL'] @@ -158,7 +159,7 @@ """Search for releases and return a ReleaseList object containing the results. """ - predicate = self._get_version_predicate(requirements) + predicate = get_version_predicate(requirements) if self._projects.has_key(predicate.name.lower()) and not force_update: return self._projects.get(predicate.name.lower()) prefer_final = self._get_prefer_final(prefer_final) @@ -173,7 +174,7 @@ def get_release(self, requirements, prefer_final=None): """Return only one release that fulfill the given requirements""" - predicate = self._get_version_predicate(requirements) + predicate = get_version_predicate(requirements) release = self.get_releases(predicate, prefer_final)\ .get_last(predicate) if not release: diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py --- a/src/distutils2/index/xmlrpc.py +++ b/src/distutils2/index/xmlrpc.py @@ -3,8 +3,10 @@ from distutils2.errors import IrrationalVersionError from distutils2.index.base import BaseClient -from distutils2.index.errors import ProjectNotFound, InvalidSearchField +from distutils2.index.errors import (ProjectNotFound, InvalidSearchField, + ReleaseNotFound) from distutils2.index.dist import ReleaseInfo +from distutils2.version import get_version_predicate __all__ = ['Client', 'DEFAULT_XMLRPC_INDEX_URL'] @@ -41,7 +43,7 @@ related informations. """ prefer_final = self._get_prefer_final(prefer_final) - predicate = self._get_version_predicate(requirements) + predicate = get_version_predicate(requirements) releases = self.get_releases(predicate.name) release = releases.get_last(predicate, prefer_final) self.get_metadata(release.name, "%s" % release.version) @@ -72,7 +74,7 @@ def get_versions(project_name, show_hidden): return self.proxy.package_releases(project_name, show_hidden) - predicate = self._get_version_predicate(requirements) + predicate = get_version_predicate(requirements) prefer_final = self._get_prefer_final(prefer_final) project_name = predicate.name if not force_update and (project_name.lower() in self._projects): @@ -96,6 +98,8 @@ index=self._index) for version in versions]) project = project.filter(predicate) + if len(project) == 0: + raise ReleaseNotFound("%s" % predicate) project.sort_releases(prefer_final) return project diff --git a/src/distutils2/install_with_deps.py b/src/distutils2/install_with_deps.py new file mode 100644 --- /dev/null +++ b/src/distutils2/install_with_deps.py @@ -0,0 +1,171 @@ +import logging +from distutils2.index import wrapper +from distutils2.index.errors import ProjectNotFound, ReleaseNotFound +from distutils2.depgraph import generate_graph +from distutils2._backport.pkgutil import get_distributions + + +"""Provides installations scripts. + +The goal of this script is to install a release from the indexes (eg. +PyPI), including the dependencies of the releases if needed. + +It uses the work made in pkgutil and by the index crawlers to browse the +installed distributions, and rely on the instalation commands to install. +""" + + +def get_deps(requirements, index): + """Return the dependencies of a project, as a depgraph object. + + Build a :class depgraph.DependencyGraph: for the given requirements + + If the project does not uses Metadata < 1.1, dependencies can't be handled + from here, so it returns an empty list of dependencies. + + :param requirements: is a string containing the version predicate to take + the project name and version specifier from. + :param index: the index to use for making searches. + """ + deps = [] + release = index.get_release(requirements) + requires = release.metadata['Requires-Dist'] + release.metadata['Requires'] + deps.append(release) # include the release we are computing deps. + for req in requires: + deps.extend(get_deps(req, index)) + return deps + + +def install(requirements, index=None, interactive=True, upgrade=True, + prefer_source=True, prefer_final=True): + """Given a list of distributions to install, a list of distributions to + remove, and a list of conflicts, proceed and do what's needed to be done. + + :param requirements: is a *string* containing the requirements for this + project (for instance "FooBar 1.1" or "BarBaz (<1.2) + :param index: If an index is specified, use this one, otherwise, use + :class index.ClientWrapper: to get project metadatas. + :param interactive: if set to True, will prompt the user for interactions + of needed. If false, use the default values. + :param upgrade: If a project exists in a newer version, does the script + need to install the new one, or keep the already installed + version. + :param prefer_source: used to tell if the user prefer source distributions + over built dists. + :param prefer_final: if set to true, pick up the "final" versions (eg. + stable) over the beta, alpha (not final) ones. + """ + # get the default index if none is specified + if not index: + index = wrapper.WrapperClient() + + # check if the project is already installed. + installed_release = get_installed_release(requirements) + + # if a version that satisfy the requirements is already installed + if installed_release and (interactive or upgrade): + new_releases = index.get_releases(requirements) + if (new_releases.get_last(requirements).version > + installed_release.version): + if interactive: + # prompt the user to install the last version of the package. + # set upgrade here. + print "You want to install a package already installed on your" + "system. A new version exists, you could just use the version" + "you have, or upgrade to the latest version" + + upgrade = raw_input("Do you want to install the most recent one ? (Y/n)") or "Y" + if upgrade in ('Y', 'y'): + upgrade = True + else: + upgrade = False + if not upgrade: + return + + # create the depgraph from the dependencies of the release we want to + # install + graph = generate_graph(get_deps(requirements, index)) + from ipdb import set_trace + set_trace() + installed = [] # to uninstall on errors + try: + for release in graph.adjacency_list: + dist = release.get_distribution() + install(dist) + installed.append(dist) + print "%s have been installed on your system" % requirements + except: + print "an error has occured, uninstalling" + for dist in installed: + uninstall_dist(dist) + +class InstallationException(Exception): + pass + +def get_install_info(requirements, index=None, already_installed=None): + """Return the informations on what's going to be installed and upgraded. + + :param requirements: is a *string* containing the requirements for this + project (for instance "FooBar 1.1" or "BarBaz (<1.2)") + :param index: If an index is specified, use this one, otherwise, use + :class index.ClientWrapper: to get project metadatas. + :param already_installed: a list of already installed distributions. + + The results are returned in a dict. For instance:: + + >>> get_install_info("FooBar (<=1.2)") + {'install': [], 'remove': [], 'conflict': []} + + Conflict contains all the conflicting distributions, if there is a + conflict. + + """ + def update_infos(new_infos, infos): + for key, value in infos.items(): + if key in new_infos: + infos[key].extend(new_infos[key]) + return new_infos + + if not index: + index = wrapper.ClientWrapper() + logging.info("searching releases for %s" % requirements) + + # 1. get all the releases that match the requirements + try: + releases = index.get_releases(requirements) + except (ReleaseNotFound, ProjectNotFound), e: + raise InstallationException('Release not found: "%s"' % requirements) + + # 2. pick up a release, and try to get the dependency tree + release = releases.get_last(requirements) + metadata = release.fetch_metadata() + + # 3. get the distributions already_installed on the system + # 4. and add the one we want to install + if not already_installed: + already_installed = get_distributions() + + logging.info("fetching %s %s dependencies" % ( + release.name, release.version)) + distributions = already_installed + [release] + depgraph = generate_graph(distributions) + + # store all the already_installed packages in a list, in case of rollback. + infos = {'install':[], 'remove': [], 'conflict': []} + + # 5. get what the missing deps are + for dists in depgraph.missing.values(): + if dists: + logging.info("missing dependencies found, installing them") + # we have missing deps + for dist in dists: + update_infos(get_install_info(dist, index, already_installed), + infos) + + # 6. fill in the infos + existing = [d for d in already_installed if d.name == release.name] + if existing: + infos['remove'].append(existing[0]) + infos['conflict'].extend(depgraph.reverse_list[existing[0]]) + infos['install'].append(release) + return infos diff --git a/src/distutils2/manifest.py b/src/distutils2/manifest.py --- a/src/distutils2/manifest.py +++ b/src/distutils2/manifest.py @@ -22,7 +22,7 @@ # a \ followed by some spaces + EOL _COLLAPSE_PATTERN = re.compile('\\\w*\n', re.M) -_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M|re.S) +_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M | re.S) class Manifest(object): """A list of files built by on exploring the filesystem and filtered by diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -3,10 +3,10 @@ Supports all metadata formats (1.0, 1.1, 1.2). """ -import re import os import sys import platform +import re from StringIO import StringIO from email import message_from_file from tokenize import tokenize, NAME, OP, STRING, ENDMARKER @@ -52,12 +52,12 @@ PKG_INFO_PREFERRED_VERSION = '1.0' _LINE_PREFIX = re.compile('\n \|') -_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'License') -_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'License', 'Classifier', 'Download-URL', 'Obsoletes', @@ -65,7 +65,7 @@ _314_MARKERS = ('Obsoletes', 'Provides', 'Requires') -_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', @@ -92,6 +92,7 @@ return _345_FIELDS raise MetadataUnrecognizedVersionError(version) + def _best_version(fields): """Detect the best version depending on the fields used.""" def _has_marker(keys, markers): @@ -349,7 +350,7 @@ Empty values (e.g. None and []) are not setted this way. """ def _set(key, value): - if value not in ([], None) and key in _ATTR2FIELD: + if value not in ([], None, '') and key in _ATTR2FIELD: self.set(self._convert_name(key), value) if other is None: @@ -387,13 +388,16 @@ for v in value: # check that the values are valid predicates if not is_valid_predicate(v.split(';')[0]): - warn('"%s" is not a valid predicate' % v) + warn('"%s" is not a valid predicate (field "%s")' % + (v, name)) elif name in _VERSIONS_FIELDS and value is not None: if not is_valid_versions(value): - warn('"%s" is not a valid predicate' % value) + warn('"%s" is not a valid version (field "%s")' % + (value, name)) elif name in _VERSION_FIELDS and value is not None: if not is_valid_version(value): - warn('"%s" is not a valid version' % value) + warn('"%s" is not a valid version (field "%s")' % + (value, name)) if name in _UNICODEFIELDS: value = self._encode_field(value) @@ -448,7 +452,7 @@ missing.append(attr) if _HAS_DOCUTILS: - warnings = self._check_rst_data(self['Description']) + warnings.extend(self._check_rst_data(self['Description'])) # checking metadata 1.2 (XXX needs to check 1.1, 1.0) if self['Metadata-Version'] != '1.2': @@ -497,6 +501,7 @@ 'in': lambda x, y: x in y, 'not in': lambda x, y: x not in y} + def _operate(operation, x, y): return _OPERATORS[operation](x, y) @@ -508,6 +513,7 @@ 'platform.version': platform.version(), 'platform.machine': platform.machine()} + class _Operation(object): def __init__(self, execution_context=None): @@ -568,6 +574,7 @@ right = self._convert(self.right) return _operate(self.op, left, right) + class _OR(object): def __init__(self, left, right=None): self.left = left @@ -597,6 +604,7 @@ def __call__(self): return self.left() and self.right() + class _CHAIN(object): def __init__(self, execution_context=None): @@ -658,6 +666,7 @@ return False return True + def _interpret(marker, execution_context=None): """Interpret a marker and return a result depending on environment.""" marker = marker.strip() diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py --- a/src/distutils2/mkpkg.py +++ b/src/distutils2/mkpkg.py @@ -30,7 +30,11 @@ # # Detect scripts (not sure how. #! outside of package?) -import sys, os, re, shutil, ConfigParser +import sys +import os +import re +import shutil +import ConfigParser helpText = { @@ -629,19 +633,20 @@ print '\nERROR: You must select "Y" or "N".\n' -def ask(question, default = None, helptext = None, required = True, - lengthy = False, multiline = False): - prompt = '%s: ' % ( question, ) +def ask(question, default=None, helptext=None, required=True, + lengthy=False, multiline=False): + prompt = '%s: ' % (question,) if default: - prompt = '%s [%s]: ' % ( question, default ) + prompt = '%s [%s]: ' % (question, default) if default and len(question) + len(default) > 70: - prompt = '%s\n [%s]: ' % ( question, default ) + prompt = '%s\n [%s]: ' % (question, default) if lengthy or multiline: prompt += '\n >' - if not helptext: helptext = 'No additional help available.' - if helptext[0] == '\n': helptext = helptext[1:] - if helptext[-1] == '\n': helptext = helptext[:-1] + if not helptext: + helptext = 'No additional help available.' + + helptext = helptext.strip("\n") while True: sys.stdout.write(prompt) @@ -653,12 +658,14 @@ print helptext print '=' * 70 continue - if default and not line: return(default) + if default and not line: + return(default) if not line and required: print '*' * 70 print 'This value cannot be empty.' print '===========================' - if helptext: print helptext + if helptext: + print helptext print '*' * 70 continue return(line) @@ -669,7 +676,8 @@ for key in troveList: subDict = dict for subkey in key.split(' :: '): - if not subkey in subDict: subDict[subkey] = {} + if not subkey in subDict: + subDict[subkey] = {} subDict = subDict[subkey] return(dict) troveDict = buildTroveDict(troveList) @@ -687,7 +695,8 @@ def lookupOption(self, key): - if not self.config.has_option('DEFAULT', key): return(None) + if not self.config.has_option('DEFAULT', key): + return(None) return(self.config.get('DEFAULT', key)) @@ -706,7 +715,8 @@ self.config.set('DEFAULT', compareKey, self.setupData[compareKey]) - if not valuesDifferent: return + if not valuesDifferent: + return self.config.write(open(os.path.expanduser('~/.pygiver'), 'w')) @@ -718,7 +728,7 @@ def inspectFile(self, path): fp = open(path, 'r') try: - for line in [ fp.readline() for x in range(10) ]: + for line in [fp.readline() for x in range(10)]: m = re.match(r'^#!.*python((?P\d)(\.\d+)?)?$', line) if m: if m.group('major') == '3': @@ -761,16 +771,16 @@ self.setupData.get('version'), helpText['version']) self.setupData['description'] = ask('Package description', self.setupData.get('description'), helpText['description'], - lengthy = True) + lengthy=True) self.setupData['author'] = ask('Author name', self.setupData.get('author'), helpText['author']) self.setupData['author_email'] = ask('Author e-mail address', self.setupData.get('author_email'), helpText['author_email']) self.setupData['url'] = ask('Project URL', - self.setupData.get('url'), helpText['url'], required = False) + self.setupData.get('url'), helpText['url'], required=False) if (askYn('Do you want to set Trove classifiers?', - helptext = helpText['do_classifier']) == 'y'): + helptext=helpText['do_classifier']) == 'y'): self.setTroveClassifier() @@ -781,8 +791,10 @@ def setTroveOther(self, classifierDict): - if askYn('Do you want to set other trove identifiers', 'n', - helpText['trove_generic']) != 'y': return + if askYn('Do you want to set other trove identifiers', + 'n', + helpText['trove_generic']) != 'y': + return self.walkTrove(classifierDict, [troveDict], '') @@ -799,7 +811,7 @@ continue if askYn('Do you want to set items under\n "%s" (%d sub-items)' - % ( key, len(trove[key]) ), 'n', + % (key, len(trove[key])), 'n', helpText['trove_generic']) == 'y': self.walkTrove(classifierDict, trovePath + [trove[key]], desc + ' :: ' + key) @@ -808,15 +820,18 @@ def setTroveLicense(self, classifierDict): while True: license = ask('What license do you use', - helptext = helpText['trove_license'], required = False) - if not license: return + helptext=helpText['trove_license'], + required=False) + if not license: + return licenseWords = license.lower().split(' ') foundList = [] for index in range(len(troveList)): troveItem = troveList[index] - if not troveItem.startswith('License :: '): continue + if not troveItem.startswith('License :: '): + continue troveItem = troveItem[11:].lower() allMatch = True @@ -824,17 +839,20 @@ if not word in troveItem: allMatch = False break - if allMatch: foundList.append(index) + if allMatch: + foundList.append(index) question = 'Matching licenses:\n\n' for i in xrange(1, len(foundList) + 1): - question += ' %s) %s\n' % ( i, troveList[foundList[i - 1]] ) + question += ' %s) %s\n' % (i, troveList[foundList[i - 1]]) question += ('\nType the number of the license you wish to use or ' '? to try again:') - troveLicense = ask(question, required = False) + troveLicense = ask(question, required=False) - if troveLicense == '?': continue - if troveLicense == '': return + if troveLicense == '?': + continue + if troveLicense == '': + return foundIndex = foundList[int(troveLicense) - 1] classifierDict[troveList[foundIndex]] = 1 try: @@ -856,7 +874,7 @@ 6 - Mature 7 - Inactive -Status''', required = False) +Status''', required=False) if devStatus: try: key = { @@ -884,7 +902,8 @@ return modified_pkgs def writeSetup(self): - if os.path.exists('setup.py'): shutil.move('setup.py', 'setup.py.old') + if os.path.exists('setup.py'): + shutil.move('setup.py', 'setup.py.old') fp = open('setup.py', 'w') try: diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py --- a/src/distutils2/tests/pypi_server.py +++ b/src/distutils2/tests/pypi_server.py @@ -1,13 +1,37 @@ -"""Mocked PyPI Server implementation, to use in tests. +"""Mock PyPI Server implementation, to use in tests. This module also provides a simple test case to extend if you need to use -the PyPIServer all along your test case. Be sure to read the documentation +the PyPIServer all along your test case. Be sure to read the documentation before any use. + +XXX TODO: + +The mock server can handle simple HTTP request (to simulate a simple index) or +XMLRPC requests, over HTTP. Both does not have the same intergface to deal +with, and I think it's a pain. + +A good idea could be to re-think a bit the way dstributions are handled in the +mock server. As it should return malformed HTML pages, we need to keep the +static behavior. + +I think of something like that: + + >>> server = PyPIMockServer() + >>> server.startHTTP() + >>> server.startXMLRPC() + +Then, the server must have only one port to rely on, eg. + + >>> server.fulladress() + "http://ip:port/" + +It could be simple to have one HTTP server, relaying the requests to the two +implementations (static HTTP and XMLRPC over HTTP). """ -import os import Queue import SocketServer +import os.path import select import socket import threading @@ -29,7 +53,7 @@ return use_pypi_server(*server_args, **server_kwargs) def use_pypi_server(*server_args, **server_kwargs): - """Decorator to make use of the PyPIServer for test methods, + """Decorator to make use of the PyPIServer for test methods, just when needed, and not for the entire duration of the testcase. """ def wrapper(func): @@ -51,8 +75,8 @@ self.pypi.start() def tearDown(self): + super(PyPIServerTestCase, self).tearDown() self.pypi.stop() - super(PyPIServerTestCase, self).tearDown() class PyPIServer(threading.Thread): """PyPI Mocked server. @@ -65,8 +89,8 @@ static_filesystem_paths=["default"], static_uri_paths=["simple"], serve_xmlrpc=False) : """Initialize the server. - - Default behavior is to start the HTTP server. You can either start the + + Default behavior is to start the HTTP server. You can either start the xmlrpc server by setting xmlrpc to True. Caution: Only one server will be started. @@ -80,6 +104,7 @@ self._run = True self._serve_xmlrpc = serve_xmlrpc + #TODO allow to serve XMLRPC and HTTP static files at the same time. if not self._serve_xmlrpc: self.server = HTTPServer(('', 0), PyPIRequestHandler) self.server.RequestHandlerClass.pypi_server = self @@ -89,7 +114,7 @@ self.default_response_status = 200 self.default_response_headers = [('Content-type', 'text/plain')] self.default_response_data = "hello" - + # initialize static paths / filesystems self.static_uri_paths = static_uri_paths if test_static_path is not None: @@ -97,7 +122,7 @@ self.static_filesystem_paths = [PYPI_DEFAULT_STATIC_PATH + "/" + path for path in static_filesystem_paths] else: - # xmlrpc server + # XMLRPC server self.server = PyPIXMLRPCServer(('', 0)) self.xmlrpc = XMLRPCMockIndex() # register the xmlrpc methods @@ -176,7 +201,7 @@ # serve the content from local disc if we request an URL beginning # by a pattern defined in `static_paths` url_parts = self.path.split("/") - if (len(url_parts) > 1 and + if (len(url_parts) > 1 and url_parts[1] in self.pypi_server.static_uri_paths): data = None # always take the last first. @@ -214,7 +239,7 @@ try: status = int(status) except ValueError: - # we probably got something like YYY Codename. + # we probably got something like YYY Codename. # Just get the first 3 digits status = int(status[:3]) @@ -233,7 +258,7 @@ self.server_port = port class MockDist(object): - """Fake distribution, used in the Mock PyPI Server""" + """Fake distribution, used in the Mock PyPI Server""" def __init__(self, name, version="1.0", hidden=False, url="http://url/", type="sdist", filename="", size=10000, digest="123456", downloads=7, has_sig=False, @@ -252,7 +277,7 @@ self.name = name self.version = version self.hidden = hidden - + # URL infos self.url = url self.digest = digest @@ -261,7 +286,7 @@ self.python_version = python_version self.comment = comment self.type = type - + # metadata self.author = author self.author_email = author_email @@ -280,7 +305,7 @@ self.cheesecake_documentation_id = documentation_id self.cheesecake_code_kwalitee_id = code_kwalitee_id self.cheesecake_installability_id = installability_id - + self.obsoletes = obsoletes self.obsoletes_dist = obsoletes_dist self.provides = provides @@ -289,7 +314,7 @@ self.requires_dist = requires_dist self.requires_external = requires_external self.requires_python = requires_python - + def url_infos(self): return { 'url': self.url, @@ -331,11 +356,12 @@ 'summary': self.summary, 'home_page': self.homepage, 'stable_version': self.stable_version, - 'provides_dist': self.provides_dist, + 'provides_dist': self.provides_dist or "%s (%s)" % (self.name, + self.version), 'requires': self.requires, 'cheesecake_installability_id': self.cheesecake_installability_id, } - + def search_result(self): return { '_pypi_ordering': 0, @@ -346,7 +372,7 @@ class XMLRPCMockIndex(object): """Mock XMLRPC server""" - + def __init__(self, dists=[]): self._dists = dists @@ -385,7 +411,7 @@ # return only un-hidden return [d.version for d in self._dists if d.name == package_name and not d.hidden] - + def release_urls(self, package_name, version): return [d.url_infos() for d in self._dists if d.name == package_name and d.version == version] diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -1,7 +1,7 @@ """Support code for distutils2 test cases. Always import unittest from this module, it will be the right version -(standard library unittest for 2.7 and higher, third-party unittest2 +(standard library unittest for 3.2 and higher, third-party unittest2 release for older versions). Three helper classes are provided: LoggingSilencer, TempdirManager and @@ -17,10 +17,10 @@ tearDown): def setUp(self): - super(self.__class__, self).setUp() + super(SomeTestCase, self).setUp() ... # other setup code -Read each class' docstring to see their purpose and usage. +Read each class' docstring to see its purpose and usage. Also provided is a DummyCommand class, useful to mock commands in the tests of another command that needs them (see docstring). diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -153,6 +153,27 @@ my_file2 = os.path.join(tmp_dir, 'f2') dist.metadata.write_file(open(my_file, 'w')) + def test_bad_attr(self): + cls = Distribution + + # catching warnings + warns = [] + def _warn(msg): + warns.append(msg) + + old_warn = warnings.warn + warnings.warn = _warn + try: + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'badoptname': 'xxx'}) + finally: + warnings.warn = old_warn + + self.assertTrue(len(warns)==1 and "Unknown distribution" in warns[0]) + def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes @@ -176,6 +197,21 @@ self.assertEqual(len(warns), 0) + def test_non_empty_options(self): + # TODO: how to actually use options is not documented except + # for a few cryptic comments in dist.py. If this is to stay + # in the public API, it deserves some better documentation. + + # Here is an example of how it's used out there: + # http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html#specifying-customizations + cls = Distribution + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'options': dict(sdist=dict(owner="root"))}) + self.assertTrue("owner" in dist.get_option_dict("sdist")) + def test_finalize_options(self): attrs = {'keywords': 'one,two', diff --git a/src/distutils2/tests/test_install.py b/src/distutils2/tests/test_install.py --- a/src/distutils2/tests/test_install.py +++ b/src/distutils2/tests/test_install.py @@ -202,6 +202,9 @@ finally: f.close() + # XXX test that fancy_getopt is okay with options named + # record and no-record but unrelated + def _test_debug_mode(self): # this covers the code called when DEBUG is set old_logs_len = len(self.logs) diff --git a/src/distutils2/tests/test_install_with_deps.py b/src/distutils2/tests/test_install_with_deps.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_install_with_deps.py @@ -0,0 +1,152 @@ +"""Tests for the distutils2.index.xmlrpc module.""" + +from distutils2.tests.pypi_server import use_xmlrpc_server +from distutils2.tests import run_unittest +from distutils2.tests.support import unittest +from distutils2.index.xmlrpc import Client +from distutils2.install_with_deps import (get_install_info, + InstallationException) +from distutils2.metadata import DistributionMetadata + +class FakeDist(object): + """A fake distribution object, for tests""" + def __init__(self, name, version, deps): + self.name = name + self.version = version + self.metadata = DistributionMetadata() + self.metadata['Requires-Dist'] = deps + self.metadata['Provides-Dist'] = ['%s (%s)' % (name, version)] + + def __repr__(self): + return '' % self.name + +def get_fake_dists(dists): + objects = [] + for (name, version, deps) in dists: + objects.append(FakeDist(name, version, deps)) + return objects + +class TestInstallWithDeps(unittest.TestCase): + def _get_client(self, server, *args, **kwargs): + return Client(server.full_address, *args, **kwargs) + + @use_xmlrpc_server() + def test_existing_deps(self, server): + # Test that the installer get the dependencies from the metadatas + # and ask the index for this dependencies. + # In this test case, we have choxie that is dependent from towel-stuff + # 0.1, which is in-turn dependent on bacon <= 0.2: + # choxie -> towel-stuff -> bacon. + # Each release metadata is not provided in metadata 1.2. + client = self._get_client(server) + archive_path = '%s/distribution.tar.gz' % server.full_address + server.xmlrpc.set_distributions([ + {'name':'choxie', + 'version': '2.0.0.9', + 'requires_dist': ['towel-stuff (0.1)',], + 'url': archive_path}, + {'name':'towel-stuff', + 'version': '0.1', + 'requires_dist': ['bacon (<= 0.2)',], + 'url': archive_path}, + {'name':'bacon', + 'version': '0.1', + 'requires_dist': [], + 'url': archive_path}, + ]) + installed = get_fake_dists([('bacon', '0.1', []),]) + output = get_install_info("choxie", index=client, + already_installed=installed) + + # we dont have installed bacon as it's already installed on the system. + self.assertEqual(0, len(output['remove'])) + self.assertEqual(2, len(output['install'])) + readable_output = [(o.name, '%s' % o.version) + for o in output['install']] + self.assertIn(('towel-stuff', '0.1'), readable_output) + self.assertIn(('choxie', '2.0.0.9'), readable_output) + + @use_xmlrpc_server() + def test_upgrade_existing_deps(self, server): + # Tests that the existing distributions can be upgraded if needed. + client = self._get_client(server) + archive_path = '%s/distribution.tar.gz' % server.full_address + server.xmlrpc.set_distributions([ + {'name':'choxie', + 'version': '2.0.0.9', + 'requires_dist': ['towel-stuff (0.1)',], + 'url': archive_path}, + {'name':'towel-stuff', + 'version': '0.1', + 'requires_dist': ['bacon (>= 0.2)',], + 'url': archive_path}, + {'name':'bacon', + 'version': '0.2', + 'requires_dist': [], + 'url': archive_path}, + ]) + + output = get_install_info("choxie", index=client, already_installed= + get_fake_dists([('bacon', '0.1', []),])) + installed = [(o.name, '%s' % o.version) for o in output['install']] + + # we need bacon 0.2, but 0.1 is installed. + # So we expect to remove 0.1 and to install 0.2 instead. + remove = [(o.name, '%s' % o.version) for o in output['remove']] + self.assertIn(('choxie', '2.0.0.9'), installed) + self.assertIn(('towel-stuff', '0.1'), installed) + self.assertIn(('bacon', '0.2'), installed) + self.assertIn(('bacon', '0.1'), remove) + self.assertEqual(0, len(output['conflict'])) + + @use_xmlrpc_server() + def test_conflicts(self, server): + # Tests that conflicts are detected + client = self._get_client(server) + archive_path = '%s/distribution.tar.gz' % server.full_address + server.xmlrpc.set_distributions([ + {'name':'choxie', + 'version': '2.0.0.9', + 'requires_dist': ['towel-stuff (0.1)',], + 'url': archive_path}, + {'name':'towel-stuff', + 'version': '0.1', + 'requires_dist': ['bacon (>= 0.2)',], + 'url': archive_path}, + {'name':'bacon', + 'version': '0.2', + 'requires_dist': [], + 'url': archive_path}, + ]) + already_installed = [('bacon', '0.1', []), + ('chicken', '1.1', ['bacon (0.1)'])] + output = get_install_info("choxie", index=client, already_installed= + get_fake_dists(already_installed)) + + # we need bacon 0.2, but 0.1 is installed. + # So we expect to remove 0.1 and to install 0.2 instead. + installed = [(o.name, '%s' % o.version) for o in output['install']] + remove = [(o.name, '%s' % o.version) for o in output['remove']] + conflict = [(o.name, '%s' % o.version) for o in output['conflict']] + self.assertIn(('choxie', '2.0.0.9'), installed) + self.assertIn(('towel-stuff', '0.1'), installed) + self.assertIn(('bacon', '0.2'), installed) + self.assertIn(('bacon', '0.1'), remove) + self.assertIn(('chicken', '1.1'), conflict) + + @use_xmlrpc_server() + def test_installation_unexisting_project(self, server): + # Test that the isntalled raises an exception if the project does not + # exists. + client = self._get_client(server) + self.assertRaises(InstallationException, get_install_info, + 'unexistant project', index=client) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestInstallWithDeps)) + return suite + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/src/distutils2/version.py b/src/distutils2/version.py --- a/src/distutils2/version.py +++ b/src/distutils2/version.py @@ -349,6 +349,7 @@ } def __init__(self, predicate): + self._string = predicate predicate = predicate.strip() match = _PREDICATE.match(predicate) if match is None: @@ -374,6 +375,9 @@ return False return True + def __repr__(self): + return self._string + class _Versions(VersionPredicate): def __init__(self, predicate): @@ -418,3 +422,12 @@ return False else: return True + + +def get_version_predicate(requirements): + """Return a VersionPredicate object, from a string or an already + existing object. + """ + if isinstance(requirements, str): + requirements = VersionPredicate(requirements) + return requirements diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -5,9 +5,19 @@ """ import sys -from os.path import dirname, islink, realpath +from os.path import dirname, islink, realpath, join, abspath from optparse import OptionParser +COVERAGE_FILE = join(dirname(abspath(__file__)), '.coverage') + +def get_coverage(): + """ Return a usable coverage object. """ + # deferred import because coverage is optional + import coverage + cov = getattr(coverage, "the_coverage", None) + if not cov: + cov = coverage.coverage(COVERAGE_FILE) + return cov def ignore_prefixes(module): """ Return a list of prefixes to ignore in the coverage report if @@ -44,10 +54,17 @@ def coverage_report(opts): - import coverage from distutils2.tests.support import unittest - cov = coverage.coverage() - cov.load() + cov = get_coverage() + if hasattr(cov, "load"): + # running coverage 3.x + cov.load() + morfs = None + else: + # running coverage 2.x + cov.cache = COVERAGE_FILE + cov.restore() + morfs = [m for m in cov.cexecuted.keys() if "distutils2" in m] prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] prefixes += ignore_prefixes(unittest) @@ -66,7 +83,7 @@ # that module is also completely optional pass - cov.report(omit_prefixes=prefixes, show_missing=opts.show_missing) + cov.report(morfs, omit_prefixes=prefixes, show_missing=opts.show_missing) def test_main(): @@ -74,11 +91,8 @@ verbose = not opts.quiet ret = 0 - if opts.coverage or opts.report: - import coverage - if opts.coverage: - cov = coverage.coverage() + cov = get_coverage() cov.erase() cov.start() if not opts.report: diff --git a/src/tests.sh b/src/tests.sh --- a/src/tests.sh +++ b/src/tests.sh @@ -4,36 +4,37 @@ python2.4 setup.py build_ext -f -q 2> /dev/null > /dev/null python2.4 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then - echo "Failed" + echo Failed rm -f distutils2/_backport/_hashlib.so exit 1 else - echo "Success" + echo Success fi echo -n "Running tests for Python 2.5... " python2.5 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then - echo "Failed" + echo Failed exit 1 else - echo "Success" + echo Success fi echo -n "Running tests for Python 2.6... " python2.6 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then - echo "Failed" + echo Failed exit 1 else - echo "Success" + echo Success fi echo -n "Running tests for Python 2.7... " python2.7 -Wd -bb -3 runtests.py -q 2> /dev/null if [ $? -ne 0 ];then - echo "Failed" + echo Failed exit 1 else - echo "Success" + echo Success fi +echo Good job, commit now! -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: test command uses resolve_dotted_name, desctiption added, _backport.any fix, Message-ID: tarek.ziade pushed 8e9ad8266328 to distutils2: http://hg.python.org/distutils2/rev/8e9ad8266328 changeset: 611:8e9ad8266328 user: Konrad Delong date: Thu Aug 12 19:59:22 2010 +0200 summary: test command uses resolve_dotted_name, desctiption added, _backport.any fix, added test to commands.__all__ files: src/distutils2/_backport/__init__.py, src/distutils2/command/__init__.py, src/distutils2/command/test.py diff --git a/src/distutils2/_backport/__init__.py b/src/distutils2/_backport/__init__.py --- a/src/distutils2/_backport/__init__.py +++ b/src/distutils2/_backport/__init__.py @@ -4,5 +4,5 @@ def any(seq): for elem in seq: if elem: - return elem + return True return False diff --git a/src/distutils2/command/__init__.py b/src/distutils2/command/__init__.py --- a/src/distutils2/command/__init__.py +++ b/src/distutils2/command/__init__.py @@ -23,5 +23,6 @@ 'bdist_dumb', 'bdist_wininst', 'register', + 'test', 'upload', ] diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -1,32 +1,13 @@ import os, sys from distutils2.core import Command from distutils2._backport.pkgutil import get_distribution +from distutils2.util import resolve_dotted_name import unittest import warnings -def get_loader_instance(dotted_path): - if dotted_path is None: - return None - module_name, rest = dotted_path.split('.')[0], dotted_path.split('.')[1:] - while True: - try: - ret = __import__(module_name) - break - except ImportError: - if rest == []: - return None - module_name += ('.' + rest[0]) - rest = rest[1:] - while rest: - try: - ret = getattr(ret, rest.pop(0)) - except AttributeError: - return None - return ret() - class test(Command): - description = "" # TODO + description = "run the distribution's test suite" user_options = [ ('test-suite=', 's', "Test suite to run (e.g. 'some_module.test_suite')"), @@ -56,7 +37,7 @@ os.chdir(self.build_lib) args = {"module": self.test_suite, "argv": sys.argv[:1], - "testLoader": get_loader_instance(self.test_loader) + "testLoader": resolve_dotted_name(self.test_loader) } if args['testLoader'] is None: del args['testLoader'] -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: rearranged imports Message-ID: tarek.ziade pushed 12f5cbf3ab8e to distutils2: http://hg.python.org/distutils2/rev/12f5cbf3ab8e changeset: 614:12f5cbf3ab8e user: Konrad Delong date: Thu Aug 12 20:04:12 2010 +0200 summary: rearranged imports files: src/distutils2/tests/test_test.py diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -1,11 +1,15 @@ -import os, sys, shutil, re +import os +import re +import sys +import shutil +import subprocess + from copy import copy from os.path import join from StringIO import StringIO from distutils2.tests.support import unittest, TempdirManager from distutils2.command.test import test from distutils2.dist import Distribution -import subprocess try: any -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: replaced e.g. with "for example" Message-ID: tarek.ziade pushed 1843bcc7d7e4 to distutils2: http://hg.python.org/distutils2/rev/1843bcc7d7e4 changeset: 615:1843bcc7d7e4 user: Konrad Delong date: Thu Aug 12 20:06:15 2010 +0200 summary: replaced e.g. with "for example" files: docs/source/distutils/newcommands.rst diff --git a/docs/source/distutils/newcommands.rst b/docs/source/distutils/newcommands.rst --- a/docs/source/distutils/newcommands.rst +++ b/docs/source/distutils/newcommands.rst @@ -37,14 +37,14 @@ By default, tests will be run in the "verbose" mode of the ``unittest`` package's text test runner, but you can get the "quiet" mode (just dots) if you supply the ``-q`` or ``--quiet`` option, either as a global option to -the setup script (e.g. ``setup.py -q test``) or as an option for the ``test`` -command itself (e.g. ``setup.py test -q``). There is one other option +the setup script (for example ``setup.py -q test``) or as an option for the ``test`` +command itself (for example ``setup.py test -q``). There is one other option available: ``--test-suite=NAME, -s NAME`` Specify the test suite (or module, class, or method) to be run - (e.g. ``some_module.test_suite``). The default for this option can be - set by giving a ``test_suite`` argument to the ``setup()`` function, e.g.:: + (for example ``some_module.test_suite``). The default for this option can be + set by giving a ``test_suite`` argument to the ``setup()`` function, for example:: setup( # ... @@ -113,7 +113,7 @@ command -- just like the ``upload`` command. Assuming there is an ``Example`` project with documentation in the -subdirectory ``docs``, e.g.:: +subdirectory ``docs``, for example:: Example/ |-- example.py @@ -134,7 +134,7 @@ python setup.py upload_docs --upload-dir=docs/build/html As with any other ``setuptools`` based command, you can define useful -defaults in the ``setup.cfg`` of your Python project, e.g.: +defaults in the ``setup.cfg`` of your Python project, for example: .. code-block:: ini -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: removed vertical align Message-ID: tarek.ziade pushed d836af1591d2 to distutils2: http://hg.python.org/distutils2/rev/d836af1591d2 changeset: 613:d836af1591d2 user: Konrad Delong date: Thu Aug 12 20:03:09 2010 +0200 summary: removed vertical align files: src/distutils2/command/test.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -16,7 +16,7 @@ ] def initialize_options(self): - self.test_suite = None + self.test_suite = None self.test_loader = None def finalize_options(self): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: merged from upstream Message-ID: tarek.ziade pushed 4e62116dae8c to distutils2: http://hg.python.org/distutils2/rev/4e62116dae8c changeset: 612:4e62116dae8c parent: 611:8e9ad8266328 parent: 553:80265368bda0 user: Konrad Delong date: Thu Aug 12 20:00:18 2010 +0200 summary: merged from upstream files: src/distutils2/dist.py diff --git a/src/distutils2/_trove.py b/src/distutils2/_trove.py new file mode 100644 --- /dev/null +++ b/src/distutils2/_trove.py @@ -0,0 +1,552 @@ +# Temporary helper for mkpkg. + +# XXX get the list from PyPI and cache it instead of hardcoding + +# XXX see if it would be more useful to store it as another structure +# than a list of strings + +all_classifiers = [ + 'Development Status :: 1 - Planning', + 'Development Status :: 2 - Pre-Alpha', + 'Development Status :: 3 - Alpha', + 'Development Status :: 4 - Beta', + 'Development Status :: 5 - Production/Stable', + 'Development Status :: 6 - Mature', + 'Development Status :: 7 - Inactive', + 'Environment :: Console', + 'Environment :: Console :: Curses', + 'Environment :: Console :: Framebuffer', + 'Environment :: Console :: Newt', + 'Environment :: Console :: svgalib', + "Environment :: Handhelds/PDA's", + 'Environment :: MacOS X', + 'Environment :: MacOS X :: Aqua', + 'Environment :: MacOS X :: Carbon', + 'Environment :: MacOS X :: Cocoa', + 'Environment :: No Input/Output (Daemon)', + 'Environment :: Other Environment', + 'Environment :: Plugins', + 'Environment :: Web Environment', + 'Environment :: Web Environment :: Buffet', + 'Environment :: Web Environment :: Mozilla', + 'Environment :: Web Environment :: ToscaWidgets', + 'Environment :: Win32 (MS Windows)', + 'Environment :: X11 Applications', + 'Environment :: X11 Applications :: Gnome', + 'Environment :: X11 Applications :: GTK', + 'Environment :: X11 Applications :: KDE', + 'Environment :: X11 Applications :: Qt', + 'Framework :: BFG', + 'Framework :: Buildout', + 'Framework :: Chandler', + 'Framework :: CubicWeb', + 'Framework :: Django', + 'Framework :: IDLE', + 'Framework :: Paste', + 'Framework :: Plone', + 'Framework :: Pylons', + 'Framework :: Setuptools Plugin', + 'Framework :: Trac', + 'Framework :: TurboGears', + 'Framework :: TurboGears :: Applications', + 'Framework :: TurboGears :: Widgets', + 'Framework :: Twisted', + 'Framework :: ZODB', + 'Framework :: Zope2', + 'Framework :: Zope3', + 'Intended Audience :: Customer Service', + 'Intended Audience :: Developers', + 'Intended Audience :: Education', + 'Intended Audience :: End Users/Desktop', + 'Intended Audience :: Financial and Insurance Industry', + 'Intended Audience :: Healthcare Industry', + 'Intended Audience :: Information Technology', + 'Intended Audience :: Legal Industry', + 'Intended Audience :: Manufacturing', + 'Intended Audience :: Other Audience', + 'Intended Audience :: Religion', + 'Intended Audience :: Science/Research', + 'Intended Audience :: System Administrators', + 'Intended Audience :: Telecommunications Industry', + 'License :: Aladdin Free Public License (AFPL)', + 'License :: DFSG approved', + 'License :: Eiffel Forum License (EFL)', + 'License :: Free For Educational Use', + 'License :: Free For Home Use', + 'License :: Free for non-commercial use', + 'License :: Freely Distributable', + 'License :: Free To Use But Restricted', + 'License :: Freeware', + 'License :: Netscape Public License (NPL)', + 'License :: Nokia Open Source License (NOKOS)', + 'License :: OSI Approved', + 'License :: OSI Approved :: Academic Free License (AFL)', + 'License :: OSI Approved :: Apache Software License', + 'License :: OSI Approved :: Apple Public Source License', + 'License :: OSI Approved :: Artistic License', + 'License :: OSI Approved :: Attribution Assurance License', + 'License :: OSI Approved :: BSD License', + 'License :: OSI Approved :: Common Public License', + 'License :: OSI Approved :: Eiffel Forum License', + 'License :: OSI Approved :: European Union Public Licence 1.0 (EUPL 1.0)', + 'License :: OSI Approved :: European Union Public Licence 1.1 (EUPL 1.1)', + 'License :: OSI Approved :: GNU Affero General Public License v3', + 'License :: OSI Approved :: GNU Free Documentation License (FDL)', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', + 'License :: OSI Approved :: IBM Public License', + 'License :: OSI Approved :: Intel Open Source License', + 'License :: OSI Approved :: ISC License (ISCL)', + 'License :: OSI Approved :: Jabber Open Source License', + 'License :: OSI Approved :: MIT License', + 'License :: OSI Approved :: MITRE Collaborative Virtual Workspace License (CVW)', + 'License :: OSI Approved :: Motosoto License', + 'License :: OSI Approved :: Mozilla Public License 1.0 (MPL)', + 'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)', + 'License :: OSI Approved :: Nethack General Public License', + 'License :: OSI Approved :: Nokia Open Source License', + 'License :: OSI Approved :: Open Group Test Suite License', + 'License :: OSI Approved :: Python License (CNRI Python License)', + 'License :: OSI Approved :: Python Software Foundation License', + 'License :: OSI Approved :: Qt Public License (QPL)', + 'License :: OSI Approved :: Ricoh Source Code Public License', + 'License :: OSI Approved :: Sleepycat License', + 'License :: OSI Approved :: Sun Industry Standards Source License (SISSL)', + 'License :: OSI Approved :: Sun Public License', + 'License :: OSI Approved :: University of Illinois/NCSA Open Source License', + 'License :: OSI Approved :: Vovida Software License 1.0', + 'License :: OSI Approved :: W3C License', + 'License :: OSI Approved :: X.Net License', + 'License :: OSI Approved :: zlib/libpng License', + 'License :: OSI Approved :: Zope Public License', + 'License :: Other/Proprietary License', + 'License :: Public Domain', + 'License :: Repoze Public License', + 'Natural Language :: Afrikaans', + 'Natural Language :: Arabic', + 'Natural Language :: Bengali', + 'Natural Language :: Bosnian', + 'Natural Language :: Bulgarian', + 'Natural Language :: Catalan', + 'Natural Language :: Chinese (Simplified)', + 'Natural Language :: Chinese (Traditional)', + 'Natural Language :: Croatian', + 'Natural Language :: Czech', + 'Natural Language :: Danish', + 'Natural Language :: Dutch', + 'Natural Language :: English', + 'Natural Language :: Esperanto', + 'Natural Language :: Finnish', + 'Natural Language :: French', + 'Natural Language :: German', + 'Natural Language :: Greek', + 'Natural Language :: Hebrew', + 'Natural Language :: Hindi', + 'Natural Language :: Hungarian', + 'Natural Language :: Icelandic', + 'Natural Language :: Indonesian', + 'Natural Language :: Italian', + 'Natural Language :: Japanese', + 'Natural Language :: Javanese', + 'Natural Language :: Korean', + 'Natural Language :: Latin', + 'Natural Language :: Latvian', + 'Natural Language :: Macedonian', + 'Natural Language :: Malay', + 'Natural Language :: Marathi', + 'Natural Language :: Norwegian', + 'Natural Language :: Panjabi', + 'Natural Language :: Persian', + 'Natural Language :: Polish', + 'Natural Language :: Portuguese', + 'Natural Language :: Portuguese (Brazilian)', + 'Natural Language :: Romanian', + 'Natural Language :: Russian', + 'Natural Language :: Serbian', + 'Natural Language :: Slovak', + 'Natural Language :: Slovenian', + 'Natural Language :: Spanish', + 'Natural Language :: Swedish', + 'Natural Language :: Tamil', + 'Natural Language :: Telugu', + 'Natural Language :: Thai', + 'Natural Language :: Turkish', + 'Natural Language :: Ukranian', + 'Natural Language :: Urdu', + 'Natural Language :: Vietnamese', + 'Operating System :: BeOS', + 'Operating System :: MacOS', + 'Operating System :: MacOS :: MacOS 9', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft', + 'Operating System :: Microsoft :: MS-DOS', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: Microsoft :: Windows :: Windows 3.1 or Earlier', + 'Operating System :: Microsoft :: Windows :: Windows 95/98/2000', + 'Operating System :: Microsoft :: Windows :: Windows CE', + 'Operating System :: Microsoft :: Windows :: Windows NT/2000', + 'Operating System :: OS/2', + 'Operating System :: OS Independent', + 'Operating System :: Other OS', + 'Operating System :: PalmOS', + 'Operating System :: PDA Systems', + 'Operating System :: POSIX', + 'Operating System :: POSIX :: AIX', + 'Operating System :: POSIX :: BSD', + 'Operating System :: POSIX :: BSD :: BSD/OS', + 'Operating System :: POSIX :: BSD :: FreeBSD', + 'Operating System :: POSIX :: BSD :: NetBSD', + 'Operating System :: POSIX :: BSD :: OpenBSD', + 'Operating System :: POSIX :: GNU Hurd', + 'Operating System :: POSIX :: HP-UX', + 'Operating System :: POSIX :: IRIX', + 'Operating System :: POSIX :: Linux', + 'Operating System :: POSIX :: Other', + 'Operating System :: POSIX :: SCO', + 'Operating System :: POSIX :: SunOS/Solaris', + 'Operating System :: Unix', + 'Programming Language :: Ada', + 'Programming Language :: APL', + 'Programming Language :: ASP', + 'Programming Language :: Assembly', + 'Programming Language :: Awk', + 'Programming Language :: Basic', + 'Programming Language :: C', + 'Programming Language :: C#', + 'Programming Language :: C++', + 'Programming Language :: Cold Fusion', + 'Programming Language :: Cython', + 'Programming Language :: Delphi/Kylix', + 'Programming Language :: Dylan', + 'Programming Language :: Eiffel', + 'Programming Language :: Emacs-Lisp', + 'Programming Language :: Erlang', + 'Programming Language :: Euler', + 'Programming Language :: Euphoria', + 'Programming Language :: Forth', + 'Programming Language :: Fortran', + 'Programming Language :: Haskell', + 'Programming Language :: Java', + 'Programming Language :: JavaScript', + 'Programming Language :: Lisp', + 'Programming Language :: Logo', + 'Programming Language :: ML', + 'Programming Language :: Modula', + 'Programming Language :: Objective C', + 'Programming Language :: Object Pascal', + 'Programming Language :: OCaml', + 'Programming Language :: Other', + 'Programming Language :: Other Scripting Engines', + 'Programming Language :: Pascal', + 'Programming Language :: Perl', + 'Programming Language :: PHP', + 'Programming Language :: Pike', + 'Programming Language :: Pliant', + 'Programming Language :: PL/SQL', + 'Programming Language :: PROGRESS', + 'Programming Language :: Prolog', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.3', + 'Programming Language :: Python :: 2.4', + 'Programming Language :: Python :: 2.5', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.0', + 'Programming Language :: Python :: 3.1', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: REBOL', + 'Programming Language :: Rexx', + 'Programming Language :: Ruby', + 'Programming Language :: Scheme', + 'Programming Language :: Simula', + 'Programming Language :: Smalltalk', + 'Programming Language :: SQL', + 'Programming Language :: Tcl', + 'Programming Language :: Unix Shell', + 'Programming Language :: Visual Basic', + 'Programming Language :: XBasic', + 'Programming Language :: YACC', + 'Programming Language :: Zope', + 'Topic :: Adaptive Technologies', + 'Topic :: Artistic Software', + 'Topic :: Communications', + 'Topic :: Communications :: BBS', + 'Topic :: Communications :: Chat', + 'Topic :: Communications :: Chat :: AOL Instant Messenger', + 'Topic :: Communications :: Chat :: ICQ', + 'Topic :: Communications :: Chat :: Internet Relay Chat', + 'Topic :: Communications :: Chat :: Unix Talk', + 'Topic :: Communications :: Conferencing', + 'Topic :: Communications :: Email', + 'Topic :: Communications :: Email :: Address Book', + 'Topic :: Communications :: Email :: Email Clients (MUA)', + 'Topic :: Communications :: Email :: Filters', + 'Topic :: Communications :: Email :: Mailing List Servers', + 'Topic :: Communications :: Email :: Mail Transport Agents', + 'Topic :: Communications :: Email :: Post-Office', + 'Topic :: Communications :: Email :: Post-Office :: IMAP', + 'Topic :: Communications :: Email :: Post-Office :: POP3', + 'Topic :: Communications :: Fax', + 'Topic :: Communications :: FIDO', + 'Topic :: Communications :: File Sharing', + 'Topic :: Communications :: File Sharing :: Gnutella', + 'Topic :: Communications :: File Sharing :: Napster', + 'Topic :: Communications :: Ham Radio', + 'Topic :: Communications :: Internet Phone', + 'Topic :: Communications :: Telephony', + 'Topic :: Communications :: Usenet News', + 'Topic :: Database', + 'Topic :: Database :: Database Engines/Servers', + 'Topic :: Database :: Front-Ends', + 'Topic :: Desktop Environment', + 'Topic :: Desktop Environment :: File Managers', + 'Topic :: Desktop Environment :: Gnome', + 'Topic :: Desktop Environment :: GNUstep', + 'Topic :: Desktop Environment :: K Desktop Environment (KDE)', + 'Topic :: Desktop Environment :: K Desktop Environment (KDE) :: Themes', + 'Topic :: Desktop Environment :: PicoGUI', + 'Topic :: Desktop Environment :: PicoGUI :: Applications', + 'Topic :: Desktop Environment :: PicoGUI :: Themes', + 'Topic :: Desktop Environment :: Screen Savers', + 'Topic :: Desktop Environment :: Window Managers', + 'Topic :: Desktop Environment :: Window Managers :: Afterstep', + 'Topic :: Desktop Environment :: Window Managers :: Afterstep :: Themes', + 'Topic :: Desktop Environment :: Window Managers :: Applets', + 'Topic :: Desktop Environment :: Window Managers :: Blackbox', + 'Topic :: Desktop Environment :: Window Managers :: Blackbox :: Themes', + 'Topic :: Desktop Environment :: Window Managers :: CTWM', + 'Topic :: Desktop Environment :: Window Managers :: CTWM :: Themes', + 'Topic :: Desktop Environment :: Window Managers :: Enlightenment', + 'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Epplets', + 'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR15', + 'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR16', + 'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR17', + 'Topic :: Desktop Environment :: Window Managers :: Fluxbox', + 'Topic :: Desktop Environment :: Window Managers :: Fluxbox :: Themes', + 'Topic :: Desktop Environment :: Window Managers :: FVWM', + 'Topic :: Desktop Environment :: Window Managers :: FVWM :: Themes', + 'Topic :: Desktop Environment :: Window Managers :: IceWM', + 'Topic :: Desktop Environment :: Window Managers :: IceWM :: Themes', + 'Topic :: Desktop Environment :: Window Managers :: MetaCity', + 'Topic :: Desktop Environment :: Window Managers :: MetaCity :: Themes', + 'Topic :: Desktop Environment :: Window Managers :: Oroborus', + 'Topic :: Desktop Environment :: Window Managers :: Oroborus :: Themes', + 'Topic :: Desktop Environment :: Window Managers :: Sawfish', + 'Topic :: Desktop Environment :: Window Managers :: Sawfish :: Themes 0.30', + 'Topic :: Desktop Environment :: Window Managers :: Sawfish :: Themes pre-0.30', + 'Topic :: Desktop Environment :: Window Managers :: Waimea', + 'Topic :: Desktop Environment :: Window Managers :: Waimea :: Themes', + 'Topic :: Desktop Environment :: Window Managers :: Window Maker', + 'Topic :: Desktop Environment :: Window Managers :: Window Maker :: Applets', + 'Topic :: Desktop Environment :: Window Managers :: Window Maker :: Themes', + 'Topic :: Desktop Environment :: Window Managers :: XFCE', + 'Topic :: Desktop Environment :: Window Managers :: XFCE :: Themes', + 'Topic :: Documentation', + 'Topic :: Education', + 'Topic :: Education :: Computer Aided Instruction (CAI)', + 'Topic :: Education :: Testing', + 'Topic :: Games/Entertainment', + 'Topic :: Games/Entertainment :: Arcade', + 'Topic :: Games/Entertainment :: Board Games', + 'Topic :: Games/Entertainment :: First Person Shooters', + 'Topic :: Games/Entertainment :: Fortune Cookies', + 'Topic :: Games/Entertainment :: Multi-User Dungeons (MUD)', + 'Topic :: Games/Entertainment :: Puzzle Games', + 'Topic :: Games/Entertainment :: Real Time Strategy', + 'Topic :: Games/Entertainment :: Role-Playing', + 'Topic :: Games/Entertainment :: Side-Scrolling/Arcade Games', + 'Topic :: Games/Entertainment :: Simulation', + 'Topic :: Games/Entertainment :: Turn Based Strategy', + 'Topic :: Home Automation', + 'Topic :: Internet', + 'Topic :: Internet :: File Transfer Protocol (FTP)', + 'Topic :: Internet :: Finger', + 'Topic :: Internet :: Log Analysis', + 'Topic :: Internet :: Name Service (DNS)', + 'Topic :: Internet :: Proxy Servers', + 'Topic :: Internet :: WAP', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: Browsers', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Message Boards', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: News/Diary', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Page Counters', + 'Topic :: Internet :: WWW/HTTP :: HTTP Servers', + 'Topic :: Internet :: WWW/HTTP :: Indexing/Search', + 'Topic :: Internet :: WWW/HTTP :: Site Management', + 'Topic :: Internet :: WWW/HTTP :: Site Management :: Link Checking', + 'Topic :: Internet :: WWW/HTTP :: WSGI', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Server', + 'Topic :: Internet :: Z39.50', + 'Topic :: Multimedia', + 'Topic :: Multimedia :: Graphics', + 'Topic :: Multimedia :: Graphics :: 3D Modeling', + 'Topic :: Multimedia :: Graphics :: 3D Rendering', + 'Topic :: Multimedia :: Graphics :: Capture', + 'Topic :: Multimedia :: Graphics :: Capture :: Digital Camera', + 'Topic :: Multimedia :: Graphics :: Capture :: Scanners', + 'Topic :: Multimedia :: Graphics :: Capture :: Screen Capture', + 'Topic :: Multimedia :: Graphics :: Editors', + 'Topic :: Multimedia :: Graphics :: Editors :: Raster-Based', + 'Topic :: Multimedia :: Graphics :: Editors :: Vector-Based', + 'Topic :: Multimedia :: Graphics :: Graphics Conversion', + 'Topic :: Multimedia :: Graphics :: Presentation', + 'Topic :: Multimedia :: Graphics :: Viewers', + 'Topic :: Multimedia :: Sound/Audio', + 'Topic :: Multimedia :: Sound/Audio :: Analysis', + 'Topic :: Multimedia :: Sound/Audio :: Capture/Recording', + 'Topic :: Multimedia :: Sound/Audio :: CD Audio', + 'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Playing', + 'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Ripping', + 'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Writing', + 'Topic :: Multimedia :: Sound/Audio :: Conversion', + 'Topic :: Multimedia :: Sound/Audio :: Editors', + 'Topic :: Multimedia :: Sound/Audio :: MIDI', + 'Topic :: Multimedia :: Sound/Audio :: Mixers', + 'Topic :: Multimedia :: Sound/Audio :: Players', + 'Topic :: Multimedia :: Sound/Audio :: Players :: MP3', + 'Topic :: Multimedia :: Sound/Audio :: Sound Synthesis', + 'Topic :: Multimedia :: Sound/Audio :: Speech', + 'Topic :: Multimedia :: Video', + 'Topic :: Multimedia :: Video :: Capture', + 'Topic :: Multimedia :: Video :: Conversion', + 'Topic :: Multimedia :: Video :: Display', + 'Topic :: Multimedia :: Video :: Non-Linear Editor', + 'Topic :: Office/Business', + 'Topic :: Office/Business :: Financial', + 'Topic :: Office/Business :: Financial :: Accounting', + 'Topic :: Office/Business :: Financial :: Investment', + 'Topic :: Office/Business :: Financial :: Point-Of-Sale', + 'Topic :: Office/Business :: Financial :: Spreadsheet', + 'Topic :: Office/Business :: Groupware', + 'Topic :: Office/Business :: News/Diary', + 'Topic :: Office/Business :: Office Suites', + 'Topic :: Office/Business :: Scheduling', + 'Topic :: Other/Nonlisted Topic', + 'Topic :: Printing', + 'Topic :: Religion', + 'Topic :: Scientific/Engineering', + 'Topic :: Scientific/Engineering :: Artificial Intelligence', + 'Topic :: Scientific/Engineering :: Astronomy', + 'Topic :: Scientific/Engineering :: Atmospheric Science', + 'Topic :: Scientific/Engineering :: Bio-Informatics', + 'Topic :: Scientific/Engineering :: Chemistry', + 'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)', + 'Topic :: Scientific/Engineering :: GIS', + 'Topic :: Scientific/Engineering :: Human Machine Interfaces', + 'Topic :: Scientific/Engineering :: Image Recognition', + 'Topic :: Scientific/Engineering :: Information Analysis', + 'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator', + 'Topic :: Scientific/Engineering :: Mathematics', + 'Topic :: Scientific/Engineering :: Medical Science Apps.', + 'Topic :: Scientific/Engineering :: Physics', + 'Topic :: Scientific/Engineering :: Visualization', + 'Topic :: Security', + 'Topic :: Security :: Cryptography', + 'Topic :: Sociology', + 'Topic :: Sociology :: Genealogy', + 'Topic :: Sociology :: History', + 'Topic :: Software Development', + 'Topic :: Software Development :: Assemblers', + 'Topic :: Software Development :: Bug Tracking', + 'Topic :: Software Development :: Build Tools', + 'Topic :: Software Development :: Code Generators', + 'Topic :: Software Development :: Compilers', + 'Topic :: Software Development :: Debuggers', + 'Topic :: Software Development :: Disassemblers', + 'Topic :: Software Development :: Documentation', + 'Topic :: Software Development :: Embedded Systems', + 'Topic :: Software Development :: Internationalization', + 'Topic :: Software Development :: Interpreters', + 'Topic :: Software Development :: Libraries', + 'Topic :: Software Development :: Libraries :: Application Frameworks', + 'Topic :: Software Development :: Libraries :: Java Libraries', + 'Topic :: Software Development :: Libraries :: Perl Modules', + 'Topic :: Software Development :: Libraries :: PHP Classes', + 'Topic :: Software Development :: Libraries :: Pike Modules', + 'Topic :: Software Development :: Libraries :: pygame', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Software Development :: Libraries :: Ruby Modules', + 'Topic :: Software Development :: Libraries :: Tcl Extensions', + 'Topic :: Software Development :: Localization', + 'Topic :: Software Development :: Object Brokering', + 'Topic :: Software Development :: Object Brokering :: CORBA', + 'Topic :: Software Development :: Pre-processors', + 'Topic :: Software Development :: Quality Assurance', + 'Topic :: Software Development :: Testing', + 'Topic :: Software Development :: Testing :: Traffic Generation', + 'Topic :: Software Development :: User Interfaces', + 'Topic :: Software Development :: Version Control', + 'Topic :: Software Development :: Version Control :: CVS', + 'Topic :: Software Development :: Version Control :: RCS', + 'Topic :: Software Development :: Version Control :: SCCS', + 'Topic :: Software Development :: Widget Sets', + 'Topic :: System', + 'Topic :: System :: Archiving', + 'Topic :: System :: Archiving :: Backup', + 'Topic :: System :: Archiving :: Compression', + 'Topic :: System :: Archiving :: Mirroring', + 'Topic :: System :: Archiving :: Packaging', + 'Topic :: System :: Benchmark', + 'Topic :: System :: Boot', + 'Topic :: System :: Boot :: Init', + 'Topic :: System :: Clustering', + 'Topic :: System :: Console Fonts', + 'Topic :: System :: Distributed Computing', + 'Topic :: System :: Emulators', + 'Topic :: System :: Filesystems', + 'Topic :: System :: Hardware', + 'Topic :: System :: Hardware :: Hardware Drivers', + 'Topic :: System :: Hardware :: Mainframes', + 'Topic :: System :: Hardware :: Symmetric Multi-processing', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Logging', + 'Topic :: System :: Monitoring', + 'Topic :: System :: Networking', + 'Topic :: System :: Networking :: Firewalls', + 'Topic :: System :: Networking :: Monitoring', + 'Topic :: System :: Networking :: Monitoring :: Hardware Watchdog', + 'Topic :: System :: Networking :: Time Synchronization', + 'Topic :: System :: Operating System', + 'Topic :: System :: Operating System Kernels', + 'Topic :: System :: Operating System Kernels :: BSD', + 'Topic :: System :: Operating System Kernels :: GNU Hurd', + 'Topic :: System :: Operating System Kernels :: Linux', + 'Topic :: System :: Power (UPS)', + 'Topic :: System :: Recovery Tools', + 'Topic :: System :: Shells', + 'Topic :: System :: Software Distribution', + 'Topic :: System :: Systems Administration', + 'Topic :: System :: Systems Administration :: Authentication/Directory', + 'Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP', + 'Topic :: System :: Systems Administration :: Authentication/Directory :: NIS', + 'Topic :: System :: System Shells', + 'Topic :: Terminals', + 'Topic :: Terminals :: Serial', + 'Topic :: Terminals :: Telnet', + 'Topic :: Terminals :: Terminal Emulators/X Terminals', + 'Topic :: Text Editors', + 'Topic :: Text Editors :: Documentation', + 'Topic :: Text Editors :: Emacs', + 'Topic :: Text Editors :: Integrated Development Environments (IDE)', + 'Topic :: Text Editors :: Text Processing', + 'Topic :: Text Editors :: Word Processors', + 'Topic :: Text Processing', + 'Topic :: Text Processing :: Filters', + 'Topic :: Text Processing :: Fonts', + 'Topic :: Text Processing :: General', + 'Topic :: Text Processing :: Indexing', + 'Topic :: Text Processing :: Linguistic', + 'Topic :: Text Processing :: Markup', + 'Topic :: Text Processing :: Markup :: HTML', + 'Topic :: Text Processing :: Markup :: LaTeX', + 'Topic :: Text Processing :: Markup :: SGML', + 'Topic :: Text Processing :: Markup :: VRML', + 'Topic :: Text Processing :: Markup :: XML', + 'Topic :: Utilities', +] diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -16,7 +16,7 @@ from distutils2.errors import (DistutilsOptionError, DistutilsArgError, DistutilsModuleError, DistutilsClassError) from distutils2.fancy_getopt import FancyGetopt, translate_longopt -from distutils2.util import check_environ, strtobool, resolve_dotted_name +from distutils2.util import check_environ, strtobool, resolve_name from distutils2 import log from distutils2.metadata import DistributionMetadata @@ -969,7 +969,7 @@ if hooks is None: return for hook in hooks.values(): - hook_func = resolve_dotted_name(hook) + hook_func = resolve_name(hook) hook_func(cmd_obj) # -- Distribution query methods ------------------------------------ diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -336,34 +336,28 @@ self._write_field(fileobject, field, value) def update(self, other=None, **kwargs): - """Set metadata values from the given mapping + """Set metadata values from the given iterable `other` and kwargs. - Convert the keys to Metadata fields. Given keys that don't match a - metadata argument will not be used. + Behavior is like `dict.update`: If `other` has a ``keys`` method, + they are looped over and ``self[key]`` is assigned ``other[key]``. + Else, ``other`` is an iterable of ``(key, value)`` iterables. - If overwrite is set to False, just add metadata values that are - actually not defined. - - If there is existing values in conflict with the dictionary ones, the - new values prevails. - - Empty values (e.g. None and []) are not setted this way. + Keys that don't match a metadata field or that have an empty value are + dropped. """ def _set(key, value): - if value not in ([], None, '') and key in _ATTR2FIELD: + if key in _ATTR2FIELD and value: self.set(self._convert_name(key), value) if other is None: pass - elif hasattr(other, 'iteritems'): # iteritems saves memory and lookups - for k, v in other.iteritems(): - _set(k, v) elif hasattr(other, 'keys'): for k in other.keys(): - _set(k, v) + _set(k, other[k]) else: for k, v in other: _set(k, v) + if kwargs: self.update(kwargs) diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py old mode 100755 new mode 100644 --- a/src/distutils2/mkpkg.py +++ b/src/distutils2/mkpkg.py @@ -12,623 +12,75 @@ # # Written by Sean Reifschneider # -# TODO: -# -# Look for a license file and automatically add the category. -# -# When a .c file is found during the walk, can we add it as an extension? -# -# Ask if the author is the maintainer? -# -# Ask for the platform (can we detect this via "import win32" or something)? -# -# Ask for the dependencies. -# -# Ask for the Requires-Dist -# -# Ask for the Provides-Dist -# -# Detect scripts (not sure how. #! outside of package?) +# Original TODO list: +# Look for a license file and automatically add the category. +# When a .c file is found during the walk, can we add it as an extension? +# Ask if there is a maintainer different that the author +# Ask for the platform (can we detect this via "import win32" or something?) +# Ask for the dependencies. +# Ask for the Requires-Dist +# Ask for the Provides-Dist +# Detect scripts (not sure how. #! outside of package?) +import os import sys -import os import re import shutil -import ConfigParser +from ConfigParser import RawConfigParser +from textwrap import dedent +# importing this with an underscore as it should be replaced by the +# dict form or another structures for all purposes +from distutils2._trove import all_classifiers as _CLASSIFIERS_LIST -helpText = { - 'name' : ''' +_helptext = { + 'name': ''' The name of the program to be packaged, usually a single word composed of lower-case characters such as "python", "sqlalchemy", or "CherryPy". ''', - 'version' : ''' + 'version': ''' Version number of the software, typically 2 or 3 numbers separated by dots such as "1.00", "0.6", or "3.02.01". "0.1.0" is recommended for initial development. ''', - 'description' : ''' + 'description': ''' A short summary of what this package is or does, typically a sentence 80 characters or less in length. ''', - 'author' : ''' + 'author': ''' The full name of the author (typically you). ''', - 'author_email' : ''' + 'author_email': ''' E-mail address of the package author (typically you). ''', - 'do_classifier' : ''' + 'do_classifier': ''' Trove classifiers are optional identifiers that allow you to specify the intended audience by saying things like "Beta software with a text UI for Linux under the PSF license. However, this can be a somewhat involved process. ''', - 'url' : ''' + 'url': ''' The home page for the package, typically starting with "http://". ''', - 'trove_license' : ''' + 'trove_license': ''' Optionally you can specify a license. Type a string that identifies a common license, and then you can select a list of license specifiers. ''', - 'trove_generic' : ''' + 'trove_generic': ''' Optionally, you can set other trove identifiers for things such as the human language, programming language, user interface, etc... ''', } -# XXX this list should be asked at PyPI (it changes) -# then cached, rather than hardcoded -troveList = [ - 'Development Status :: 1 - Planning', - 'Development Status :: 2 - Pre-Alpha', - 'Development Status :: 3 - Alpha', - 'Development Status :: 4 - Beta', - 'Development Status :: 5 - Production/Stable', - 'Development Status :: 6 - Mature', - 'Development Status :: 7 - Inactive', - 'Environment :: Console', - 'Environment :: Console :: Curses', - 'Environment :: Console :: Framebuffer', - 'Environment :: Console :: Newt', - 'Environment :: Console :: svgalib', - 'Environment :: Handhelds/PDA\'s', - 'Environment :: MacOS X', - 'Environment :: MacOS X :: Aqua', - 'Environment :: MacOS X :: Carbon', - 'Environment :: MacOS X :: Cocoa', - 'Environment :: No Input/Output (Daemon)', - 'Environment :: Other Environment', - 'Environment :: Plugins', - 'Environment :: Web Environment', - 'Environment :: Web Environment :: Buffet', - 'Environment :: Web Environment :: Mozilla', - 'Environment :: Web Environment :: ToscaWidgets', - 'Environment :: Win32 (MS Windows)', - 'Environment :: X11 Applications', - 'Environment :: X11 Applications :: Gnome', - 'Environment :: X11 Applications :: GTK', - 'Environment :: X11 Applications :: KDE', - 'Environment :: X11 Applications :: Qt', - 'Framework :: BFG', - 'Framework :: Buildout', - 'Framework :: Chandler', - 'Framework :: CubicWeb', - 'Framework :: Django', - 'Framework :: IDLE', - 'Framework :: Paste', - 'Framework :: Plone', - 'Framework :: Pylons', - 'Framework :: Setuptools Plugin', - 'Framework :: Trac', - 'Framework :: TurboGears', - 'Framework :: TurboGears :: Applications', - 'Framework :: TurboGears :: Widgets', - 'Framework :: Twisted', - 'Framework :: ZODB', - 'Framework :: Zope2', - 'Framework :: Zope3', - 'Intended Audience :: Customer Service', - 'Intended Audience :: Developers', - 'Intended Audience :: Education', - 'Intended Audience :: End Users/Desktop', - 'Intended Audience :: Financial and Insurance Industry', - 'Intended Audience :: Healthcare Industry', - 'Intended Audience :: Information Technology', - 'Intended Audience :: Legal Industry', - 'Intended Audience :: Manufacturing', - 'Intended Audience :: Other Audience', - 'Intended Audience :: Religion', - 'Intended Audience :: Science/Research', - 'Intended Audience :: System Administrators', - 'Intended Audience :: Telecommunications Industry', - 'License :: Aladdin Free Public License (AFPL)', - 'License :: DFSG approved', - 'License :: Eiffel Forum License (EFL)', - 'License :: Free For Educational Use', - 'License :: Free For Home Use', - 'License :: Free for non-commercial use', - 'License :: Freely Distributable', - 'License :: Free To Use But Restricted', - 'License :: Freeware', - 'License :: Netscape Public License (NPL)', - 'License :: Nokia Open Source License (NOKOS)', - 'License :: OSI Approved', - 'License :: OSI Approved :: Academic Free License (AFL)', - 'License :: OSI Approved :: Apache Software License', - 'License :: OSI Approved :: Apple Public Source License', - 'License :: OSI Approved :: Artistic License', - 'License :: OSI Approved :: Attribution Assurance License', - 'License :: OSI Approved :: BSD License', - 'License :: OSI Approved :: Common Public License', - 'License :: OSI Approved :: Eiffel Forum License', - 'License :: OSI Approved :: European Union Public Licence 1.0 (EUPL 1.0)', - 'License :: OSI Approved :: European Union Public Licence 1.1 (EUPL 1.1)', - 'License :: OSI Approved :: GNU Affero General Public License v3', - 'License :: OSI Approved :: GNU Free Documentation License (FDL)', - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', - 'License :: OSI Approved :: IBM Public License', - 'License :: OSI Approved :: Intel Open Source License', - 'License :: OSI Approved :: ISC License (ISCL)', - 'License :: OSI Approved :: Jabber Open Source License', - 'License :: OSI Approved :: MIT License', - 'License :: OSI Approved :: MITRE Collaborative Virtual Workspace License (CVW)', - 'License :: OSI Approved :: Motosoto License', - 'License :: OSI Approved :: Mozilla Public License 1.0 (MPL)', - 'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)', - 'License :: OSI Approved :: Nethack General Public License', - 'License :: OSI Approved :: Nokia Open Source License', - 'License :: OSI Approved :: Open Group Test Suite License', - 'License :: OSI Approved :: Python License (CNRI Python License)', - 'License :: OSI Approved :: Python Software Foundation License', - 'License :: OSI Approved :: Qt Public License (QPL)', - 'License :: OSI Approved :: Ricoh Source Code Public License', - 'License :: OSI Approved :: Sleepycat License', - 'License :: OSI Approved :: Sun Industry Standards Source License (SISSL)', - 'License :: OSI Approved :: Sun Public License', - 'License :: OSI Approved :: University of Illinois/NCSA Open Source License', - 'License :: OSI Approved :: Vovida Software License 1.0', - 'License :: OSI Approved :: W3C License', - 'License :: OSI Approved :: X.Net License', - 'License :: OSI Approved :: zlib/libpng License', - 'License :: OSI Approved :: Zope Public License', - 'License :: Other/Proprietary License', - 'License :: Public Domain', - 'License :: Repoze Public License', - 'Natural Language :: Afrikaans', - 'Natural Language :: Arabic', - 'Natural Language :: Bengali', - 'Natural Language :: Bosnian', - 'Natural Language :: Bulgarian', - 'Natural Language :: Catalan', - 'Natural Language :: Chinese (Simplified)', - 'Natural Language :: Chinese (Traditional)', - 'Natural Language :: Croatian', - 'Natural Language :: Czech', - 'Natural Language :: Danish', - 'Natural Language :: Dutch', - 'Natural Language :: English', - 'Natural Language :: Esperanto', - 'Natural Language :: Finnish', - 'Natural Language :: French', - 'Natural Language :: German', - 'Natural Language :: Greek', - 'Natural Language :: Hebrew', - 'Natural Language :: Hindi', - 'Natural Language :: Hungarian', - 'Natural Language :: Icelandic', - 'Natural Language :: Indonesian', - 'Natural Language :: Italian', - 'Natural Language :: Japanese', - 'Natural Language :: Javanese', - 'Natural Language :: Korean', - 'Natural Language :: Latin', - 'Natural Language :: Latvian', - 'Natural Language :: Macedonian', - 'Natural Language :: Malay', - 'Natural Language :: Marathi', - 'Natural Language :: Norwegian', - 'Natural Language :: Panjabi', - 'Natural Language :: Persian', - 'Natural Language :: Polish', - 'Natural Language :: Portuguese', - 'Natural Language :: Portuguese (Brazilian)', - 'Natural Language :: Romanian', - 'Natural Language :: Russian', - 'Natural Language :: Serbian', - 'Natural Language :: Slovak', - 'Natural Language :: Slovenian', - 'Natural Language :: Spanish', - 'Natural Language :: Swedish', - 'Natural Language :: Tamil', - 'Natural Language :: Telugu', - 'Natural Language :: Thai', - 'Natural Language :: Turkish', - 'Natural Language :: Ukranian', - 'Natural Language :: Urdu', - 'Natural Language :: Vietnamese', - 'Operating System :: BeOS', - 'Operating System :: MacOS', - 'Operating System :: MacOS :: MacOS 9', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft', - 'Operating System :: Microsoft :: MS-DOS', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: Microsoft :: Windows :: Windows 3.1 or Earlier', - 'Operating System :: Microsoft :: Windows :: Windows 95/98/2000', - 'Operating System :: Microsoft :: Windows :: Windows CE', - 'Operating System :: Microsoft :: Windows :: Windows NT/2000', - 'Operating System :: OS/2', - 'Operating System :: OS Independent', - 'Operating System :: Other OS', - 'Operating System :: PalmOS', - 'Operating System :: PDA Systems', - 'Operating System :: POSIX', - 'Operating System :: POSIX :: AIX', - 'Operating System :: POSIX :: BSD', - 'Operating System :: POSIX :: BSD :: BSD/OS', - 'Operating System :: POSIX :: BSD :: FreeBSD', - 'Operating System :: POSIX :: BSD :: NetBSD', - 'Operating System :: POSIX :: BSD :: OpenBSD', - 'Operating System :: POSIX :: GNU Hurd', - 'Operating System :: POSIX :: HP-UX', - 'Operating System :: POSIX :: IRIX', - 'Operating System :: POSIX :: Linux', - 'Operating System :: POSIX :: Other', - 'Operating System :: POSIX :: SCO', - 'Operating System :: POSIX :: SunOS/Solaris', - 'Operating System :: Unix', - 'Programming Language :: Ada', - 'Programming Language :: APL', - 'Programming Language :: ASP', - 'Programming Language :: Assembly', - 'Programming Language :: Awk', - 'Programming Language :: Basic', - 'Programming Language :: C', - 'Programming Language :: C#', - 'Programming Language :: C++', - 'Programming Language :: Cold Fusion', - 'Programming Language :: Cython', - 'Programming Language :: Delphi/Kylix', - 'Programming Language :: Dylan', - 'Programming Language :: Eiffel', - 'Programming Language :: Emacs-Lisp', - 'Programming Language :: Erlang', - 'Programming Language :: Euler', - 'Programming Language :: Euphoria', - 'Programming Language :: Forth', - 'Programming Language :: Fortran', - 'Programming Language :: Haskell', - 'Programming Language :: Java', - 'Programming Language :: JavaScript', - 'Programming Language :: Lisp', - 'Programming Language :: Logo', - 'Programming Language :: ML', - 'Programming Language :: Modula', - 'Programming Language :: Objective C', - 'Programming Language :: Object Pascal', - 'Programming Language :: OCaml', - 'Programming Language :: Other', - 'Programming Language :: Other Scripting Engines', - 'Programming Language :: Pascal', - 'Programming Language :: Perl', - 'Programming Language :: PHP', - 'Programming Language :: Pike', - 'Programming Language :: Pliant', - 'Programming Language :: PL/SQL', - 'Programming Language :: PROGRESS', - 'Programming Language :: Prolog', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.3', - 'Programming Language :: Python :: 2.4', - 'Programming Language :: Python :: 2.5', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.0', - 'Programming Language :: Python :: 3.1', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: REBOL', - 'Programming Language :: Rexx', - 'Programming Language :: Ruby', - 'Programming Language :: Scheme', - 'Programming Language :: Simula', - 'Programming Language :: Smalltalk', - 'Programming Language :: SQL', - 'Programming Language :: Tcl', - 'Programming Language :: Unix Shell', - 'Programming Language :: Visual Basic', - 'Programming Language :: XBasic', - 'Programming Language :: YACC', - 'Programming Language :: Zope', - 'Topic :: Adaptive Technologies', - 'Topic :: Artistic Software', - 'Topic :: Communications', - 'Topic :: Communications :: BBS', - 'Topic :: Communications :: Chat', - 'Topic :: Communications :: Chat :: AOL Instant Messenger', - 'Topic :: Communications :: Chat :: ICQ', - 'Topic :: Communications :: Chat :: Internet Relay Chat', - 'Topic :: Communications :: Chat :: Unix Talk', - 'Topic :: Communications :: Conferencing', - 'Topic :: Communications :: Email', - 'Topic :: Communications :: Email :: Address Book', - 'Topic :: Communications :: Email :: Email Clients (MUA)', - 'Topic :: Communications :: Email :: Filters', - 'Topic :: Communications :: Email :: Mailing List Servers', - 'Topic :: Communications :: Email :: Mail Transport Agents', - 'Topic :: Communications :: Email :: Post-Office', - 'Topic :: Communications :: Email :: Post-Office :: IMAP', - 'Topic :: Communications :: Email :: Post-Office :: POP3', - 'Topic :: Communications :: Fax', - 'Topic :: Communications :: FIDO', - 'Topic :: Communications :: File Sharing', - 'Topic :: Communications :: File Sharing :: Gnutella', - 'Topic :: Communications :: File Sharing :: Napster', - 'Topic :: Communications :: Ham Radio', - 'Topic :: Communications :: Internet Phone', - 'Topic :: Communications :: Telephony', - 'Topic :: Communications :: Usenet News', - 'Topic :: Database', - 'Topic :: Database :: Database Engines/Servers', - 'Topic :: Database :: Front-Ends', - 'Topic :: Desktop Environment', - 'Topic :: Desktop Environment :: File Managers', - 'Topic :: Desktop Environment :: Gnome', - 'Topic :: Desktop Environment :: GNUstep', - 'Topic :: Desktop Environment :: K Desktop Environment (KDE)', - 'Topic :: Desktop Environment :: K Desktop Environment (KDE) :: Themes', - 'Topic :: Desktop Environment :: PicoGUI', - 'Topic :: Desktop Environment :: PicoGUI :: Applications', - 'Topic :: Desktop Environment :: PicoGUI :: Themes', - 'Topic :: Desktop Environment :: Screen Savers', - 'Topic :: Desktop Environment :: Window Managers', - 'Topic :: Desktop Environment :: Window Managers :: Afterstep', - 'Topic :: Desktop Environment :: Window Managers :: Afterstep :: Themes', - 'Topic :: Desktop Environment :: Window Managers :: Applets', - 'Topic :: Desktop Environment :: Window Managers :: Blackbox', - 'Topic :: Desktop Environment :: Window Managers :: Blackbox :: Themes', - 'Topic :: Desktop Environment :: Window Managers :: CTWM', - 'Topic :: Desktop Environment :: Window Managers :: CTWM :: Themes', - 'Topic :: Desktop Environment :: Window Managers :: Enlightenment', - 'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Epplets', - 'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR15', - 'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR16', - 'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR17', - 'Topic :: Desktop Environment :: Window Managers :: Fluxbox', - 'Topic :: Desktop Environment :: Window Managers :: Fluxbox :: Themes', - 'Topic :: Desktop Environment :: Window Managers :: FVWM', - 'Topic :: Desktop Environment :: Window Managers :: FVWM :: Themes', - 'Topic :: Desktop Environment :: Window Managers :: IceWM', - 'Topic :: Desktop Environment :: Window Managers :: IceWM :: Themes', - 'Topic :: Desktop Environment :: Window Managers :: MetaCity', - 'Topic :: Desktop Environment :: Window Managers :: MetaCity :: Themes', - 'Topic :: Desktop Environment :: Window Managers :: Oroborus', - 'Topic :: Desktop Environment :: Window Managers :: Oroborus :: Themes', - 'Topic :: Desktop Environment :: Window Managers :: Sawfish', - 'Topic :: Desktop Environment :: Window Managers :: Sawfish :: Themes 0.30', - 'Topic :: Desktop Environment :: Window Managers :: Sawfish :: Themes pre-0.30', - 'Topic :: Desktop Environment :: Window Managers :: Waimea', - 'Topic :: Desktop Environment :: Window Managers :: Waimea :: Themes', - 'Topic :: Desktop Environment :: Window Managers :: Window Maker', - 'Topic :: Desktop Environment :: Window Managers :: Window Maker :: Applets', - 'Topic :: Desktop Environment :: Window Managers :: Window Maker :: Themes', - 'Topic :: Desktop Environment :: Window Managers :: XFCE', - 'Topic :: Desktop Environment :: Window Managers :: XFCE :: Themes', - 'Topic :: Documentation', - 'Topic :: Education', - 'Topic :: Education :: Computer Aided Instruction (CAI)', - 'Topic :: Education :: Testing', - 'Topic :: Games/Entertainment', - 'Topic :: Games/Entertainment :: Arcade', - 'Topic :: Games/Entertainment :: Board Games', - 'Topic :: Games/Entertainment :: First Person Shooters', - 'Topic :: Games/Entertainment :: Fortune Cookies', - 'Topic :: Games/Entertainment :: Multi-User Dungeons (MUD)', - 'Topic :: Games/Entertainment :: Puzzle Games', - 'Topic :: Games/Entertainment :: Real Time Strategy', - 'Topic :: Games/Entertainment :: Role-Playing', - 'Topic :: Games/Entertainment :: Side-Scrolling/Arcade Games', - 'Topic :: Games/Entertainment :: Simulation', - 'Topic :: Games/Entertainment :: Turn Based Strategy', - 'Topic :: Home Automation', - 'Topic :: Internet', - 'Topic :: Internet :: File Transfer Protocol (FTP)', - 'Topic :: Internet :: Finger', - 'Topic :: Internet :: Log Analysis', - 'Topic :: Internet :: Name Service (DNS)', - 'Topic :: Internet :: Proxy Servers', - 'Topic :: Internet :: WAP', - 'Topic :: Internet :: WWW/HTTP', - 'Topic :: Internet :: WWW/HTTP :: Browsers', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Message Boards', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: News/Diary', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Page Counters', - 'Topic :: Internet :: WWW/HTTP :: HTTP Servers', - 'Topic :: Internet :: WWW/HTTP :: Indexing/Search', - 'Topic :: Internet :: WWW/HTTP :: Site Management', - 'Topic :: Internet :: WWW/HTTP :: Site Management :: Link Checking', - 'Topic :: Internet :: WWW/HTTP :: WSGI', - 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', - 'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware', - 'Topic :: Internet :: WWW/HTTP :: WSGI :: Server', - 'Topic :: Internet :: Z39.50', - 'Topic :: Multimedia', - 'Topic :: Multimedia :: Graphics', - 'Topic :: Multimedia :: Graphics :: 3D Modeling', - 'Topic :: Multimedia :: Graphics :: 3D Rendering', - 'Topic :: Multimedia :: Graphics :: Capture', - 'Topic :: Multimedia :: Graphics :: Capture :: Digital Camera', - 'Topic :: Multimedia :: Graphics :: Capture :: Scanners', - 'Topic :: Multimedia :: Graphics :: Capture :: Screen Capture', - 'Topic :: Multimedia :: Graphics :: Editors', - 'Topic :: Multimedia :: Graphics :: Editors :: Raster-Based', - 'Topic :: Multimedia :: Graphics :: Editors :: Vector-Based', - 'Topic :: Multimedia :: Graphics :: Graphics Conversion', - 'Topic :: Multimedia :: Graphics :: Presentation', - 'Topic :: Multimedia :: Graphics :: Viewers', - 'Topic :: Multimedia :: Sound/Audio', - 'Topic :: Multimedia :: Sound/Audio :: Analysis', - 'Topic :: Multimedia :: Sound/Audio :: Capture/Recording', - 'Topic :: Multimedia :: Sound/Audio :: CD Audio', - 'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Playing', - 'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Ripping', - 'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Writing', - 'Topic :: Multimedia :: Sound/Audio :: Conversion', - 'Topic :: Multimedia :: Sound/Audio :: Editors', - 'Topic :: Multimedia :: Sound/Audio :: MIDI', - 'Topic :: Multimedia :: Sound/Audio :: Mixers', - 'Topic :: Multimedia :: Sound/Audio :: Players', - 'Topic :: Multimedia :: Sound/Audio :: Players :: MP3', - 'Topic :: Multimedia :: Sound/Audio :: Sound Synthesis', - 'Topic :: Multimedia :: Sound/Audio :: Speech', - 'Topic :: Multimedia :: Video', - 'Topic :: Multimedia :: Video :: Capture', - 'Topic :: Multimedia :: Video :: Conversion', - 'Topic :: Multimedia :: Video :: Display', - 'Topic :: Multimedia :: Video :: Non-Linear Editor', - 'Topic :: Office/Business', - 'Topic :: Office/Business :: Financial', - 'Topic :: Office/Business :: Financial :: Accounting', - 'Topic :: Office/Business :: Financial :: Investment', - 'Topic :: Office/Business :: Financial :: Point-Of-Sale', - 'Topic :: Office/Business :: Financial :: Spreadsheet', - 'Topic :: Office/Business :: Groupware', - 'Topic :: Office/Business :: News/Diary', - 'Topic :: Office/Business :: Office Suites', - 'Topic :: Office/Business :: Scheduling', - 'Topic :: Other/Nonlisted Topic', - 'Topic :: Printing', - 'Topic :: Religion', - 'Topic :: Scientific/Engineering', - 'Topic :: Scientific/Engineering :: Artificial Intelligence', - 'Topic :: Scientific/Engineering :: Astronomy', - 'Topic :: Scientific/Engineering :: Atmospheric Science', - 'Topic :: Scientific/Engineering :: Bio-Informatics', - 'Topic :: Scientific/Engineering :: Chemistry', - 'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)', - 'Topic :: Scientific/Engineering :: GIS', - 'Topic :: Scientific/Engineering :: Human Machine Interfaces', - 'Topic :: Scientific/Engineering :: Image Recognition', - 'Topic :: Scientific/Engineering :: Information Analysis', - 'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator', - 'Topic :: Scientific/Engineering :: Mathematics', - 'Topic :: Scientific/Engineering :: Medical Science Apps.', - 'Topic :: Scientific/Engineering :: Physics', - 'Topic :: Scientific/Engineering :: Visualization', - 'Topic :: Security', - 'Topic :: Security :: Cryptography', - 'Topic :: Sociology', - 'Topic :: Sociology :: Genealogy', - 'Topic :: Sociology :: History', - 'Topic :: Software Development', - 'Topic :: Software Development :: Assemblers', - 'Topic :: Software Development :: Bug Tracking', - 'Topic :: Software Development :: Build Tools', - 'Topic :: Software Development :: Code Generators', - 'Topic :: Software Development :: Compilers', - 'Topic :: Software Development :: Debuggers', - 'Topic :: Software Development :: Disassemblers', - 'Topic :: Software Development :: Documentation', - 'Topic :: Software Development :: Embedded Systems', - 'Topic :: Software Development :: Internationalization', - 'Topic :: Software Development :: Interpreters', - 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Application Frameworks', - 'Topic :: Software Development :: Libraries :: Java Libraries', - 'Topic :: Software Development :: Libraries :: Perl Modules', - 'Topic :: Software Development :: Libraries :: PHP Classes', - 'Topic :: Software Development :: Libraries :: Pike Modules', - 'Topic :: Software Development :: Libraries :: pygame', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'Topic :: Software Development :: Libraries :: Ruby Modules', - 'Topic :: Software Development :: Libraries :: Tcl Extensions', - 'Topic :: Software Development :: Localization', - 'Topic :: Software Development :: Object Brokering', - 'Topic :: Software Development :: Object Brokering :: CORBA', - 'Topic :: Software Development :: Pre-processors', - 'Topic :: Software Development :: Quality Assurance', - 'Topic :: Software Development :: Testing', - 'Topic :: Software Development :: Testing :: Traffic Generation', - 'Topic :: Software Development :: User Interfaces', - 'Topic :: Software Development :: Version Control', - 'Topic :: Software Development :: Version Control :: CVS', - 'Topic :: Software Development :: Version Control :: RCS', - 'Topic :: Software Development :: Version Control :: SCCS', - 'Topic :: Software Development :: Widget Sets', - 'Topic :: System', - 'Topic :: System :: Archiving', - 'Topic :: System :: Archiving :: Backup', - 'Topic :: System :: Archiving :: Compression', - 'Topic :: System :: Archiving :: Mirroring', - 'Topic :: System :: Archiving :: Packaging', - 'Topic :: System :: Benchmark', - 'Topic :: System :: Boot', - 'Topic :: System :: Boot :: Init', - 'Topic :: System :: Clustering', - 'Topic :: System :: Console Fonts', - 'Topic :: System :: Distributed Computing', - 'Topic :: System :: Emulators', - 'Topic :: System :: Filesystems', - 'Topic :: System :: Hardware', - 'Topic :: System :: Hardware :: Hardware Drivers', - 'Topic :: System :: Hardware :: Mainframes', - 'Topic :: System :: Hardware :: Symmetric Multi-processing', - 'Topic :: System :: Installation/Setup', - 'Topic :: System :: Logging', - 'Topic :: System :: Monitoring', - 'Topic :: System :: Networking', - 'Topic :: System :: Networking :: Firewalls', - 'Topic :: System :: Networking :: Monitoring', - 'Topic :: System :: Networking :: Monitoring :: Hardware Watchdog', - 'Topic :: System :: Networking :: Time Synchronization', - 'Topic :: System :: Operating System', - 'Topic :: System :: Operating System Kernels', - 'Topic :: System :: Operating System Kernels :: BSD', - 'Topic :: System :: Operating System Kernels :: GNU Hurd', - 'Topic :: System :: Operating System Kernels :: Linux', - 'Topic :: System :: Power (UPS)', - 'Topic :: System :: Recovery Tools', - 'Topic :: System :: Shells', - 'Topic :: System :: Software Distribution', - 'Topic :: System :: Systems Administration', - 'Topic :: System :: Systems Administration :: Authentication/Directory', - 'Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP', - 'Topic :: System :: Systems Administration :: Authentication/Directory :: NIS', - 'Topic :: System :: System Shells', - 'Topic :: Terminals', - 'Topic :: Terminals :: Serial', - 'Topic :: Terminals :: Telnet', - 'Topic :: Terminals :: Terminal Emulators/X Terminals', - 'Topic :: Text Editors', - 'Topic :: Text Editors :: Documentation', - 'Topic :: Text Editors :: Emacs', - 'Topic :: Text Editors :: Integrated Development Environments (IDE)', - 'Topic :: Text Editors :: Text Processing', - 'Topic :: Text Editors :: Word Processors', - 'Topic :: Text Processing', - 'Topic :: Text Processing :: Filters', - 'Topic :: Text Processing :: Fonts', - 'Topic :: Text Processing :: General', - 'Topic :: Text Processing :: Indexing', - 'Topic :: Text Processing :: Linguistic', - 'Topic :: Text Processing :: Markup', - 'Topic :: Text Processing :: Markup :: HTML', - 'Topic :: Text Processing :: Markup :: LaTeX', - 'Topic :: Text Processing :: Markup :: SGML', - 'Topic :: Text Processing :: Markup :: VRML', - 'Topic :: Text Processing :: Markup :: XML', - 'Topic :: Utilities', - ] +# XXX everything needs docstrings and tests (both low-level tests of various +# methods and functional tests of running the script) -def askYn(question, default = None, helptext = None): + +def ask_yn(question, default=None, helptext=None): while True: - answer = ask(question, default, helptext, required = True) + answer = ask(question, default, helptext, required=True) if answer and answer[0].lower() in 'yn': - return(answer[0].lower()) + return answer[0].lower() print '\nERROR: You must select "Y" or "N".\n' @@ -659,7 +111,7 @@ print '=' * 70 continue if default and not line: - return(default) + return default if not line and required: print '*' * 70 print 'This value cannot be empty.' @@ -668,92 +120,104 @@ print helptext print '*' * 70 continue - return(line) + return line -def buildTroveDict(troveList): - dict = {} - for key in troveList: - subDict = dict +def _build_classifiers_dict(classifiers): + d = {} + for key in classifiers: + subDict = d for subkey in key.split(' :: '): if not subkey in subDict: subDict[subkey] = {} subDict = subDict[subkey] - return(dict) -troveDict = buildTroveDict(troveList) + return d +CLASSIFIERS = _build_classifiers_dict(_CLASSIFIERS_LIST) -class SetupClass(object): + +class MainProgram(object): def __init__(self): - self.config = None - self.classifierDict = {} - self.setupData = {} - self.setupData['classifier'] = self.classifierDict - self.setupData['packages'] = {} + self.configparser = None + self.classifiers = {} + self.data = {} + self.data['classifier'] = self.classifiers + self.data['packages'] = {} + self.load_config_file() - self.loadConfigFile() + def lookup_option(self, key): + if not self.configparser.has_option('DEFAULT', key): + return None + return self.configparser.get('DEFAULT', key) + def load_config_file(self): + self.configparser = RawConfigParser() + # TODO replace with section in distutils config file + self.configparser.read(os.path.expanduser('~/.mkpkgpy')) + self.data['author'] = self.lookup_option('author') + self.data['author_email'] = self.lookup_option('author_email') - def lookupOption(self, key): - if not self.config.has_option('DEFAULT', key): - return(None) - return(self.config.get('DEFAULT', key)) - - - def loadConfigFile(self): - self.config = ConfigParser.RawConfigParser() - self.config.read(os.path.expanduser('~/.mkpkgpy')) - self.setupData['author'] = self.lookupOption('author') - self.setupData['author_email'] = self.lookupOption('author_email') - - - def updateConfigFile(self): + def update_config_file(self): valuesDifferent = False + # FIXME looking only for those two fields seems wrong for compareKey in ('author', 'author_email'): - if self.lookupOption(compareKey) != self.setupData[compareKey]: + if self.lookup_option(compareKey) != self.data[compareKey]: valuesDifferent = True - self.config.set('DEFAULT', compareKey, - self.setupData[compareKey]) + self.configparser.set('DEFAULT', compareKey, + self.data[compareKey]) if not valuesDifferent: return - self.config.write(open(os.path.expanduser('~/.pygiver'), 'w')) + fp = open(os.path.expanduser('~/.mkpkgpy'), 'w') + try: + self.configparser.write(fp) + finally: + fp.close() + def load_existing_setup_script(self): + raise NotImplementedError + # Ideas: + # - define a mock module to assign to sys.modules['distutils'] before + # importing the setup script as a module (or executing it); it would + # provide setup (a function that just returns its args as a dict), + # Extension (ditto), find_packages (the real function) + # - we could even mock Distribution and commands to handle more setup + # scripts + # - we could use a sandbox (http://bugs.python.org/issue8680) + # - the cleanest way is to parse the file, not import it, but there is + # no way to do that across versions (the compiler package is + # deprecated or removed in recent Pythons, the ast module is not + # present before 2.6) - def loadExistingSetup(self): - raise NotImplementedError - - - def inspectFile(self, path): + def inspect_file(self, path): fp = open(path, 'r') try: - for line in [fp.readline() for x in range(10)]: + for line in [fp.readline() for _ in range(10)]: m = re.match(r'^#!.*python((?P\d)(\.\d+)?)?$', line) if m: if m.group('major') == '3': - self.classifierDict['Programming Language :: Python :: 3'] = 1 + self.classifiers['Programming Language :: Python :: 3'] = 1 else: - self.classifierDict['Programming Language :: Python :: 2'] = 1 + self.classifiers['Programming Language :: Python :: 2'] = 1 finally: fp.close() - - def inspectDirectory(self): + def inspect_directory(self): dirName = os.path.basename(os.getcwd()) - self.setupData['name'] = dirName + self.data['name'] = dirName m = re.match(r'(.*)-(\d.+)', dirName) if m: - self.setupData['name'] = m.group(1) - self.setupData['version'] = m.group(2) + self.data['name'] = m.group(1) + self.data['version'] = m.group(2) for root, dirs, files in os.walk(os.curdir): - for file in files: - if root == os.curdir and file == 'setup.py': continue - fileName = os.path.join(root, file) - self.inspectFile(fileName) + for filename in files: + if root == os.curdir and filename == 'setup.py': + continue + self.inspect_file(os.path.join(root, filename)) - if file == '__init__.py': + if filename == '__init__.py': trySrc = os.path.join(os.curdir, 'src') tmpRoot = root if tmpRoot.startswith(trySrc): @@ -761,75 +225,69 @@ if tmpRoot.startswith(os.path.sep): tmpRoot = tmpRoot[len(os.path.sep):] - self.setupData['packages'][tmpRoot] = root[1 + len(os.path.sep):] + self.data['packages'][tmpRoot] = root[1 + len(os.path.sep):] + def query_user(self): + self.data['name'] = ask('Package name', self.data['name'], + _helptext['name']) + self.data['version'] = ask('Current version number', + self.data.get('version'), _helptext['version']) + self.data['description'] = ask('Package description', + self.data.get('description'), _helptext['description'], + lengthy=True) + self.data['author'] = ask('Author name', + self.data.get('author'), _helptext['author']) + self.data['author_email'] = ask('Author e-mail address', + self.data.get('author_email'), _helptext['author_email']) + self.data['url'] = ask('Project URL', + self.data.get('url'), _helptext['url'], required=False) - def queryUser(self): - self.setupData['name'] = ask('Package name', self.setupData['name'], - helpText['name']) - self.setupData['version'] = ask('Current version number', - self.setupData.get('version'), helpText['version']) - self.setupData['description'] = ask('Package description', - self.setupData.get('description'), helpText['description'], - lengthy=True) - self.setupData['author'] = ask('Author name', - self.setupData.get('author'), helpText['author']) - self.setupData['author_email'] = ask('Author e-mail address', - self.setupData.get('author_email'), helpText['author_email']) - self.setupData['url'] = ask('Project URL', - self.setupData.get('url'), helpText['url'], required=False) + if ask_yn('Do you want to set Trove classifiers?', + helptext=_helptext['do_classifier']) == 'y': + self.set_classifier() - if (askYn('Do you want to set Trove classifiers?', - helptext=helpText['do_classifier']) == 'y'): - self.setTroveClassifier() + def set_classifier(self): + self.set_devel_status(self.classifiers) + self.set_license(self.classifiers) + self.set_other_classifier(self.classifiers) + def set_other_classifier(self, classifiers): + if ask_yn('Do you want to set other trove identifiers', 'n', + _helptext['trove_generic']) != 'y': + return + self.walk_classifiers(classifiers, [CLASSIFIERS], '') - def setTroveClassifier(self): - self.setTroveDevStatus(self.classifierDict) - self.setTroveLicense(self.classifierDict) - self.setTroveOther(self.classifierDict) - - - def setTroveOther(self, classifierDict): - if askYn('Do you want to set other trove identifiers', - 'n', - helpText['trove_generic']) != 'y': - return - - self.walkTrove(classifierDict, [troveDict], '') - - def walkTrove(self, classifierDict, trovePath, desc): - trove = trovePath[-1] + def walk_classifiers(self, classifiers, trovepath, desc): + trove = trovepath[-1] if not trove: return for key in sorted(trove.keys()): if len(trove[key]) == 0: - if askYn('Add "%s"' % desc[4:] + ' :: ' + key, 'n') == 'y': - classifierDict[desc[4:] + ' :: ' + key] = 1 + if ask_yn('Add "%s"' % desc[4:] + ' :: ' + key, 'n') == 'y': + classifiers[desc[4:] + ' :: ' + key] = 1 continue - if askYn('Do you want to set items under\n "%s" (%d sub-items)' - % (key, len(trove[key])), 'n', - helpText['trove_generic']) == 'y': - self.walkTrove(classifierDict, trovePath + [trove[key]], - desc + ' :: ' + key) + if ask_yn('Do you want to set items under\n "%s" (%d sub-items)' + % (key, len(trove[key])), 'n', + _helptext['trove_generic']) == 'y': + self.walk_classifiers(classifiers, trovepath + [trove[key]], + desc + ' :: ' + key) - - def setTroveLicense(self, classifierDict): + def set_license(self, classifiers): while True: license = ask('What license do you use', - helptext=helpText['trove_license'], - required=False) + helptext=_helptext['trove_license'], required=False) if not license: return licenseWords = license.lower().split(' ') foundList = [] - for index in range(len(troveList)): - troveItem = troveList[index] + # TODO use enumerate + for index in range(len(_CLASSIFIERS_LIST)): + troveItem = _CLASSIFIERS_LIST[index] if not troveItem.startswith('License :: '): continue troveItem = troveItem[11:].lower() @@ -843,54 +301,55 @@ foundList.append(index) question = 'Matching licenses:\n\n' + # TODO use enumerate? for i in xrange(1, len(foundList) + 1): - question += ' %s) %s\n' % (i, troveList[foundList[i - 1]]) + question += ' %s) %s\n' % (i, _CLASSIFIERS_LIST[foundList[i - 1]]) question += ('\nType the number of the license you wish to use or ' - '? to try again:') + '? to try again:') troveLicense = ask(question, required=False) if troveLicense == '?': continue if troveLicense == '': return + # FIXME the int conversion can fail foundIndex = foundList[int(troveLicense) - 1] - classifierDict[troveList[foundIndex]] = 1 + classifiers[_CLASSIFIERS_LIST[foundIndex]] = 1 try: return except IndexError: - print("ERROR: Invalid selection, type a number from the list " - "above.") + print ("ERROR: Invalid selection, type a number from the list " + "above.") + def set_devel_status(self, classifiers): + while True: + choice = ask(dedent('''\ + Please select the project status: - def setTroveDevStatus(self, classifierDict): - while True: - devStatus = ask('''Please select the project status: + 1 - Planning + 2 - Pre-Alpha + 3 - Alpha + 4 - Beta + 5 - Production/Stable + 6 - Mature + 7 - Inactive -1 - Planning -2 - Pre-Alpha -3 - Alpha -4 - Beta -5 - Production/Stable -6 - Mature -7 - Inactive - -Status''', required=False) - if devStatus: + Status'''), required=False) + if choice: try: - key = { - '1' : 'Development Status :: 1 - Planning', - '2' : 'Development Status :: 2 - Pre-Alpha', - '3' : 'Development Status :: 3 - Alpha', - '4' : 'Development Status :: 4 - Beta', - '5' : 'Development Status :: 5 - Production/Stable', - '6' : 'Development Status :: 6 - Mature', - '7' : 'Development Status :: 7 - Inactive', - }[devStatus] - classifierDict[key] = 1 + choice = int(choice) - 1 + key = ['Development Status :: 1 - Planning', + 'Development Status :: 2 - Pre-Alpha', + 'Development Status :: 3 - Alpha', + 'Development Status :: 4 - Beta', + 'Development Status :: 5 - Production/Stable', + 'Development Status :: 6 - Mature', + 'Development Status :: 7 - Inactive'][choice] + classifiers[key] = 1 return - except KeyError: - print("ERROR: Invalid selection, type a single digit " - "number.") + except (IndexError, ValueError): + print ("ERROR: Invalid selection, type a single digit " + "number.") def _dotted_packages(self, data): packages = sorted(data.keys()) @@ -901,33 +360,38 @@ modified_pkgs.append(pkg) return modified_pkgs - def writeSetup(self): + def write_setup_script(self): if os.path.exists('setup.py'): + if os.path.exists('setup.py.old'): + print ("ERROR: setup.py.old backup exists, please check that " + "current setup.py is correct and remove setup.py.old") + return shutil.move('setup.py', 'setup.py.old') fp = open('setup.py', 'w') try: + # XXX do LFs work on all platforms? fp.write('#!/usr/bin/env python\n\n') fp.write('from distutils2.core import setup\n\n') - fp.write('setup(name=%s,\n' % repr(self.setupData['name'])) - fp.write(' version=%s,\n' % repr(self.setupData['version'])) + fp.write('setup(name=%s,\n' % repr(self.data['name'])) + fp.write(' version=%s,\n' % repr(self.data['version'])) fp.write(' description=%s,\n' - % repr(self.setupData['description'])) - fp.write(' author=%s,\n' % repr(self.setupData['author'])) + % repr(self.data['description'])) + fp.write(' author=%s,\n' % repr(self.data['author'])) fp.write(' author_email=%s,\n' - % repr(self.setupData['author_email'])) - if self.setupData['url']: - fp.write(' url=%s,\n' % repr(self.setupData['url'])) - if self.setupData['classifier']: + % repr(self.data['author_email'])) + if self.data['url']: + fp.write(' url=%s,\n' % repr(self.data['url'])) + if self.data['classifier']: fp.write(' classifier=[\n') - for classifier in sorted(self.setupData['classifier'].keys()): + for classifier in sorted(self.data['classifier'].keys()): fp.write(' %s,\n' % repr(classifier)) fp.write(' ],\n') - if self.setupData['packages']: + if self.data['packages']: fp.write(' packages=%s,\n' - % repr(self._dotted_packages(self.setupData['packages']))) + % repr(self._dotted_packages(self.data['packages']))) fp.write(' package_dir=%s,\n' - % repr(self.setupData['packages'])) + % repr(self.data['packages'])) fp.write(' #scripts=[\'path/to/script\']\n') fp.write(' )\n') @@ -939,11 +403,14 @@ def main(): - setup = SetupClass() - setup.inspectDirectory() - setup.queryUser() - setup.updateConfigFile() - setup.writeSetup() + """Main entry point.""" + program = MainProgram() + # uncomment when implemented + #program.load_existing_setup_script() + program.inspect_directory() + program.query_user() + program.update_config_file() + program.write_setup() if __name__ == '__main__': diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -152,7 +152,11 @@ self.assertIn('Version', metadata.keys()) self.assertIn('0.5', metadata.values()) self.assertIn(('Version', '0.5'), metadata.items()) - #TODO test update + + metadata.update({'version': '0.6'}) + self.assertEqual(metadata['Version'], '0.6') + metadata.update([('version', '0.7')]) + self.assertEqual(metadata['Version'], '0.7') def test_versions(self): metadata = DistributionMetadata() diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -18,7 +18,7 @@ _find_exe_version, _MAC_OS_X_LD_VERSION, byte_compile, find_packages, spawn, find_executable, _nt_quote_args, get_pypirc_path, generate_pypirc, - read_pypirc, resolve_dotted_name) + read_pypirc, resolve_name) from distutils2 import util from distutils2.tests import support @@ -342,14 +342,14 @@ res = find_packages([root], ['pkg1.pkg2']) self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6'])) - def test_resolve_dotted_name(self): - self.assertEqual(UtilTestCase, resolve_dotted_name("distutils2.tests.test_util.UtilTestCase")) - self.assertEqual(UtilTestCase.test_resolve_dotted_name, - resolve_dotted_name("distutils2.tests.test_util.UtilTestCase.test_resolve_dotted_name")) + def test_resolve_name(self): + self.assertEqual(UtilTestCase, resolve_name("distutils2.tests.test_util.UtilTestCase")) + self.assertEqual(UtilTestCase.test_resolve_name, + resolve_name("distutils2.tests.test_util.UtilTestCase.test_resolve_name")) - self.assertRaises(ImportError, resolve_dotted_name, + self.assertRaises(ImportError, resolve_name, "distutils2.tests.test_util.UtilTestCaseNot") - self.assertRaises(ImportError, resolve_dotted_name, + self.assertRaises(ImportError, resolve_name, "distutils2.tests.test_util.UtilTestCase.nonexistent_attribute") def test_import_nested_first_time(self): @@ -361,12 +361,11 @@ try: sys.path.append(tmp_dir) - resolve_dotted_name("a.b.c.Foo") + resolve_name("a.b.c.Foo") # assert nothing raised finally: sys.path.remove(tmp_dir) - @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_run_2to3_on_code(self): content = "print 'test'" diff --git a/src/distutils2/util.py b/src/distutils2/util.py --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -636,8 +636,8 @@ packages.append(package_name) return packages -def resolve_dotted_name(name): - """Resolves the name and returns the corresponding object.""" +def resolve_name(name): + """Resolve a name like ``module.object`` to an object and return it.""" parts = name.split('.') cursor = len(parts) module_name, rest = parts[:cursor], parts[cursor:] @@ -659,6 +659,7 @@ except AttributeError: raise ImportError return ret + # utility functions for 2to3 support def run_2to3(files, doctests_only=False, fixer_names=None, options=None, -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: refactor Message-ID: tarek.ziade pushed 14a5f0aa0634 to distutils2: http://hg.python.org/distutils2/rev/14a5f0aa0634 changeset: 616:14a5f0aa0634 user: Konrad Delong date: Thu Aug 12 20:41:12 2010 +0200 summary: refactor files: src/distutils2/command/test.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -1,7 +1,7 @@ import os, sys from distutils2.core import Command from distutils2._backport.pkgutil import get_distribution -from distutils2.util import resolve_dotted_name +from distutils2.util import resolve_name import unittest import warnings @@ -36,11 +36,11 @@ self.run_command('build') os.chdir(self.build_lib) args = {"module": self.test_suite, - "argv": sys.argv[:1], - "testLoader": resolve_dotted_name(self.test_loader) + "argv": sys.argv[:1] } - if args['testLoader'] is None: - del args['testLoader'] + loader_instance = resolve_name(self.test_loader) + if loader_instance is not None: + args['testLoader'] = loader_instance unittest.main(**args) finally: os.chdir(prev_cwd) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: made the test_test pass again + added test boilerplate Message-ID: tarek.ziade pushed 4b87947839a9 to distutils2: http://hg.python.org/distutils2/rev/4b87947839a9 changeset: 617:4b87947839a9 user: Konrad Delong date: Thu Aug 12 20:49:01 2010 +0200 summary: made the test_test pass again + added test boilerplate files: src/distutils2/command/test.py, src/distutils2/tests/test_test.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -38,9 +38,10 @@ args = {"module": self.test_suite, "argv": sys.argv[:1] } - loader_instance = resolve_name(self.test_loader) - if loader_instance is not None: - args['testLoader'] = loader_instance + if self.test_loader: + loader_class = resolve_name(self.test_loader) + if loader_class is not None: + args['testLoader'] = loader_class() unittest.main(**args) finally: os.chdir(prev_cwd) diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -104,3 +104,6 @@ def test_suite(): return unittest.makeSuite(TestTest) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: changed semantics of test_suite to actually mean test_suite Message-ID: tarek.ziade pushed d7ebb655e75e to distutils2: http://hg.python.org/distutils2/rev/d7ebb655e75e changeset: 618:d7ebb655e75e user: Konrad Delong date: Fri Aug 13 12:24:31 2010 +0200 summary: changed semantics of test_suite to actually mean test_suite files: src/distutils2/command/test.py, src/distutils2/tests/dists/extensions_test/myowntestmodule.py, src/distutils2/tests/dists/extensions_test/setup.cfg, src/distutils2/tests/dists/simple_test/myowntestmodule.py, src/distutils2/tests/dists/simple_test/setup.cfg diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -24,7 +24,7 @@ if self.distribution.tests_require: for requirement in self.distribution.tests_require: if get_distribution(requirement) is None: - warnings.warn("The test dependency %s is not installed which may couse the tests to fail.", + warnings.warn("The test dependency %s is not installed which may couse the tests to fail." % requirement, RuntimeWarning) def run(self): @@ -35,13 +35,14 @@ build.inplace = 1 # TODO - remove make sure it's needed self.run_command('build') os.chdir(self.build_lib) - args = {"module": self.test_suite, - "argv": sys.argv[:1] - } + args = {} if self.test_loader: loader_class = resolve_name(self.test_loader) - if loader_class is not None: - args['testLoader'] = loader_class() - unittest.main(**args) + args['testLoader'] = loader_class() + if self.test_suite: + argv = [unittest.__file__, '--verbose', self.test_suite] + else: + argv = [unittest.__file__, '--verbose'] + unittest.main(None, None, argv, **args) finally: os.chdir(prev_cwd) diff --git a/src/distutils2/tests/dists/extensions_test/myowntestmodule.py b/src/distutils2/tests/dists/extensions_test/myowntestmodule.py --- a/src/distutils2/tests/dists/extensions_test/myowntestmodule.py +++ b/src/distutils2/tests/dists/extensions_test/myowntestmodule.py @@ -2,3 +2,5 @@ class SomeTest(unittest.TestCase): def test_blah(self): self.fail("horribly") +def test_suite(): + return unittest.makeSuite(SomeTest) diff --git a/src/distutils2/tests/dists/extensions_test/setup.cfg b/src/distutils2/tests/dists/extensions_test/setup.cfg --- a/src/distutils2/tests/dists/extensions_test/setup.cfg +++ b/src/distutils2/tests/dists/extensions_test/setup.cfg @@ -1,2 +1,2 @@ [test] -test-suite = myowntestmodule +test-suite = myowntestmodule.test_suite diff --git a/src/distutils2/tests/dists/simple_test/myowntestmodule.py b/src/distutils2/tests/dists/simple_test/myowntestmodule.py --- a/src/distutils2/tests/dists/simple_test/myowntestmodule.py +++ b/src/distutils2/tests/dists/simple_test/myowntestmodule.py @@ -2,3 +2,5 @@ class SomeTest(unittest.TestCase): def test_blah(self): self.fail("horribly") +def test_suite(): + return unittest.makeSuite(SomeTest) diff --git a/src/distutils2/tests/dists/simple_test/setup.cfg b/src/distutils2/tests/dists/simple_test/setup.cfg --- a/src/distutils2/tests/dists/simple_test/setup.cfg +++ b/src/distutils2/tests/dists/simple_test/setup.cfg @@ -1,2 +1,2 @@ [test] -test-suite = myowntestmodule +test-suite = myowntestmodule.test_suite -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: updated docs Message-ID: tarek.ziade pushed 4d66dae7c4ae to distutils2: http://hg.python.org/distutils2/rev/4d66dae7c4ae changeset: 619:4d66dae7c4ae user: Konrad Delong date: Fri Aug 13 16:51:43 2010 +0200 summary: updated docs files: docs/source/distutils/newcommands.rst diff --git a/docs/source/distutils/newcommands.rst b/docs/source/distutils/newcommands.rst --- a/docs/source/distutils/newcommands.rst +++ b/docs/source/distutils/newcommands.rst @@ -12,47 +12,27 @@ When doing test-driven development, or running automated builds that need testing before they are deployed for downloading or use, it's often useful to be able to run a project's unit tests without actually deploying the project -anywhere, even using the ``develop`` command. The ``test`` command runs a +anywhere. The ``test`` command runs project's unit tests without actually deploying it, by temporarily putting the -project's source on ``sys.path``, after first running ``build_ext -i`` and -``egg_info`` to ensure that any C extensions and project metadata are -up-to-date. +project's source on ``sys.path``, after first running ``build_ext -i`` +to ensure that any C extensions are built. -To use this command, your project's tests must be wrapped in a ``unittest`` -test suite by either a function, a ``TestCase`` class or method, or a module -or package containing ``TestCase`` classes. If the named suite is a module, -and the module has an ``additional_tests()`` function, it is called and the -result (which must be a ``unittest.TestSuite``) is added to the tests to be -run. If the named suite is a package, any submodules and subpackages are -recursively added to the overall test suite. (Note: if your project specifies -a ``test_loader``, the rules for processing the chosen ``test_suite`` may -differ; see the `test_loader`_ documentation for more details.) +You can use this command in one of two ways: either by specifying a +unittest-compatible test suite for your project (or any callable that returns +it) or by passing a test runner function that will run your tests and display +results in the console. Both options: ``suite`` and ``runner`` accept dotted +names that will be resolved into actual objects. -Note that many test systems including ``doctest`` support wrapping their -non-``unittest`` tests in ``TestSuite`` objects. So, if you are using a test -package that does not support this, we suggest you encourage its developers to -implement test suite support, as this is a convenient and standard way to -aggregate a collection of tests to be run under a common test harness. - -By default, tests will be run in the "verbose" mode of the ``unittest`` -package's text test runner, but you can get the "quiet" mode (just dots) if -you supply the ``-q`` or ``--quiet`` option, either as a global option to -the setup script (for example ``setup.py -q test``) or as an option for the ``test`` -command itself (for example ``setup.py test -q``). There is one other option -available: - -``--test-suite=NAME, -s NAME`` +``--suite=NAME, -s NAME`` Specify the test suite (or module, class, or method) to be run (for example ``some_module.test_suite``). The default for this option can be - set by giving a ``test_suite`` argument to the ``setup()`` function, for example:: + set by in your ``setup.cfg`` file. - setup( - # ... - test_suite = "my_package.tests.test_all" - ) + [test] + suite = my_package.tests.test_all - If you did not set a ``test_suite`` in your ``setup()`` call, and do not - provide a ``--test-suite`` option, an error will occur. +``--runner=NAME, -r NAME`` + Specify the test runner to be called. ``upload`` - Upload source and/or binary distributions to PyPI -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: dropped test_loader and added test_runner, renamed test_suite Message-ID: tarek.ziade pushed b81715424b1c to distutils2: http://hg.python.org/distutils2/rev/b81715424b1c changeset: 620:b81715424b1c user: Konrad Delong date: Fri Aug 13 17:31:20 2010 +0200 summary: dropped test_loader and added test_runner, renamed test_suite files: src/distutils2/command/test.py, src/distutils2/tests/dists/custom_loader/myowntestmodule.py, src/distutils2/tests/dists/custom_loader/setup.cfg, src/distutils2/tests/dists/custom_loader/setup.py, src/distutils2/tests/dists/extensions_test/setup.cfg, src/distutils2/tests/dists/simple_test/setup.cfg, src/distutils2/tests/test_test.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -1,4 +1,5 @@ import os, sys +from distutils2 import log from distutils2.core import Command from distutils2._backport.pkgutil import get_distribution from distutils2.util import resolve_name @@ -8,16 +9,17 @@ class test(Command): description = "run the distribution's test suite" + user_options = [ - ('test-suite=', 's', + ('suite=', 's', "Test suite to run (e.g. 'some_module.test_suite')"), - ('test-loader=', None, - "Test loader to be used to load the test suite."), + ('runner=', None, + "Test runner to be called."), ] def initialize_options(self): - self.test_suite = None - self.test_loader = None + self.suite = None + self.runner = None def finalize_options(self): self.build_lib = self.get_finalized_command("build").build_lib @@ -26,23 +28,23 @@ if get_distribution(requirement) is None: warnings.warn("The test dependency %s is not installed which may couse the tests to fail." % requirement, RuntimeWarning) + if not self.suite and not self.runner: + self.announce("Either 'suite' or 'runner' option must be specified", log.ERROR) def run(self): prev_cwd = os.getcwd() try: + # build distribution if needed if self.distribution.has_ext_modules(): build = self.get_reinitialized_command('build') - build.inplace = 1 # TODO - remove make sure it's needed + build.inplace = 1 self.run_command('build') os.chdir(self.build_lib) - args = {} - if self.test_loader: - loader_class = resolve_name(self.test_loader) - args['testLoader'] = loader_class() - if self.test_suite: - argv = [unittest.__file__, '--verbose', self.test_suite] - else: - argv = [unittest.__file__, '--verbose'] - unittest.main(None, None, argv, **args) + + # run the tests + if self.runner: + resolve_name(self.runner)() + elif self.suite: + unittest.main(None, None, [unittest.__file__, '--verbose', self.suite]) finally: os.chdir(prev_cwd) diff --git a/src/distutils2/tests/dists/custom_loader/myowntestmodule.py b/src/distutils2/tests/dists/custom_loader/myowntestmodule.py deleted file mode 100644 --- a/src/distutils2/tests/dists/custom_loader/myowntestmodule.py +++ /dev/null @@ -1,9 +0,0 @@ -import unittest -class Loader(object): - def loadTestsFromModule(self, names, module=None): - class SomeTest(unittest.TestCase): - def test_blah(self): - self.fail("horribly") - return unittest.makeSuite(SomeTest) - def __repr__(self): - return 'YO' diff --git a/src/distutils2/tests/dists/custom_loader/setup.cfg b/src/distutils2/tests/dists/custom_loader/setup.cfg deleted file mode 100644 --- a/src/distutils2/tests/dists/custom_loader/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[test] -test-loader = myowntestmodule.Loader diff --git a/src/distutils2/tests/dists/custom_loader/setup.py b/src/distutils2/tests/dists/custom_loader/setup.py deleted file mode 100644 --- a/src/distutils2/tests/dists/custom_loader/setup.py +++ /dev/null @@ -1,5 +0,0 @@ -from distutils2.core import setup -setup(name='somedist', - version='0.1', - py_modules=['myowntestmodule', 'somemod'], -) diff --git a/src/distutils2/tests/dists/extensions_test/setup.cfg b/src/distutils2/tests/dists/extensions_test/setup.cfg --- a/src/distutils2/tests/dists/extensions_test/setup.cfg +++ b/src/distutils2/tests/dists/extensions_test/setup.cfg @@ -1,2 +1,2 @@ [test] -test-suite = myowntestmodule.test_suite +suite = myowntestmodule.test_suite diff --git a/src/distutils2/tests/dists/simple_test/setup.cfg b/src/distutils2/tests/dists/simple_test/setup.cfg --- a/src/distutils2/tests/dists/simple_test/setup.cfg +++ b/src/distutils2/tests/dists/simple_test/setup.cfg @@ -1,2 +1,2 @@ [test] -test-suite = myowntestmodule.test_suite +suite = myowntestmodule.test_suite diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -7,7 +7,7 @@ from copy import copy from os.path import join from StringIO import StringIO -from distutils2.tests.support import unittest, TempdirManager +from distutils2.tests.support import unittest, LoggingSilencer, TempdirManager from distutils2.command.test import test from distutils2.dist import Distribution @@ -27,7 +27,9 @@ here = os.path.dirname(os.path.abspath(__file__)) -class TestTest(TempdirManager, unittest.TestCase): +class TestTest(TempdirManager, + #LoggingSilencer, + unittest.TestCase): def setUp(self): super(TestTest, self).setUp() @@ -80,11 +82,6 @@ self.assertTrue(os.path.exists(join(self.pkg_dir, 'build'))) self.assertTrue(any(x.startswith('lib') for x in os.listdir(join(self.pkg_dir, 'build')))) - def test_custom_test_loader(self): - self.pkg_dir = self.prepare_dist("custom_loader") - output = self.run_with_dist_cwd(self.pkg_dir) - self.assert_re_match(EXPECTED_OUTPUT_RE, output) - def _test_works_with_2to3(self): pass @@ -102,6 +99,25 @@ examine_warnings(examinator) + def test_custom_runner(self): + dist = Distribution() + cmd = test(dist) + + tmp_dir = self.mkdtemp() + try: + sys.path.append(tmp_dir) + self.write_file(os.path.join(tmp_dir, 'distutils2_tests_a.py'), '') + import distutils2_tests_a as a_module + a_module.recorder = lambda: record.append("runner called") + record = [] + cmd.runner = "distutils2_tests_a.recorder" + cmd.ensure_finalized() + cmd.run() + self.assertEqual(["runner called"], record) + finally: + sys.path.remove(tmp_dir) + + def test_suite(): return unittest.makeSuite(TestTest) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: merged test improvments Message-ID: tarek.ziade pushed 6daaf3e7e7d4 to distutils2: http://hg.python.org/distutils2/rev/6daaf3e7e7d4 changeset: 610:6daaf3e7e7d4 parent: 609:bc408018ca27 parent: 551:6eea7e7a79da user: Konrad Delong date: Thu Aug 12 19:39:59 2010 +0200 summary: merged test improvments files: docs/source/command_hooks.rst, docs/source/commands.rst, docs/source/depgraph.rst, docs/source/distutils/newcommands.rst, docs/source/metadata.rst, docs/source/pkgutil.rst, docs/source/projects-index.client.rst, docs/source/projects-index.dist.rst, docs/source/projects-index.rst, docs/source/projects-index.simple.rst, docs/source/projects-index.xmlrpc.rst, docs/source/test_framework.rst, docs/source/version.rst, src/distutils2/command/upload_docs.py, src/distutils2/dist.py, src/distutils2/tests/test_upload_docs.py diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -3,8 +3,9 @@ __pycache__/ *.so configure.cache +MANIFEST build/ -MANIFEST dist/ +_static/ *.swp .coverage diff --git a/docs/design/pep-0376.txt b/docs/design/pep-0376.txt --- a/docs/design/pep-0376.txt +++ b/docs/design/pep-0376.txt @@ -415,7 +415,7 @@ A new class called ``Distribution`` is created with the path of the `.egg-info` directory provided to the constructor. It reads the metadata -contained in `PKG-INFO` when it is instanciated. +contained in `PKG-INFO` when it is instantiated. ``Distribution(path)`` -> instance diff --git a/docs/source/conf.py b/docs/source/conf.py --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -3,7 +3,8 @@ # Distutils2 documentation build configuration file, created by # sphinx-quickstart on Sun Feb 28 15:23:06 2010. # -# This file is execfile()d with the current directory set to its containing dir. +# This file is execfile()d with the current directory set to its containing +# dir. It requires Sphinx 1.0. # # Note that not all possible configuration values are present in this # autogenerated file. @@ -39,7 +40,7 @@ # General information about the project. project = u'Distutils2' -copyright = u'2010, Tarek Ziade and contributors' +copyright = u'2010, Tarek Ziad?? and contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -48,7 +49,7 @@ # The short X.Y version. version = '1.0a1' # The full version, including alpha/beta/rc tags. -release = '1.0a1' +release = '1.0a1+' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -97,14 +98,14 @@ # 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 = {} +html_theme_options = {'collapsiblesidebar': True} # 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 # " v documentation". -#html_title = None +html_title = 'Distutils2' # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None @@ -174,7 +175,7 @@ # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Distutils2.tex', u'Distutils2 Documentation', - u'Tarek Ziade and contributors', 'manual'), + u'Tarek Ziad?? and contributors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/docs/source/devresources.rst b/docs/source/devresources.rst new file mode 100644 --- /dev/null +++ b/docs/source/devresources.rst @@ -0,0 +1,46 @@ +=================== +Developer Resources +=================== + + +Source code +~~~~~~~~~~~ + +* Main repo: http://hg.python.org/distutils2 +* For contributors at: http://bitbucket.org/tarek/distutils2 + +Dependencies +~~~~~~~~~~~~ + +* unittest2 + +Issue Tracker +~~~~~~~~~~~~~ + +Using the `distutils2` component at the `python.org bug tracker `_, + +Mailing List +~~~~~~~~~~~~ + +http://groups.google.com/group/the-fellowship-of-the-packaging + +more general discussion at distutils-sig at python.org + +IRC Channel +~~~~~~~~~~~ + +#distutils on irc.freenode.net + +Documentation +~~~~~~~~~~~~~ + +This documentation is present in the docs/source directory of the above source +code. + +Additional useful information available at: + +* `Hitchhikers guide to packaging `_ + + + + diff --git a/docs/source/distutils/apiref.rst b/docs/source/distutils/apiref.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/apiref.rst @@ -0,0 +1,1990 @@ +.. _api-reference: + +************* +API Reference +************* + + +:mod:`distutils2.core` --- Core Distutils functionality +======================================================= + +.. module:: distutils2.core + :synopsis: The core Distutils functionality + + +The :mod:`distutils2.core` module is the only module that needs to be installed +to use the Distutils. It provides the :func:`setup` (which is called from the +setup script). Indirectly provides the :class:`distutils2.dist.Distribution` and +:class:`distutils2.cmd.Command` class. + + +.. function:: setup(arguments) + + The basic do-everything function that does most everything you could ever ask + for from a Distutils method. + + The setup function takes a large number of arguments. These are laid out in + the following table. + + +--------------------+--------------------------------+-------------------------------------------------------------+ + | argument name | value | type | + +====================+================================+=============================================================+ + | *name* | The name of the package | a string | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *version* | The version number of the | See :mod:`distutils2.version` | + | | package | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *summary* | A single line describing the | a string | + | | package | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *description* | Longer description of the | a string | + | | package | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *author* | The name of the package author | a string | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *author_email* | The email address of the | a string | + | | package author | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *maintainer* | The name of the current | a string | + | | maintainer, if different from | | + | | the author | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *maintainer_email* | The email address of the | | + | | current maintainer, if | | + | | different from the author | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *home_page* | A URL for the package | a URL | + | | (homepage) | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *download_url* | A URL to download the package | a URL | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *packages* | A list of Python packages that | a list of strings | + | | distutils will manipulate | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *py_modules* | A list of Python modules that | a list of strings | + | | distutils will manipulate | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *scripts* | A list of standalone script | a list of strings | + | | files to be built and | | + | | installed | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *ext_modules* | A list of Python extensions to | A list of instances of | + | | be built | :class:`distutils2.extension.Extension` | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *classifiers* | A list of categories for the | The list of available | + | | package | categorizations is at | + | | | http://pypi.python.org/pypi?:action=list_classifiers. | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *distclass* | the :class:`Distribution` | A subclass of | + | | class to use | :class:`distutils2.dist.Distribution` | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *script_name* | The name of the setup.py | a string | + | | script - defaults to | | + | | ``sys.argv[0]`` | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *script_args* | Arguments to supply to the | a list of strings | + | | setup script | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *options* | default options for the setup | a string | + | | script | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *license* | The license for the package | a string | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *keywords* | Descriptive metadata, see | | + | | :PEP:`314` | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *platforms* | | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *cmdclass* | A mapping of command names to | a dictionary | + | | :class:`Command` subclasses | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *data_files* | A list of data files to | a list | + | | install | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + | *package_dir* | A mapping of package to | a dictionary | + | | directory names | | + +--------------------+--------------------------------+-------------------------------------------------------------+ + + + +.. function:: run_setup(script_name[, script_args=None, stop_after='run']) + + Run a setup script in a somewhat controlled environment, and return the + :class:`distutils2.dist.Distribution` instance that drives things. This is + useful if you need to find out the distribution metadata (passed as keyword + args from *script* to :func:`setup`), or the contents of the config files or + command line. + + *script_name* is a file that will be run with :func:`execfile` + ``sys.argv[0]`` will be replaced with *script* for the duration of the call. + *script_args* is a list of strings; if supplied, ``sys.argv[1:]`` will be + replaced by *script_args* for the duration of the call. + + *stop_after* tells :func:`setup` when to stop processing; possible values: + + +---------------+---------------------------------------------+ + | value | description | + +===============+=============================================+ + | *init* | Stop after the :class:`Distribution` | + | | instance has been created and populated | + | | with the keyword arguments to :func:`setup` | + +---------------+---------------------------------------------+ + | *config* | Stop after config files have been parsed | + | | (and their data stored in the | + | | :class:`Distribution` instance) | + +---------------+---------------------------------------------+ + | *commandline* | Stop after the command line | + | | (``sys.argv[1:]`` or *script_args*) have | + | | been parsed (and the data stored in the | + | | :class:`Distribution` instance.) | + +---------------+---------------------------------------------+ + | *run* | Stop after all commands have been run (the | + | | same as if :func:`setup` had been called in | + | | the usual way). This is the default value. | + +---------------+---------------------------------------------+ + +In addition, the :mod:`distutils2.core` module exposed a number of classes that +live elsewhere. + +* :class:`~distutils.extension.Extension` from :mod:`distutils2.extension` + +* :class:`~distutils.command.cmd.Command` from :mod:`distutils2.command.cmd` + +* :class:`~distutils.dist.Distribution` from :mod:`distutils2.dist` + +A short description of each of these follows, but see the relevant module for +the full reference. + + +.. class:: Extension + + The Extension class describes a single C or C++extension module in a setup + script. It accepts the following keyword arguments in its constructor + + +------------------------+--------------------------------+---------------------------+ + | argument name | value | type | + +========================+================================+===========================+ + | *name* | the full name of the | string | + | | extension, including any | | + | | packages --- i.e. *not* a | | + | | filename or pathname, but | | + | | Python dotted name | | + +------------------------+--------------------------------+---------------------------+ + | *sources* | list of source filenames, | string | + | | relative to the distribution | | + | | root (where the setup script | | + | | lives), in Unix form (slash- | | + | | separated) for portability. | | + | | Source files may be C, C++, | | + | | SWIG (.i), platform-specific | | + | | resource files, or whatever | | + | | else is recognized by the | | + | | :command:`build_ext` command | | + | | as source for a Python | | + | | extension. | | + +------------------------+--------------------------------+---------------------------+ + | *include_dirs* | list of directories to search | string | + | | for C/C++ header files (in | | + | | Unix form for portability) | | + +------------------------+--------------------------------+---------------------------+ + | *define_macros* | list of macros to define; each | (string, string) tuple or | + | | macro is defined using a | (name, ``None``) | + | | 2-tuple ``(name, value)``, | | + | | where *value* is | | + | | either the string to define it | | + | | to or ``None`` to define it | | + | | without a particular value | | + | | (equivalent of ``#define FOO`` | | + | | in source or :option:`-DFOO` | | + | | on Unix C compiler command | | + | | line) | | + +------------------------+--------------------------------+---------------------------+ + | *undef_macros* | list of macros to undefine | string | + | | explicitly | | + +------------------------+--------------------------------+---------------------------+ + | *library_dirs* | list of directories to search | string | + | | for C/C++ libraries at link | | + | | time | | + +------------------------+--------------------------------+---------------------------+ + | *libraries* | list of library names (not | string | + | | filenames or paths) to link | | + | | against | | + +------------------------+--------------------------------+---------------------------+ + | *runtime_library_dirs* | list of directories to search | string | + | | for C/C++ libraries at run | | + | | time (for shared extensions, | | + | | this is when the extension is | | + | | loaded) | | + +------------------------+--------------------------------+---------------------------+ + | *extra_objects* | list of extra files to link | string | + | | with (e.g. object files not | | + | | implied by 'sources', static | | + | | library that must be | | + | | explicitly specified, binary | | + | | resource files, etc.) | | + +------------------------+--------------------------------+---------------------------+ + | *extra_compile_args* | any extra platform- and | string | + | | compiler-specific information | | + | | to use when compiling the | | + | | source files in 'sources'. For | | + | | platforms and compilers where | | + | | a command line makes sense, | | + | | this is typically a list of | | + | | command-line arguments, but | | + | | for other platforms it could | | + | | be anything. | | + +------------------------+--------------------------------+---------------------------+ + | *extra_link_args* | any extra platform- and | string | + | | compiler-specific information | | + | | to use when linking object | | + | | files together to create the | | + | | extension (or to create a new | | + | | static Python interpreter). | | + | | Similar interpretation as for | | + | | 'extra_compile_args'. | | + +------------------------+--------------------------------+---------------------------+ + | *export_symbols* | list of symbols to be exported | string | + | | from a shared extension. Not | | + | | used on all platforms, and not | | + | | generally necessary for Python | | + | | extensions, which typically | | + | | export exactly one symbol: | | + | | ``init`` + extension_name. | | + +------------------------+--------------------------------+---------------------------+ + | *depends* | list of files that the | string | + | | extension depends on | | + +------------------------+--------------------------------+---------------------------+ + | *language* | extension language (i.e. | string | + | | ``'c'``, ``'c++'``, | | + | | ``'objc'``). Will be detected | | + | | from the source extensions if | | + | | not provided. | | + +------------------------+--------------------------------+---------------------------+ + + +.. class:: Distribution + + A :class:`Distribution` describes how to build, install and package up a + Python software package. + + See the :func:`setup` function for a list of keyword arguments accepted by + the Distribution constructor. :func:`setup` creates a Distribution instance. + + +.. class:: Command + + A :class:`Command` class (or rather, an instance of one of its subclasses) + implement a single distutils command. + + +:mod:`distutils2.ccompiler` --- CCompiler base class +==================================================== + +.. module:: distutils2.ccompiler + :synopsis: Abstract CCompiler class + + +This module provides the abstract base class for the :class:`CCompiler` +classes. A :class:`CCompiler` instance can be used for all the compile and +link steps needed to build a single project. Methods are provided to set +options for the compiler --- macro definitions, include directories, link path, +libraries and the like. + +This module provides the following functions. + + +.. function:: gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries) + + Generate linker options for searching library directories and linking with + specific libraries. *libraries* and *library_dirs* are, respectively, lists + of library names (not filenames!) and search directories. Returns a list of + command-line options suitable for use with some compiler (depending on the + two format strings passed in). + + +.. function:: gen_preprocess_options(macros, include_dirs) + + Generate C preprocessor options (:option:`-D`, :option:`-U`, :option:`-I`) as + used by at least two types of compilers: the typical Unix compiler and Visual + C++. *macros* is the usual thing, a list of 1- or 2-tuples, where ``(name,)`` + means undefine (:option:`-U`) macro *name*, and ``(name, value)`` means + define (:option:`-D`) macro *name* to *value*. *include_dirs* is just a list + of directory names to be added to the header file search path (:option:`-I`). + Returns a list of command-line options suitable for either Unix compilers or + Visual C++. + + +.. function:: get_default_compiler(osname, platform) + + Determine the default compiler to use for the given platform. + + *osname* should be one of the standard Python OS names (i.e. the ones + returned by ``os.name``) and *platform* the common value returned by + ``sys.platform`` for the platform in question. + + The default values are ``os.name`` and ``sys.platform``. + + +.. function:: new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0) + + Factory function to generate an instance of some CCompiler subclass for the + supplied platform/compiler combination. *plat* defaults to ``os.name`` (e.g. + ``'posix'``, ``'nt'``), and *compiler* defaults to the default compiler for + that platform. Currently only ``'posix'`` and ``'nt'`` are supported, and the + default compilers are "traditional Unix interface" (:class:`UnixCCompiler` + class) and Visual C++ (:class:`MSVCCompiler` class). Note that it's perfectly + possible to ask for a Unix compiler object under Windows, and a Microsoft + compiler object under Unix---if you supply a value for *compiler*, *plat* is + ignored. + + .. % Is the posix/nt only thing still true? Mac OS X seems to work, and + .. % returns a UnixCCompiler instance. How to document this... hmm. + + +.. function:: show_compilers() + + Print list of available compilers (used by the :option:`--help-compiler` + options to :command:`build`, :command:`build_ext`, :command:`build_clib`). + + +.. class:: CCompiler([verbose=0, dry_run=0, force=0]) + + The abstract base class :class:`CCompiler` defines the interface that must be + implemented by real compiler classes. The class also has some utility + methods used by several compiler classes. + + The basic idea behind a compiler abstraction class is that each instance can + be used for all the compile/link steps in building a single project. Thus, + attributes common to all of those compile and link steps --- include + directories, macros to define, libraries to link against, etc. --- are + attributes of the compiler instance. To allow for variability in how + individual files are treated, most of those attributes may be varied on a + per-compilation or per-link basis. + + The constructor for each subclass creates an instance of the Compiler object. + Flags are *verbose* (show verbose output), *dry_run* (don't actually execute + the steps) and *force* (rebuild everything, regardless of dependencies). All + of these flags default to ``0`` (off). Note that you probably don't want to + instantiate :class:`CCompiler` or one of its subclasses directly - use the + :func:`distutils2.CCompiler.new_compiler` factory function instead. + + The following methods allow you to manually alter compiler options for the + instance of the Compiler class. + + + .. method:: CCompiler.add_include_dir(dir) + + Add *dir* to the list of directories that will be searched for header + files. The compiler is instructed to search directories in the order in + which they are supplied by successive calls to :meth:`add_include_dir`. + + + .. method:: CCompiler.set_include_dirs(dirs) + + Set the list of directories that will be searched to *dirs* (a list of + strings). Overrides any preceding calls to :meth:`add_include_dir`; + subsequent calls to :meth:`add_include_dir` add to the list passed to + :meth:`set_include_dirs`. This does not affect any list of standard + include directories that the compiler may search by default. + + + .. method:: CCompiler.add_library(libname) + + Add *libname* to the list of libraries that will be included in all links + driven by this compiler object. Note that *libname* should *not* be the + name of a file containing a library, but the name of the library itself: + the actual filename will be inferred by the linker, the compiler, or the + compiler class (depending on the platform). + + The linker will be instructed to link against libraries in the order they + were supplied to :meth:`add_library` and/or :meth:`set_libraries`. It is + perfectly valid to duplicate library names; the linker will be instructed + to link against libraries as many times as they are mentioned. + + + .. method:: CCompiler.set_libraries(libnames) + + Set the list of libraries to be included in all links driven by this + compiler object to *libnames* (a list of strings). This does not affect + any standard system libraries that the linker may include by default. + + + .. method:: CCompiler.add_library_dir(dir) + + Add *dir* to the list of directories that will be searched for libraries + specified to :meth:`add_library` and :meth:`set_libraries`. The linker + will be instructed to search for libraries in the order they are supplied + to :meth:`add_library_dir` and/or :meth:`set_library_dirs`. + + + .. method:: CCompiler.set_library_dirs(dirs) + + Set the list of library search directories to *dirs* (a list of strings). + This does not affect any standard library search path that the linker may + search by default. + + + .. method:: CCompiler.add_runtime_library_dir(dir) + + Add *dir* to the list of directories that will be searched for shared + libraries at runtime. + + + .. method:: CCompiler.set_runtime_library_dirs(dirs) + + Set the list of directories to search for shared libraries at runtime to + *dirs* (a list of strings). This does not affect any standard search path + that the runtime linker may search by default. + + + .. method:: CCompiler.define_macro(name[, value=None]) + + Define a preprocessor macro for all compilations driven by this compiler + object. The optional parameter *value* should be a string; if it is not + supplied, then the macro will be defined without an explicit value and the + exact outcome depends on the compiler used (XXX true? does ANSI say + anything about this?) + + + .. method:: CCompiler.undefine_macro(name) + + Undefine a preprocessor macro for all compilations driven by this compiler + object. If the same macro is defined by :meth:`define_macro` and + undefined by :meth:`undefine_macro` the last call takes precedence + (including multiple redefinitions or undefinitions). If the macro is + redefined/undefined on a per-compilation basis (i.e. in the call to + :meth:`compile`), then that takes precedence. + + + .. method:: CCompiler.add_link_object(object) + + Add *object* to the list of object files (or analogues, such as explicitly + named library files or the output of "resource compilers") to be included + in every link driven by this compiler object. + + + .. method:: CCompiler.set_link_objects(objects) + + Set the list of object files (or analogues) to be included in every link + to *objects*. This does not affect any standard object files that the + linker may include by default (such as system libraries). + + The following methods implement methods for autodetection of compiler + options, providing some functionality similar to GNU :program:`autoconf`. + + + .. method:: CCompiler.detect_language(sources) + + Detect the language of a given file, or list of files. Uses the instance + attributes :attr:`language_map` (a dictionary), and :attr:`language_order` + (a list) to do the job. + + + .. method:: CCompiler.find_library_file(dirs, lib[, debug=0]) + + Search the specified list of directories for a static or shared library file + *lib* and return the full path to that file. If *debug* is true, look for a + debugging version (if that makes sense on the current platform). Return + ``None`` if *lib* wasn't found in any of the specified directories. + + + .. method:: CCompiler.has_function(funcname [, includes=None, include_dirs=None, libraries=None, library_dirs=None]) + + Return a boolean indicating whether *funcname* is supported on the current + platform. The optional arguments can be used to augment the compilation + environment by providing additional include files and paths and libraries and + paths. + + + .. method:: CCompiler.library_dir_option(dir) + + Return the compiler option to add *dir* to the list of directories searched for + libraries. + + + .. method:: CCompiler.library_option(lib) + + Return the compiler option to add *dir* to the list of libraries linked into the + shared library or executable. + + + .. method:: CCompiler.runtime_library_dir_option(dir) + + Return the compiler option to add *dir* to the list of directories searched for + runtime libraries. + + + .. method:: CCompiler.set_executables(**args) + + Define the executables (and options for them) that will be run to perform the + various stages of compilation. The exact set of executables that may be + specified here depends on the compiler class (via the 'executables' class + attribute), but most will have: + + +--------------+------------------------------------------+ + | attribute | description | + +==============+==========================================+ + | *compiler* | the C/C++ compiler | + +--------------+------------------------------------------+ + | *linker_so* | linker used to create shared objects and | + | | libraries | + +--------------+------------------------------------------+ + | *linker_exe* | linker used to create binary executables | + +--------------+------------------------------------------+ + | *archiver* | static library creator | + +--------------+------------------------------------------+ + + On platforms with a command line (Unix, DOS/Windows), each of these is a string + that will be split into executable name and (optional) list of arguments. + (Splitting the string is done similarly to how Unix shells operate: words are + delimited by spaces, but quotes and backslashes can override this. See + :func:`distutils2.util.split_quoted`.) + + The following methods invoke stages in the build process. + + + .. method:: CCompiler.compile(sources[, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None]) + + Compile one or more source files. Generates object files (e.g. transforms a + :file:`.c` file to a :file:`.o` file.) + + *sources* must be a list of filenames, most likely C/C++ files, but in reality + anything that can be handled by a particular compiler and compiler class (e.g. + :class:`MSVCCompiler` can handle resource files in *sources*). Return a list of + object filenames, one per source filename in *sources*. Depending on the + implementation, not all source files will necessarily be compiled, but all + corresponding object filenames will be returned. + + If *output_dir* is given, object files will be put under it, while retaining + their original path component. That is, :file:`foo/bar.c` normally compiles to + :file:`foo/bar.o` (for a Unix implementation); if *output_dir* is *build*, then + it would compile to :file:`build/foo/bar.o`. + + *macros*, if given, must be a list of macro definitions. A macro definition is + either a ``(name, value)`` 2-tuple or a ``(name,)`` 1-tuple. The former defines + a macro; if the value is ``None``, the macro is defined without an explicit + value. The 1-tuple case undefines a macro. Later + definitions/redefinitions/undefinitions take precedence. + + *include_dirs*, if given, must be a list of strings, the directories to add to + the default include file search path for this compilation only. + + *debug* is a boolean; if true, the compiler will be instructed to output debug + symbols in (or alongside) the object file(s). + + *extra_preargs* and *extra_postargs* are implementation-dependent. On platforms + that have the notion of a command line (e.g. Unix, DOS/Windows), they are most + likely lists of strings: extra command-line arguments to prepend/append to the + compiler command line. On other platforms, consult the implementation class + documentation. In any event, they are intended as an escape hatch for those + occasions when the abstract compiler framework doesn't cut the mustard. + + *depends*, if given, is a list of filenames that all targets depend on. If a + source file is older than any file in depends, then the source file will be + recompiled. This supports dependency tracking, but only at a coarse + granularity. + + Raises :exc:`CompileError` on failure. + + + .. method:: CCompiler.create_static_lib(objects, output_libname[, output_dir=None, debug=0, target_lang=None]) + + Link a bunch of stuff together to create a static library file. The "bunch of + stuff" consists of the list of object files supplied as *objects*, the extra + object files supplied to :meth:`add_link_object` and/or + :meth:`set_link_objects`, the libraries supplied to :meth:`add_library` and/or + :meth:`set_libraries`, and the libraries supplied as *libraries* (if any). + + *output_libname* should be a library name, not a filename; the filename will be + inferred from the library name. *output_dir* is the directory where the library + file will be put. XXX defaults to what? + + *debug* is a boolean; if true, debugging information will be included in the + library (note that on most platforms, it is the compile step where this matters: + the *debug* flag is included here just for consistency). + + *target_lang* is the target language for which the given objects are being + compiled. This allows specific linkage time treatment of certain languages. + + Raises :exc:`LibError` on failure. + + + .. method:: CCompiler.link(target_desc, objects, output_filename[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, target_lang=None]) + + Link a bunch of stuff together to create an executable or shared library file. + + The "bunch of stuff" consists of the list of object files supplied as *objects*. + *output_filename* should be a filename. If *output_dir* is supplied, + *output_filename* is relative to it (i.e. *output_filename* can provide + directory components if needed). + + *libraries* is a list of libraries to link against. These are library names, + not filenames, since they're translated into filenames in a platform-specific + way (e.g. *foo* becomes :file:`libfoo.a` on Unix and :file:`foo.lib` on + DOS/Windows). However, they can include a directory component, which means the + linker will look in that specific directory rather than searching all the normal + locations. + + *library_dirs*, if supplied, should be a list of directories to search for + libraries that were specified as bare library names (i.e. no directory + component). These are on top of the system default and those supplied to + :meth:`add_library_dir` and/or :meth:`set_library_dirs`. *runtime_library_dirs* + is a list of directories that will be embedded into the shared library and used + to search for other shared libraries that \*it\* depends on at run-time. (This + may only be relevant on Unix.) + + *export_symbols* is a list of symbols that the shared library will export. + (This appears to be relevant only on Windows.) + + *debug* is as for :meth:`compile` and :meth:`create_static_lib`, with the + slight distinction that it actually matters on most platforms (as opposed to + :meth:`create_static_lib`, which includes a *debug* flag mostly for form's + sake). + + *extra_preargs* and *extra_postargs* are as for :meth:`compile` (except of + course that they supply command-line arguments for the particular linker being + used). + + *target_lang* is the target language for which the given objects are being + compiled. This allows specific linkage time treatment of certain languages. + + Raises :exc:`LinkError` on failure. + + + .. method:: CCompiler.link_executable(objects, output_progname[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, target_lang=None]) + + Link an executable. *output_progname* is the name of the file executable, while + *objects* are a list of object filenames to link in. Other arguments are as for + the :meth:`link` method. + + + .. method:: CCompiler.link_shared_lib(objects, output_libname[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, target_lang=None]) + + Link a shared library. *output_libname* is the name of the output library, + while *objects* is a list of object filenames to link in. Other arguments are + as for the :meth:`link` method. + + + .. method:: CCompiler.link_shared_object(objects, output_filename[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, target_lang=None]) + + Link a shared object. *output_filename* is the name of the shared object that + will be created, while *objects* is a list of object filenames to link in. + Other arguments are as for the :meth:`link` method. + + + .. method:: CCompiler.preprocess(source[, output_file=None, macros=None, include_dirs=None, extra_preargs=None, extra_postargs=None]) + + Preprocess a single C/C++ source file, named in *source*. Output will be written + to file named *output_file*, or *stdout* if *output_file* not supplied. + *macros* is a list of macro definitions as for :meth:`compile`, which will + augment the macros set with :meth:`define_macro` and :meth:`undefine_macro`. + *include_dirs* is a list of directory names that will be added to the default + list, in the same way as :meth:`add_include_dir`. + + Raises :exc:`PreprocessError` on failure. + + The following utility methods are defined by the :class:`CCompiler` class, for + use by the various concrete subclasses. + + + .. method:: CCompiler.executable_filename(basename[, strip_dir=0, output_dir='']) + + Returns the filename of the executable for the given *basename*. Typically for + non-Windows platforms this is the same as the basename, while Windows will get + a :file:`.exe` added. + + + .. method:: CCompiler.library_filename(libname[, lib_type='static', strip_dir=0, output_dir='']) + + Returns the filename for the given library name on the current platform. On Unix + a library with *lib_type* of ``'static'`` will typically be of the form + :file:`liblibname.a`, while a *lib_type* of ``'dynamic'`` will be of the form + :file:`liblibname.so`. + + + .. method:: CCompiler.object_filenames(source_filenames[, strip_dir=0, output_dir='']) + + Returns the name of the object files for the given source files. + *source_filenames* should be a list of filenames. + + + .. method:: CCompiler.shared_object_filename(basename[, strip_dir=0, output_dir='']) + + Returns the name of a shared object file for the given file name *basename*. + + + .. method:: CCompiler.execute(func, args[, msg=None, level=1]) + + Invokes :func:`distutils2.util.execute` This method invokes a Python function + *func* with the given arguments *args*, after logging and taking into account + the *dry_run* flag. XXX see also. + + + .. method:: CCompiler.spawn(cmd) + + Invokes :func:`distutils2.util.spawn`. This invokes an external process to run + the given command. XXX see also. + + + .. method:: CCompiler.mkpath(name[, mode=511]) + + Invokes :func:`distutils2.dir_util.mkpath`. This creates a directory and any + missing ancestor directories. XXX see also. + + + .. method:: CCompiler.move_file(src, dst) + + Invokes :meth:`distutils2.file_util.move_file`. Renames *src* to *dst*. XXX see + also. + + + .. method:: CCompiler.announce(msg[, level=1]) + + Write a message using :func:`distutils2.log.debug`. XXX see also. + + + .. method:: CCompiler.warn(msg) + + Write a warning message *msg* to standard error. + + + .. method:: CCompiler.debug_print(msg) + + If the *debug* flag is set on this :class:`CCompiler` instance, print *msg* to + standard output, otherwise do nothing. + +.. % \subsection{Compiler-specific modules} +.. % +.. % The following modules implement concrete subclasses of the abstract +.. % \class{CCompiler} class. They should not be instantiated directly, but should +.. % be created using \function{distutils.ccompiler.new_compiler()} factory +.. % function. + + +:mod:`distutils2.unixccompiler` --- Unix C Compiler +=================================================== + +.. module:: distutils2.unixccompiler + :synopsis: UNIX C Compiler + + +This module provides the :class:`UnixCCompiler` class, a subclass of +:class:`CCompiler` that handles the typical Unix-style command-line C compiler: + +* macros defined with :option:`-Dname[=value]` + +* macros undefined with :option:`-Uname` + +* include search directories specified with :option:`-Idir` + +* libraries specified with :option:`-llib` + +* library search directories specified with :option:`-Ldir` + +* compile handled by :program:`cc` (or similar) executable with :option:`-c` + option: compiles :file:`.c` to :file:`.o` + +* link static library handled by :program:`ar` command (possibly with + :program:`ranlib`) + +* link shared library handled by :program:`cc` :option:`-shared` + + +:mod:`distutils2.msvccompiler` --- Microsoft Compiler +===================================================== + +.. module:: distutils2.msvccompiler + :synopsis: Microsoft Compiler + + +This module provides :class:`MSVCCompiler`, an implementation of the abstract +:class:`CCompiler` class for Microsoft Visual Studio. Typically, extension +modules need to be compiled with the same compiler that was used to compile +Python. For Python 2.3 and earlier, the compiler was Visual Studio 6. For Python +2.4 and 2.5, the compiler is Visual Studio .NET 2003. The AMD64 and Itanium +binaries are created using the Platform SDK. + +:class:`MSVCCompiler` will normally choose the right compiler, linker etc. on +its own. To override this choice, the environment variables *DISTUTILS_USE_SDK* +and *MSSdk* must be both set. *MSSdk* indicates that the current environment has +been setup by the SDK's ``SetEnv.Cmd`` script, or that the environment variables +had been registered when the SDK was installed; *DISTUTILS_USE_SDK* indicates +that the distutils user has made an explicit choice to override the compiler +selection by :class:`MSVCCompiler`. + + +:mod:`distutils2.bcppcompiler` --- Borland Compiler +=================================================== + +.. module:: distutils2.bcppcompiler + + +This module provides :class:`BorlandCCompiler`, an subclass of the abstract +:class:`CCompiler` class for the Borland C++ compiler. + + +:mod:`distutils2.cygwincompiler` --- Cygwin Compiler +==================================================== + +.. module:: distutils2.cygwinccompiler + + +This module provides the :class:`CygwinCCompiler` class, a subclass of +:class:`UnixCCompiler` that handles the Cygwin port of the GNU C compiler to +Windows. It also contains the Mingw32CCompiler class which handles the mingw32 +port of GCC (same as cygwin in no-cygwin mode). + + +:mod:`distutils2.emxccompiler` --- OS/2 EMX Compiler +==================================================== + +.. module:: distutils2.emxccompiler + :synopsis: OS/2 EMX Compiler support + + +This module provides the EMXCCompiler class, a subclass of +:class:`UnixCCompiler` that handles the EMX port of the GNU C compiler to OS/2. + + +:mod:`distutils2.archive_util` --- Archiving utilities +====================================================== + +.. module:: distutils2.archive_util + :synopsis: Utility functions for creating archive files (tarballs, zip files, ...) + + +This module provides a few functions for creating archive files, such as +tarballs or zipfiles. + + +.. function:: make_archive(base_name, format[, root_dir=None, base_dir=None, verbose=0, dry_run=0]) + + Create an archive file (e.g. ``zip`` or ``tar``). *base_name* is the name of + the file to create, minus any format-specific extension; *format* is the + archive format: one of ``zip``, ``tar``, ``ztar``, or ``gztar``. *root_dir* + is a directory that will be the root directory of the archive; i.e. we + typically + ``chdir`` into *root_dir* before creating the archive. *base_dir* is the + directory where we start archiving from; i.e. *base_dir* will be the common + prefix of all files and directories in the archive. *root_dir* and *base_dir* + both default to the current directory. Returns the name of the archive file. + + .. XXX This should be changed to support bz2 files. + + +.. function:: make_tarball(base_name, base_dir[, compress='gzip', verbose=0, dry_run=0]) + + 'Create an (optional compressed) archive as a tar file from all files in and + under *base_dir*. *compress* must be ``'gzip'`` (the default), ``'compress'``, + ``'bzip2'``, or ``None``. Both :program:`tar` and the compression utility named + by *compress* must be on the default program search path, so this is probably + Unix-specific. The output tar file will be named :file:`base_dir.tar`, + possibly plus the appropriate compression extension (:file:`.gz`, :file:`.bz2` + or :file:`.Z`). Return the output filename. + + .. TODO update to reflect use of the :mod:`tarfile` module. + + +.. function:: make_zipfile(base_name, base_dir[, verbose=0, dry_run=0]) + + Create a zip file from all files in and under *base_dir*. The output zip file + will be named *base_dir* + :file:`.zip`. Uses either the :mod:`zipfile` Python + module (if available) or the InfoZIP :file:`zip` utility (if installed and + found on the default search path). If neither tool is available, raises + :exc:`DistutilsExecError`. Returns the name of the output zip file. + + +:mod:`distutils2.dep_util` --- Dependency checking +================================================== + +.. module:: distutils2.dep_util + :synopsis: Utility functions for simple dependency checking + + +This module provides functions for performing simple, timestamp-based +dependency of files and groups of files; also, functions based entirely on such +timestamp dependency analysis. + + +.. function:: newer(source, target) + + Return true if *source* exists and is more recently modified than *target*, + or if *source* exists and *target* doesn't. Return false if both exist and + *target* is the same age or newer than *source*. Raise + :exc:`DistutilsFileError` if *source* does not exist. + + +.. function:: newer_pairwise(sources, targets) + + Walk two filename lists in parallel, testing if each source is newer than its + corresponding target. Return a pair of lists (*sources*, *targets*) where + source is newer than target, according to the semantics of :func:`newer` + + .. % % equivalent to a listcomp... + + +.. function:: newer_group(sources, target[, missing='error']) + + Return true if *target* is out-of-date with respect to any file listed in + *sources*. In other words, if *target* exists and is newer than every file + in *sources*, return false; otherwise return true. *missing* controls what + we do when a source file is missing; the default (``'error'``) is to blow up + with an :exc:`OSError` from inside :func:`os.stat`; if it is ``'ignore'``, we + silently drop any missing source files; if it is ``'newer'``, any missing + source files make us assume that *target* is out-of-date (this is handy in + "dry-run" mode: it'll make you pretend to carry out commands that wouldn't + work because inputs are missing, but that doesn't matter because you're not + actually going to run the commands). + + +:mod:`distutils2.dir_util` --- Directory tree operations +======================================================== + +.. module:: distutils2.dir_util + :synopsis: Utility functions for operating on directories and directory trees + + +This module provides functions for operating on directories and trees of +directories. + + +.. function:: mkpath(name[, mode=0777, verbose=0, dry_run=0]) + + Create a directory and any missing ancestor directories. If the directory + already exists (or if *name* is the empty string, which means the current + directory, which of course exists), then do nothing. Raise + :exc:`DistutilsFileError` if unable to create some directory along the way + (e.g. some sub-path exists, but is a file rather than a directory). If + *verbose* is true, print a one-line summary of each mkdir to stdout. Return + the list of directories actually created. + + +.. function:: create_tree(base_dir, files[, mode=0777, verbose=0, dry_run=0]) + + Create all the empty directories under *base_dir* needed to put *files* + there. *base_dir* is just the a name of a directory which doesn't necessarily + exist yet; *files* is a list of filenames to be interpreted relative to + *base_dir*. *base_dir* + the directory portion of every file in *files* will + be created if it doesn't already exist. *mode*, *verbose* and *dry_run* + flags are as for :func:`mkpath`. + + +.. function:: copy_tree(src, dst[, preserve_mode=1, preserve_times=1, preserve_symlinks=0, update=0, verbose=0, dry_run=0]) + + Copy an entire directory tree *src* to a new location *dst*. Both *src* and + *dst* must be directory names. If *src* is not a directory, raise + :exc:`DistutilsFileError`. If *dst* does not exist, it is created with + :func:`mkpath`. The end result of the copy is that every file in *src* is + copied to *dst*, and directories under *src* are recursively copied to + *dst*. Return the list of files that were copied or might have been copied, + using their output name. The return value is unaffected by *update* or + *dry_run*: it is simply the list of all files under *src*, with the names + changed to be under *dst*. + + *preserve_mode* and *preserve_times* are the same as for :func:`copy_file` + in :mod:`distutils2.file_util`; note that they only apply to regular files, + not to directories. If *preserve_symlinks* is true, symlinks will be copied + as symlinks (on platforms that support them!); otherwise (the default), the + destination of the symlink will be copied. *update* and *verbose* are the + same as for :func:`copy_file`. + + +.. function:: remove_tree(directory[, verbose=0, dry_run=0]) + + Recursively remove *directory* and all files and directories underneath it. + Any errors are ignored (apart from being reported to ``sys.stdout`` if + *verbose* is true). + +.. XXX Some of this could be replaced with the shutil module? + + +:mod:`distutils2.file_util` --- Single file operations +====================================================== + +.. module:: distutils2.file_util + :synopsis: Utility functions for operating on single files + + +This module contains some utility functions for operating on individual files. + + +.. function:: copy_file(src, dst[, preserve_mode=1, preserve_times=1, update=0, link=None, verbose=0, dry_run=0]) + + Copy file *src* to *dst*. If *dst* is a directory, then *src* is copied there + with the same name; otherwise, it must be a filename. (If the file exists, it + will be ruthlessly clobbered.) If *preserve_mode* is true (the default), the + file's mode (type and permission bits, or whatever is analogous on the + current platform) is copied. If *preserve_times* is true (the default), the + last-modified and last-access times are copied as well. If *update* is true, + *src* will only be copied if *dst* does not exist, or if *dst* does exist but + is older than *src*. + + *link* allows you to make hard links (using :func:`os.link`) or symbolic + links (using :func:`os.symlink`) instead of copying: set it to ``'hard'`` or + ``'sym'``; if it is ``None`` (the default), files are copied. Don't set + *link* on systems that don't support it: :func:`copy_file` doesn't check if + hard or symbolic linking is available. It uses :func:`_copy_file_contents` + to copy file contents. + + Return a tuple ``(dest_name, copied)``: *dest_name* is the actual name of + the output file, and *copied* is true if the file was copied (or would have + been copied, if *dry_run* true). + + .. % XXX if the destination file already exists, we clobber it if + .. % copying, but blow up if linking. Hmmm. And I don't know what + .. % macostools.copyfile() does. Should definitely be consistent, and + .. % should probably blow up if destination exists and we would be + .. % changing it (i.e. it's not already a hard/soft link to src OR + .. % (not update) and (src newer than dst)). + + +.. function:: move_file(src, dst[, verbose, dry_run]) + + Move file *src* to *dst*. If *dst* is a directory, the file will be moved + into it with the same name; otherwise, *src* is just renamed to *dst*. + Returns the new full name of the file. + + .. warning:: + + Handles cross-device moves on Unix using :func:`copy_file`. What about + other systems? + + +.. function:: write_file(filename, contents) + + Create a file called *filename* and write *contents* (a sequence of strings + without line terminators) to it. + + +:mod:`distutils2.util` --- Miscellaneous other utility functions +================================================================ + +.. module:: distutils2.util + :synopsis: Miscellaneous other utility functions + + +This module contains other assorted bits and pieces that don't fit into any +other utility module. + + +.. function:: get_platform() + + Return a string that identifies the current platform. This is used mainly to + distinguish platform-specific build directories and platform-specific built + distributions. Typically includes the OS name and version and the + architecture (as supplied by 'os.uname()'), although the exact information + included depends on the OS; e.g. for IRIX the architecture isn't particularly + important (IRIX only runs on SGI hardware), but for Linux the kernel version + isn't particularly important. + + Examples of returned values: + + * ``linux-i586`` + * ``linux-alpha`` + * ``solaris-2.6-sun4u`` + * ``irix-5.3`` + * ``irix64-6.2`` + + For non-POSIX platforms, currently just returns ``sys.platform``. + + For Mac OS X systems the OS version reflects the minimal version on which + binaries will run (that is, the value of ``MACOSX_DEPLOYMENT_TARGET`` + during the build of Python), not the OS version of the current system. + + For universal binary builds on Mac OS X the architecture value reflects + the univeral binary status instead of the architecture of the current + processor. For 32-bit universal binaries the architecture is ``fat``, + for 64-bit universal binaries the architecture is ``fat64``, and + for 4-way universal binaries the architecture is ``universal``. Starting + from Python 2.7 and Python 3.2 the architecture ``fat3`` is used for + a 3-way universal build (ppc, i386, x86_64) and ``intel`` is used for + a univeral build with the i386 and x86_64 architectures + + Examples of returned values on Mac OS X: + + * ``macosx-10.3-ppc`` + + * ``macosx-10.3-fat`` + + * ``macosx-10.5-universal`` + + * ``macosx-10.6-intel`` + + .. XXX reinvention of platform module? + + +.. function:: convert_path(pathname) + + Return 'pathname' as a name that will work on the native filesystem, i.e. + split it on '/' and put it back together again using the current directory + separator. Needed because filenames in the setup script are always supplied + in Unix style, and have to be converted to the local convention before we + can actually use them in the filesystem. Raises :exc:`ValueError` on + non-Unix-ish systems if *pathname* either starts or ends with a slash. + + +.. function:: change_root(new_root, pathname) + + Return *pathname* with *new_root* prepended. If *pathname* is relative, this + is equivalent to ``os.path.join(new_root,pathname)`` Otherwise, it requires + making *pathname* relative and then joining the two, which is tricky on + DOS/Windows. + + +.. function:: check_environ() + + Ensure that 'os.environ' has all the environment variables we guarantee that + users can use in config files, command-line options, etc. Currently this + includes: + + * :envvar:`HOME` - user's home directory (Unix only) + * :envvar:`PLAT` - description of the current platform, including hardware + and OS (see :func:`get_platform`) + + +.. function:: subst_vars(s, local_vars) + + Perform shell/Perl-style variable substitution on *s*. Every occurrence of + ``$`` followed by a name is considered a variable, and variable is + substituted by the value found in the *local_vars* dictionary, or in + ``os.environ`` if it's not in *local_vars*. *os.environ* is first + checked/augmented to guarantee that it contains certain values: see + :func:`check_environ`. Raise :exc:`ValueError` for any variables not found + in either *local_vars* or ``os.environ``. + + Note that this is not a fully-fledged string interpolation function. A valid + ``$variable`` can consist only of upper and lower case letters, numbers and + an underscore. No { } or ( ) style quoting is available. + + +.. function:: grok_environment_error(exc[, prefix='error: ']) + + Generate a useful error message from an :exc:`EnvironmentError` + (:exc:`IOError` or :exc:`OSError`) exception object. Does what it can to deal + with exception objects that don't have a filename (which happens when the + error is due to a two-file operation, such as :func:`rename` or + :func:`link`). Returns the error message as a string prefixed with *prefix*. + + +.. function:: split_quoted(s) + + Split a string up according to Unix shell-like rules for quotes and + backslashes. In short: words are delimited by spaces, as long as those spaces + are not escaped by a backslash, or inside a quoted string. Single and double + quotes are equivalent, and the quote characters can be backslash-escaped. + The backslash is stripped from any two-character escape sequence, leaving + only the escaped character. The quote characters are stripped from any + quoted string. Returns a list of words. + + .. % Should probably be moved into the standard library. + + +.. function:: execute(func, args[, msg=None, verbose=0, dry_run=0]) + + Perform some action that affects the outside world (for instance, writing to + the filesystem). Such actions are special because they are disabled by the + *dry_run* flag. This method takes care of all that bureaucracy for you; + all you have to do is supply the function to call and an argument tuple for + it (to embody the "external action" being performed), and an optional message + to print. + + +.. function:: strtobool(val) + + Convert a string representation of truth to true (1) or false (0). + + True values are ``y``, ``yes``, ``t``, ``true``, ``on`` and ``1``; false + values are ``n``, ``no``, ``f``, ``false``, ``off`` and ``0``. Raises + :exc:`ValueError` if *val* is anything else. + +.. TODO Add :term: markup to bytecode when merging into the stdlib + +.. function:: byte_compile(py_files[, optimize=0, force=0, prefix=None, base_dir=None, verbose=1, dry_run=0, direct=None]) + + Byte-compile a collection of Python source files to either :file:`.pyc` or + :file:`.pyo` files in the same directory. *py_files* is a list of files to + compile; any files that don't end in :file:`.py` are silently skipped. + *optimize* must be one of the following: + + * ``0`` - don't optimize (generate :file:`.pyc`) + * ``1`` - normal optimization (like ``python -O``) + * ``2`` - extra optimization (like ``python -OO``) + + If *force* is true, all files are recompiled regardless of timestamps. + + The source filename encoded in each bytecode file defaults to the filenames + listed in *py_files*; you can modify these with *prefix* and *basedir*. + *prefix* is a string that will be stripped off of each source filename, and + *base_dir* is a directory name that will be prepended (after *prefix* is + stripped). You can supply either or both (or neither) of *prefix* and + *base_dir*, as you wish. + + If *dry_run* is true, doesn't actually do anything that would affect the + filesystem. + + Byte-compilation is either done directly in this interpreter process with the + standard :mod:`py_compile` module, or indirectly by writing a temporary + script and executing it. Normally, you should let :func:`byte_compile` + figure out to use direct compilation or not (see the source for details). + The *direct* flag is used by the script generated in indirect mode; unless + you know what you're doing, leave it set to ``None``. + + +.. function:: rfc822_escape(header) + + Return a version of *header* escaped for inclusion in an :rfc:`822` header, by + ensuring there are 8 spaces space after each newline. Note that it does no + other modification of the string. + + .. % this _can_ be replaced + +.. % \subsection{Distutils objects} + + +:mod:`distutils2.dist` --- The Distribution class +================================================= + +.. module:: distutils2.dist + :synopsis: Provides the Distribution class, which represents the module + distribution being built/installed/distributed + + +This module provides the :class:`Distribution` class, which represents the +module distribution being built/installed/distributed. + + +:mod:`distutils2.extension` --- The Extension class +=================================================== + +.. module:: distutils2.extension + :synopsis: Provides the Extension class, used to describe C/C++ extension + modules in setup scripts + + +This module provides the :class:`Extension` class, used to describe C/C++ +extension modules in setup scripts. + +.. % \subsection{Ungrouped modules} +.. % The following haven't been moved into a more appropriate section yet. + + +:mod:`distutils2.debug` --- Distutils debug mode +================================================ + +.. module:: distutils2.debug + :synopsis: Provides the debug flag for distutils + + +This module provides the DEBUG flag. + + +:mod:`distutils2.errors` --- Distutils exceptions +================================================= + +.. module:: distutils2.errors + :synopsis: Provides standard distutils exceptions + + +Provides exceptions used by the Distutils modules. Note that Distutils modules +may raise standard exceptions; in particular, SystemExit is usually raised for +errors that are obviously the end-user's fault (e.g. bad command-line arguments). + +This module is safe to use in ``from ... import *`` mode; it only exports +symbols whose names start with ``Distutils`` and end with ``Error``. + + +:mod:`distutils2.fancy_getopt` --- Wrapper around the standard getopt module +============================================================================ + +.. module:: distutils2.fancy_getopt + :synopsis: Additional getopt functionality + + +This module provides a wrapper around the standard :mod:`getopt` module that +provides the following additional features: + +* short and long options are tied together + +* options have help strings, so :func:`fancy_getopt` could potentially create a + complete usage summary + +* options set attributes of a passed-in object + +* boolean options can have "negative aliases" --- e.g. if :option:`--quiet` is + the "negative alias" of :option:`--verbose`, then :option:`--quiet` on the + command line sets *verbose* to false. + +.. XXX Should be replaced with :mod:`argparse`. + + +.. function:: fancy_getopt(options, negative_opt, object, args) + + Wrapper function. *options* is a list of ``(long_option, short_option, + help_string)`` 3-tuples as described in the constructor for + :class:`FancyGetopt`. *negative_opt* should be a dictionary mapping option names + to option names, both the key and value should be in the *options* list. + *object* is an object which will be used to store values (see the :meth:`getopt` + method of the :class:`FancyGetopt` class). *args* is the argument list. Will use + ``sys.argv[1:]`` if you pass ``None`` as *args*. + + +.. function:: wrap_text(text, width) + + Wraps *text* to less than *width* wide. + + .. XXX Should be replaced with :mod:`textwrap` (which is available in Python + 2.3 and later). + + +.. class:: FancyGetopt([option_table=None]) + + The option_table is a list of 3-tuples: ``(long_option, short_option, + help_string)`` + + If an option takes an argument, its *long_option* should have ``'='`` appended; + *short_option* should just be a single character, no ``':'`` in any case. + *short_option* should be ``None`` if a *long_option* doesn't have a + corresponding *short_option*. All option tuples must have long options. + +The :class:`FancyGetopt` class provides the following methods: + + +.. method:: FancyGetopt.getopt([args=None, object=None]) + + Parse command-line options in args. Store as attributes on *object*. + + If *args* is ``None`` or not supplied, uses ``sys.argv[1:]``. If *object* is + ``None`` or not supplied, creates a new :class:`OptionDummy` instance, stores + option values there, and returns a tuple ``(args, object)``. If *object* is + supplied, it is modified in place and :func:`getopt` just returns *args*; in + both cases, the returned *args* is a modified copy of the passed-in *args* list, + which is left untouched. + + .. % and args returned are? + + +.. method:: FancyGetopt.get_option_order() + + Returns the list of ``(option, value)`` tuples processed by the previous run of + :meth:`getopt` Raises :exc:`RuntimeError` if :meth:`getopt` hasn't been called + yet. + + +.. method:: FancyGetopt.generate_help([header=None]) + + Generate help text (a list of strings, one per suggested line of output) from + the option table for this :class:`FancyGetopt` object. + + If supplied, prints the supplied *header* at the top of the help. + + +:mod:`distutils2.filelist` --- The FileList class +================================================= + +.. module:: distutils2.filelist + :synopsis: The FileList class, used for poking about the file system and + building lists of files. + + +This module provides the :class:`FileList` class, used for poking about the +filesystem and building lists of files. + +.. TODO move to util + + +:mod:`distutils2.log` --- Simple PEP 282-style logging +====================================================== + +.. module:: distutils2.log + :synopsis: A simple logging mechanism, 282-style + + +.. XXX Should be replaced with standard :mod:`logging` module. + + + +:mod:`distutils2.spawn` --- Spawn a sub-process +=============================================== + +.. module:: distutils2.spawn + :synopsis: Provides the spawn() function + + +This module provides the :func:`spawn` function, a front-end to various +platform-specific functions for launching another program in a sub-process. +Also provides :func:`find_executable` to search the path for a given executable +name. + + +:mod:`distutils2.sysconfig` --- System configuration information +================================================================ + +.. module:: distutils2.sysconfig + :synopsis: Low-level access to configuration information of the Python interpreter. +.. moduleauthor:: Fred L. Drake, Jr. +.. moduleauthor:: Greg Ward +.. sectionauthor:: Fred L. Drake, Jr. + + +The :mod:`distutils2.sysconfig` module provides access to Python's low-level +configuration information. The specific configuration variables available +depend heavily on the platform and configuration. The specific variables depend +on the build process for the specific version of Python being run; the variables +are those found in the :file:`Makefile` and configuration header that are +installed with Python on Unix systems. The configuration header is called +:file:`pyconfig.h` for Python versions starting with 2.2, and :file:`config.h` +for earlier versions of Python. + +Some additional functions are provided which perform some useful manipulations +for other parts of the :mod:`distutils2` package. + + +.. data:: PREFIX + + The result of ``os.path.normpath(sys.prefix)``. + + +.. data:: EXEC_PREFIX + + The result of ``os.path.normpath(sys.exec_prefix)``. + + +.. function:: get_config_var(name) + + Return the value of a single variable. This is equivalent to + ``get_config_vars().get(name)``. + + +.. function:: get_config_vars(...) + + Return a set of variable definitions. If there are no arguments, this returns a + dictionary mapping names of configuration variables to values. If arguments are + provided, they should be strings, and the return value will be a sequence giving + the associated values. If a given name does not have a corresponding value, + ``None`` will be included for that variable. + + +.. function:: get_config_h_filename() + + Return the full path name of the configuration header. For Unix, this will be + the header generated by the :program:`configure` script; for other platforms the + header will have been supplied directly by the Python source distribution. The + file is a platform-specific text file. + + +.. function:: get_makefile_filename() + + Return the full path name of the :file:`Makefile` used to build Python. For + Unix, this will be a file generated by the :program:`configure` script; the + meaning for other platforms will vary. The file is a platform-specific text + file, if it exists. This function is only useful on POSIX platforms. + + +.. function:: get_python_inc([plat_specific[, prefix]]) + + Return the directory for either the general or platform-dependent C include + files. If *plat_specific* is true, the platform-dependent include directory is + returned; if false or omitted, the platform-independent directory is returned. + If *prefix* is given, it is used as either the prefix instead of + :const:`PREFIX`, or as the exec-prefix instead of :const:`EXEC_PREFIX` if + *plat_specific* is true. + + +.. function:: get_python_lib([plat_specific[, standard_lib[, prefix]]]) + + Return the directory for either the general or platform-dependent library + installation. If *plat_specific* is true, the platform-dependent include + directory is returned; if false or omitted, the platform-independent directory + is returned. If *prefix* is given, it is used as either the prefix instead of + :const:`PREFIX`, or as the exec-prefix instead of :const:`EXEC_PREFIX` if + *plat_specific* is true. If *standard_lib* is true, the directory for the + standard library is returned rather than the directory for the installation of + third-party extensions. + +The following function is only intended for use within the :mod:`distutils2` +package. + + +.. function:: customize_compiler(compiler) + + Do any platform-specific customization of a + :class:`distutils2.ccompiler.CCompiler` instance. + + This function is only needed on Unix at this time, but should be called + consistently to support forward-compatibility. It inserts the information that + varies across Unix flavors and is stored in Python's :file:`Makefile`. This + information includes the selected compiler, compiler and linker options, and the + extension used by the linker for shared objects. + +This function is even more special-purpose, and should only be used from +Python's own build procedures. + + +.. function:: set_python_build() + + Inform the :mod:`distutils2.sysconfig` module that it is being used as part of + the build process for Python. This changes a lot of relative locations for + files, allowing them to be located in the build area rather than in an installed + Python. + + +:mod:`distutils2.text_file` --- The TextFile class +================================================== + +.. module:: distutils2.text_file + :synopsis: provides the TextFile class, a simple interface to text files + + +This module provides the :class:`TextFile` class, which gives an interface to +text files that (optionally) takes care of stripping comments, ignoring blank +lines, and joining lines with backslashes. + + +.. class:: TextFile([filename=None, file=None, **options]) + + This class provides a file-like object that takes care of all the things you + commonly want to do when processing a text file that has some line-by-line + syntax: strip comments (as long as ``#`` is your comment character), skip blank + lines, join adjacent lines by escaping the newline (i.e. backslash at end of + line), strip leading and/or trailing whitespace. All of these are optional and + independently controllable. + + The class provides a :meth:`warn` method so you can generate warning messages + that report physical line number, even if the logical line in question spans + multiple physical lines. Also provides :meth:`unreadline` for implementing + line-at-a-time lookahead. + + :class:`TextFile` instances are create with either *filename*, *file*, or both. + :exc:`RuntimeError` is raised if both are ``None``. *filename* should be a + string, and *file* a file object (or something that provides :meth:`readline` + and :meth:`close` methods). It is recommended that you supply at least + *filename*, so that :class:`TextFile` can include it in warning messages. If + *file* is not supplied, :class:`TextFile` creates its own using the + :func:`open` built-in function. + + The options are all boolean, and affect the values returned by :meth:`readline` + + +------------------+--------------------------------+---------+ + | option name | description | default | + +==================+================================+=========+ + | *strip_comments* | strip from ``'#'`` to end-of- | true | + | | line, as well as any | | + | | whitespace leading up to the | | + | | ``'#'``\ ---unless it is | | + | | escaped by a backslash | | + +------------------+--------------------------------+---------+ + | *lstrip_ws* | strip leading whitespace from | false | + | | each line before returning it | | + +------------------+--------------------------------+---------+ + | *rstrip_ws* | strip trailing whitespace | true | + | | (including line terminator!) | | + | | from each line before | | + | | returning it. | | + +------------------+--------------------------------+---------+ + | *skip_blanks* | skip lines that are empty | true | + | | \*after\* stripping comments | | + | | and whitespace. (If both | | + | | lstrip_ws and rstrip_ws are | | + | | false, then some lines may | | + | | consist of solely whitespace: | | + | | these will \*not\* be skipped, | | + | | even if *skip_blanks* is | | + | | true.) | | + +------------------+--------------------------------+---------+ + | *join_lines* | if a backslash is the last | false | + | | non-newline character on a | | + | | line after stripping comments | | + | | and whitespace, join the | | + | | following line to it to form | | + | | one logical line; if N | | + | | consecutive lines end with a | | + | | backslash, then N+1 physical | | + | | lines will be joined to form | | + | | one logical line. | | + +------------------+--------------------------------+---------+ + | *collapse_join* | strip leading whitespace from | false | + | | lines that are joined to their | | + | | predecessor; only matters if | | + | | ``(join_lines and not | | + | | lstrip_ws)`` | | + +------------------+--------------------------------+---------+ + + Note that since *rstrip_ws* can strip the trailing newline, the semantics of + :meth:`readline` must differ from those of the built-in file object's + :meth:`readline` method! In particular, :meth:`readline` returns ``None`` for + end-of-file: an empty string might just be a blank line (or an all-whitespace + line), if *rstrip_ws* is true but *skip_blanks* is not. + + + .. method:: TextFile.open(filename) + + Open a new file *filename*. This overrides any *file* or *filename* + constructor arguments. + + + .. method:: TextFile.close() + + Close the current file and forget everything we know about it (including the + filename and the current line number). + + + .. method:: TextFile.warn(msg[,line=None]) + + Print (to stderr) a warning message tied to the current logical line in the + current file. If the current logical line in the file spans multiple physical + lines, the warning refers to the whole range, such as ``"lines 3-5"``. If + *line* is supplied, it overrides the current line number; it may be a list or + tuple to indicate a range of physical lines, or an integer for a single + physical line. + + + .. method:: TextFile.readline() + + Read and return a single logical line from the current file (or from an internal + buffer if lines have previously been "unread" with :meth:`unreadline`). If the + *join_lines* option is true, this may involve reading multiple physical lines + concatenated into a single string. Updates the current line number, so calling + :meth:`warn` after :meth:`readline` emits a warning about the physical line(s) + just read. Returns ``None`` on end-of-file, since the empty string can occur + if *rstrip_ws* is true but *strip_blanks* is not. + + + .. method:: TextFile.readlines() + + Read and return the list of all logical lines remaining in the current file. + This updates the current line number to the last line of the file. + + + .. method:: TextFile.unreadline(line) + + Push *line* (a string) onto an internal buffer that will be checked by future + :meth:`readline` calls. Handy for implementing a parser with line-at-a-time + lookahead. Note that lines that are "unread" with :meth:`unreadline` are not + subsequently re-cleansed (whitespace stripped, or whatever) when read with + :meth:`readline`. If multiple calls are made to :meth:`unreadline` before a call + to :meth:`readline`, the lines will be returned most in most recent first order. + + +:mod:`distutils2.version` --- Version number classes +==================================================== + +.. module:: distutils2.version + :synopsis: implements classes that represent module version numbers. + + +.. % todo +.. % \section{Distutils Commands} +.. % +.. % This part of Distutils implements the various Distutils commands, such +.. % as \code{build}, \code{install} \&c. Each command is implemented as a +.. % separate module, with the command name as the name of the module. + + +:mod:`distutils2.cmd` --- Abstract base class for Distutils commands +==================================================================== + +.. module:: distutils2.cmd + :synopsis: This module provides the abstract base class Command. This class + is subclassed by the modules in the distutils.command subpackage. + + +This module supplies the abstract base class :class:`Command`. + + +.. class:: Command(dist) + + Abstract base class for defining command classes, the "worker bees" of the + Distutils. A useful analogy for command classes is to think of them as + subroutines with local variables called *options*. The options are declared + in :meth:`initialize_options` and defined (given their final values) in + :meth:`finalize_options`, both of which must be defined by every command + class. The distinction between the two is necessary because option values + might come from the outside world (command line, config file, ...), and any + options dependent on other options must be computed after these outside + influences have been processed --- hence :meth:`finalize_options`. The body + of the subroutine, where it does all its work based on the values of its + options, is the :meth:`run` method, which must also be implemented by every + command class. + + The class constructor takes a single argument *dist*, a :class:`Distribution` + instance. + + +.. % todo + +:mod:`distutils2.command.check` --- Check the metadata of a package +=================================================================== + +.. module:: distutils2.command.check + :synopsis: Check the metadata of a package + + +The ``check`` command performs some tests on the metadata of a package. +For example, it verifies that all required metadata are provided as +the arguments passed to the :func:`setup` function. + +.. % todo + +Creating a new Distutils command +================================ + +This section outlines the steps to create a new Distutils command. + +A new command lives in a module in the :mod:`distutils2.command` package. There +is a sample template in that directory called :file:`command_template`. Copy +this file to a new module with the same name as the new command you're +implementing. This module should implement a class with the same name as the +module (and the command). So, for instance, to create the command +``peel_banana`` (so that users can run ``setup.py peel_banana``), you'd copy +:file:`command_template` to :file:`distutils2/command/peel_banana.py`, then edit +it so that it's implementing the class :class:`peel_banana`, a subclass of +:class:`distutils2.cmd.Command`. + +Subclasses of :class:`Command` must define the following methods. + +.. method:: Command.initialize_options() + + Set default values for all the options that this command supports. Note that + these defaults may be overridden by other commands, by the setup script, by + config files, or by the command line. Thus, this is not the place to code + dependencies between options; generally, :meth:`initialize_options` + implementations are just a bunch of ``self.foo = None`` assignments. + + +.. method:: Command.finalize_options() + + Set final values for all the options that this command supports. This is + always called as late as possible, i.e. after any option assignments from the + command line or from other commands have been done. Thus, this is the place + to to code option dependencies: if *foo* depends on *bar*, then it is safe to + set *foo* from *bar* as long as *foo* still has the same value it was + assigned in :meth:`initialize_options`. + + +.. method:: Command.run() + + A command's raison d'etre: carry out the action it exists to perform, + controlled by the options initialized in :meth:`initialize_options`, + customized by other commands, the setup script, the command line, and config + files, and finalized in :meth:`finalize_options`. All terminal output and + filesystem interaction should be done by :meth:`run`. + + +.. attribute:: Command.sub_commands + + *sub_commands* formalizes the notion of a "family" of commands, + e.g. ``install`` as the parent with sub-commands ``install_lib``, + ``install_headers``, etc. The parent of a family of commands defines + *sub_commands* as a class attribute; it's a list of 2-tuples ``(command_name, + predicate)``, with *command_name* a string and *predicate* a function, a + string or ``None``. *predicate* is a method of the parent command that + determines whether the corresponding command is applicable in the current + situation. (E.g. ``install_headers`` is only applicable if we have any C + header files to install.) If *predicate* is ``None``, that command is always + applicable. + + *sub_commands* is usually defined at the *end* of a class, because + predicates can be methods of the class, so they must already have been + defined. The canonical example is the :command:`install` command. + + +:mod:`distutils2.command` --- Individual Distutils commands +=========================================================== + +.. module:: distutils2.command + :synopsis: This subpackage contains one module for each standard Distutils command. + + +.. % \subsubsection{Individual Distutils commands} +.. % todo + + +:mod:`distutils2.command.bdist` --- Build a binary installer +============================================================ + +.. module:: distutils2.command.bdist + :synopsis: Build a binary installer for a package + + +.. % todo + + +:mod:`distutils2.command.bdist_dumb` --- Build a "dumb" installer +================================================================= + +.. module:: distutils2.command.bdist_dumb + :synopsis: Build a "dumb" installer - a simple archive of files + + +.. % todo + + +:mod:`distutils2.command.bdist_msi` --- Build a Microsoft Installer binary package +================================================================================== + +.. module:: distutils2.command.bdist_msi + :synopsis: Build a binary distribution as a Windows MSI file + +.. class:: bdist_msi(Command) + + Builds a `Windows Installer`_ (.msi) binary package. + + .. _Windows Installer: http://msdn.microsoft.com/en-us/library/cc185688(VS.85).aspx + + In most cases, the ``bdist_msi`` installer is a better choice than the + ``bdist_wininst`` installer, because it provides better support for + Win64 platforms, allows administrators to perform non-interactive + installations, and allows installation through group policies. + + +:mod:`distutils2.command.bdist_wininst` --- Build a Windows installer +===================================================================== + +.. module:: distutils2.command.bdist_wininst + :synopsis: Build a Windows installer + + +.. % todo + + +:mod:`distutils2.command.sdist` --- Build a source distribution +=============================================================== + +.. module:: distutils2.command.sdist + :synopsis: Build a source distribution + + +.. % todo + + +:mod:`distutils2.command.build` --- Build all files of a package +================================================================ + +.. module:: distutils2.command.build + :synopsis: Build all files of a package + + +.. % todo + + +:mod:`distutils2.command.build_clib` --- Build any C libraries in a package +=========================================================================== + +.. module:: distutils2.command.build_clib + :synopsis: Build any C libraries in a package + + +.. % todo + + +:mod:`distutils2.command.build_ext` --- Build any extensions in a package +========================================================================= + +.. module:: distutils2.command.build_ext + :synopsis: Build any extensions in a package + + +.. % todo + + +:mod:`distutils2.command.build_py` --- Build the .py/.pyc files of a package +============================================================================ + +.. module:: distutils2.command.build_py + :synopsis: Build the .py/.pyc files of a package + + +.. class:: build_py(Command) + + +:mod:`distutils2.command.build_scripts` --- Build the scripts of a package +========================================================================== + +.. module:: distutils2.command.build_scripts + :synopsis: Build the scripts of a package + + +.. % todo + + +:mod:`distutils2.command.clean` --- Clean a package build area +============================================================== + +.. module:: distutils2.command.clean + :synopsis: Clean a package build area + + +.. % todo + + +:mod:`distutils2.command.config` --- Perform package configuration +================================================================== + +.. module:: distutils2.command.config + :synopsis: Perform package configuration + + +.. % todo + + +:mod:`distutils2.command.install` --- Install a package +======================================================= + +.. module:: distutils2.command.install + :synopsis: Install a package + + +.. % todo + + +:mod:`distutils2.command.install_data` --- Install data files from a package +============================================================================ + +.. module:: distutils2.command.install_data + :synopsis: Install data files from a package + + +.. % todo + + +:mod:`distutils2.command.install_headers` --- Install C/C++ header files from a package +======================================================================================= + +.. module:: distutils2.command.install_headers + :synopsis: Install C/C++ header files from a package + + +.. % todo + + +:mod:`distutils2.command.install_lib` --- Install library files from a package +============================================================================== + +.. module:: distutils2.command.install_lib + :synopsis: Install library files from a package + + +.. % todo + + +:mod:`distutils2.command.install_scripts` --- Install script files from a package +================================================================================= + +.. module:: distutils2.command.install_scripts + :synopsis: Install script files from a package + + +.. % todo + + +:mod:`distutils2.command.register` --- Register a module with the Python Package Index +====================================================================================== + +.. module:: distutils2.command.register + :synopsis: Register a module with the Python Package Index + + +The ``register`` command registers the package with the Python Package Index. +This is described in more detail in :PEP:`301`. + +.. % todo diff --git a/docs/source/distutils/builtdist.rst b/docs/source/distutils/builtdist.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/builtdist.rst @@ -0,0 +1,454 @@ +.. _built-dist: + +**************************** +Creating Built Distributions +**************************** + +A "built distribution" is what you're probably used to thinking of either as a +"binary package" or an "installer" (depending on your background). It's not +necessarily binary, though, because it might contain only Python source code +and/or byte-code; and we don't call it a package, because that word is already +spoken for in Python. (And "installer" is a term specific to the world of +mainstream desktop systems.) + +A built distribution is how you make life as easy as possible for installers of +your module distribution: for users of RPM-based Linux systems, it's a binary +RPM; for Windows users, it's an executable installer; for Debian-based Linux +users, it's a Debian package; and so forth. Obviously, no one person will be +able to create built distributions for every platform under the sun, so the +Distutils are designed to enable module developers to concentrate on their +specialty---writing code and creating source distributions---while an +intermediary species called *packagers* springs up to turn source distributions +into built distributions for as many platforms as there are packagers. + +Of course, the module developer could be his own packager; or the packager could +be a volunteer "out there" somewhere who has access to a platform which the +original developer does not; or it could be software periodically grabbing new +source distributions and turning them into built distributions for as many +platforms as the software has access to. Regardless of who they are, a packager +uses the setup script and the :command:`bdist` command family to generate built +distributions. + +As a simple example, if I run the following command in the Distutils source +tree:: + + python setup.py bdist + +then the Distutils builds my module distribution (the Distutils itself in this +case), does a "fake" installation (also in the :file:`build` directory), and +creates the default type of built distribution for my platform. The default +format for built distributions is a "dumb" tar file on Unix, and a simple +executable installer on Windows. (That tar file is considered "dumb" because it +has to be unpacked in a specific location to work.) + +Thus, the above command on a Unix system creates +:file:`Distutils-1.0.{plat}.tar.gz`; unpacking this tarball from the right place +installs the Distutils just as though you had downloaded the source distribution +and run ``python setup.py install``. (The "right place" is either the root of +the filesystem or Python's :file:`{prefix}` directory, depending on the options +given to the :command:`bdist_dumb` command; the default is to make dumb +distributions relative to :file:`{prefix}`.) + +Obviously, for pure Python distributions, this isn't any simpler than just +running ``python setup.py install``\ ---but for non-pure distributions, which +include extensions that would need to be compiled, it can mean the difference +between someone being able to use your extensions or not. And creating "smart" +built distributions, such as an RPM package or an executable installer for +Windows, is far more convenient for users even if your distribution doesn't +include any extensions. + +The :command:`bdist` command has a :option:`--formats` option, similar to the +:command:`sdist` command, which you can use to select the types of built +distribution to generate: for example, :: + + python setup.py bdist --format=zip + +would, when run on a Unix system, create :file:`Distutils-1.0.{plat}.zip`\ +---again, this archive would be unpacked from the root directory to install the +Distutils. + +The available formats for built distributions are: + ++-------------+------------------------------+---------+ +| Format | Description | Notes | ++=============+==============================+=========+ +| ``gztar`` | gzipped tar file | (1),(3) | +| | (:file:`.tar.gz`) | | ++-------------+------------------------------+---------+ +| ``ztar`` | compressed tar file | \(3) | +| | (:file:`.tar.Z`) | | ++-------------+------------------------------+---------+ +| ``tar`` | tar file (:file:`.tar`) | \(3) | ++-------------+------------------------------+---------+ +| ``zip`` | zip file (:file:`.zip`) | (2),(4) | ++-------------+------------------------------+---------+ +| ``rpm`` | RPM | \(5) | ++-------------+------------------------------+---------+ +| ``pkgtool`` | Solaris :program:`pkgtool` | | ++-------------+------------------------------+---------+ +| ``sdux`` | HP-UX :program:`swinstall` | | ++-------------+------------------------------+---------+ +| ``rpm`` | RPM | \(5) | ++-------------+------------------------------+---------+ +| ``wininst`` | self-extracting ZIP file for | \(4) | +| | Windows | | ++-------------+------------------------------+---------+ +| ``msi`` | Microsoft Installer. | | ++-------------+------------------------------+---------+ + + +Notes: + +(1) + default on Unix + +(2) + default on Windows + +(3) + requires external utilities: :program:`tar` and possibly one of :program:`gzip`, + :program:`bzip2`, or :program:`compress` + +(4) + requires either external :program:`zip` utility or :mod:`zipfile` module (part + of the standard Python library since Python 1.6) + +(5) + requires external :program:`rpm` utility, version 3.0.4 or better (use ``rpm + --version`` to find out which version you have) + +You don't have to use the :command:`bdist` command with the :option:`--formats` +option; you can also use the command that directly implements the format you're +interested in. Some of these :command:`bdist` "sub-commands" actually generate +several similar formats; for instance, the :command:`bdist_dumb` command +generates all the "dumb" archive formats (``tar``, ``ztar``, ``gztar``, and +``zip``), and :command:`bdist_rpm` generates both binary and source RPMs. The +:command:`bdist` sub-commands, and the formats generated by each, are: + ++--------------------------+-----------------------+ +| Command | Formats | ++==========================+=======================+ +| :command:`bdist_dumb` | tar, ztar, gztar, zip | ++--------------------------+-----------------------+ +| :command:`bdist_rpm` | rpm, srpm | ++--------------------------+-----------------------+ +| :command:`bdist_wininst` | wininst | ++--------------------------+-----------------------+ +| :command:`bdist_msi` | msi | ++--------------------------+-----------------------+ + +The following sections give details on the individual :command:`bdist_\*` +commands. + + +.. _creating-dumb: + +Creating dumb built distributions +================================= + +.. XXX Need to document absolute vs. prefix-relative packages here, but first + I have to implement it! + + +.. _creating-rpms: + +Creating RPM packages +===================== + +The RPM format is used by many popular Linux distributions, including Red Hat, +SuSE, and Mandrake. If one of these (or any of the other RPM-based Linux +distributions) is your usual environment, creating RPM packages for other users +of that same distribution is trivial. Depending on the complexity of your module +distribution and differences between Linux distributions, you may also be able +to create RPMs that work on different RPM-based distributions. + +The usual way to create an RPM of your module distribution is to run the +:command:`bdist_rpm` command:: + + python setup.py bdist_rpm + +or the :command:`bdist` command with the :option:`--format` option:: + + python setup.py bdist --formats=rpm + +The former allows you to specify RPM-specific options; the latter allows you to +easily specify multiple formats in one run. If you need to do both, you can +explicitly specify multiple :command:`bdist_\*` commands and their options:: + + python setup.py bdist_rpm --packager="John Doe " \ + bdist_wininst --target-version="2.0" + +Creating RPM packages is driven by a :file:`.spec` file, much as using the +Distutils is driven by the setup script. To make your life easier, the +:command:`bdist_rpm` command normally creates a :file:`.spec` file based on the +information you supply in the setup script, on the command line, and in any +Distutils configuration files. Various options and sections in the +:file:`.spec` file are derived from options in the setup script as follows: + ++------------------------------------------+----------------------------------------------+ +| RPM :file:`.spec` file option or section | Distutils setup script option | ++==========================================+==============================================+ +| Name | :option:`name` | ++------------------------------------------+----------------------------------------------+ +| Summary (in preamble) | :option:`description` | ++------------------------------------------+----------------------------------------------+ +| Version | :option:`version` | ++------------------------------------------+----------------------------------------------+ +| Vendor | :option:`author` and :option:`author_email`, | +| | or --- & :option:`maintainer` and | +| | :option:`maintainer_email` | ++------------------------------------------+----------------------------------------------+ +| Copyright | :option:`license` | ++------------------------------------------+----------------------------------------------+ +| Url | :option:`url` | ++------------------------------------------+----------------------------------------------+ +| %description (section) | :option:`long_description` | ++------------------------------------------+----------------------------------------------+ + +Additionally, there are many options in :file:`.spec` files that don't have +corresponding options in the setup script. Most of these are handled through +options to the :command:`bdist_rpm` command as follows: + ++-------------------------------+-----------------------------+-------------------------+ +| RPM :file:`.spec` file option | :command:`bdist_rpm` option | default value | +| or section | | | ++===============================+=============================+=========================+ +| Release | :option:`release` | "1" | ++-------------------------------+-----------------------------+-------------------------+ +| Group | :option:`group` | "Development/Libraries" | ++-------------------------------+-----------------------------+-------------------------+ +| Vendor | :option:`vendor` | (see above) | ++-------------------------------+-----------------------------+-------------------------+ +| Packager | :option:`packager` | (none) | ++-------------------------------+-----------------------------+-------------------------+ +| Provides | :option:`provides` | (none) | ++-------------------------------+-----------------------------+-------------------------+ +| Requires | :option:`requires` | (none) | ++-------------------------------+-----------------------------+-------------------------+ +| Conflicts | :option:`conflicts` | (none) | ++-------------------------------+-----------------------------+-------------------------+ +| Obsoletes | :option:`obsoletes` | (none) | ++-------------------------------+-----------------------------+-------------------------+ +| Distribution | :option:`distribution_name` | (none) | ++-------------------------------+-----------------------------+-------------------------+ +| BuildRequires | :option:`build_requires` | (none) | ++-------------------------------+-----------------------------+-------------------------+ +| Icon | :option:`icon` | (none) | ++-------------------------------+-----------------------------+-------------------------+ + +Obviously, supplying even a few of these options on the command line would be +tedious and error-prone, so it's usually best to put them in the setup +configuration file, :file:`setup.cfg`\ ---see section :ref:`setup-config`. If +you distribute or package many Python module distributions, you might want to +put options that apply to all of them in your personal Distutils configuration +file (:file:`~/.pydistutils.cfg`). If you want to temporarily disable +this file, you can pass the --no-user-cfg option to setup.py. + +There are three steps to building a binary RPM package, all of which are +handled automatically by the Distutils: + +#. create a :file:`.spec` file, which describes the package (analogous to the + Distutils setup script; in fact, much of the information in the setup script + winds up in the :file:`.spec` file) + +#. create the source RPM + +#. create the "binary" RPM (which may or may not contain binary code, depending + on whether your module distribution contains Python extensions) + +Normally, RPM bundles the last two steps together; when you use the Distutils, +all three steps are typically bundled together. + +If you wish, you can separate these three steps. You can use the +:option:`--spec-only` option to make :command:`bdist_rpm` just create the +:file:`.spec` file and exit; in this case, the :file:`.spec` file will be +written to the "distribution directory"---normally :file:`dist/`, but +customizable with the :option:`--dist-dir` option. (Normally, the :file:`.spec` +file winds up deep in the "build tree," in a temporary directory created by +:command:`bdist_rpm`.) + +.. % \XXX{this isn't implemented yet---is it needed?!} +.. % You can also specify a custom \file{.spec} file with the +.. % \longprogramopt{spec-file} option; used in conjunction with +.. % \longprogramopt{spec-only}, this gives you an opportunity to customize +.. % the \file{.spec} file manually: +.. % +.. % \ begin{verbatim} +.. % > python setup.py bdist_rpm --spec-only +.. % # ...edit dist/FooBar-1.0.spec +.. % > python setup.py bdist_rpm --spec-file=dist/FooBar-1.0.spec +.. % \ end{verbatim} +.. % +.. % (Although a better way to do this is probably to override the standard +.. % \command{bdist\_rpm} command with one that writes whatever else you want +.. % to the \file{.spec} file.) + + +.. _creating-wininst: + +Creating Windows Installers +=========================== + +Executable installers are the natural format for binary distributions on +Windows. They display a nice graphical user interface, display some information +about the module distribution to be installed taken from the metadata in the +setup script, let the user select a few options, and start or cancel the +installation. + +Since the metadata is taken from the setup script, creating Windows installers +is usually as easy as running:: + + python setup.py bdist_wininst + +or the :command:`bdist` command with the :option:`--formats` option:: + + python setup.py bdist --formats=wininst + +If you have a pure module distribution (only containing pure Python modules and +packages), the resulting installer will be version independent and have a name +like :file:`foo-1.0.win32.exe`. These installers can even be created on Unix +platforms or Mac OS X. + +If you have a non-pure distribution, the extensions can only be created on a +Windows platform, and will be Python version dependent. The installer filename +will reflect this and now has the form :file:`foo-1.0.win32-py2.0.exe`. You +have to create a separate installer for every Python version you want to +support. + +.. TODO Add :term: markup to bytecode when merging into the stdlib + +The installer will try to compile pure modules into bytecode after installation +on the target system in normal and optimizing mode. If you don't want this to +happen for some reason, you can run the :command:`bdist_wininst` command with +the :option:`--no-target-compile` and/or the :option:`--no-target-optimize` +option. + +By default the installer will display the cool "Python Powered" logo when it is +run, but you can also supply your own 152x261 bitmap which must be a Windows +:file:`.bmp` file with the :option:`--bitmap` option. + +The installer will also display a large title on the desktop background window +when it is run, which is constructed from the name of your distribution and the +version number. This can be changed to another text by using the +:option:`--title` option. + +The installer file will be written to the "distribution directory" --- normally +:file:`dist/`, but customizable with the :option:`--dist-dir` option. + +.. _cross-compile-windows: + +Cross-compiling on Windows +========================== + +Starting with Python 2.6, distutils is capable of cross-compiling between +Windows platforms. In practice, this means that with the correct tools +installed, you can use a 32bit version of Windows to create 64bit extensions +and vice-versa. + +To build for an alternate platform, specify the :option:`--plat-name` option +to the build command. Valid values are currently 'win32', 'win-amd64' and +'win-ia64'. For example, on a 32bit version of Windows, you could execute:: + + python setup.py build --plat-name=win-amd64 + +to build a 64bit version of your extension. The Windows Installers also +support this option, so the command:: + + python setup.py build --plat-name=win-amd64 bdist_wininst + +would create a 64bit installation executable on your 32bit version of Windows. + +To cross-compile, you must download the Python source code and cross-compile +Python itself for the platform you are targetting - it is not possible from a +binary installtion of Python (as the .lib etc file for other platforms are +not included.) In practice, this means the user of a 32 bit operating +system will need to use Visual Studio 2008 to open the +:file:`PCBuild/PCbuild.sln` solution in the Python source tree and build the +"x64" configuration of the 'pythoncore' project before cross-compiling +extensions is possible. + +Note that by default, Visual Studio 2008 does not install 64bit compilers or +tools. You may need to reexecute the Visual Studio setup process and select +these tools (using Control Panel->[Add/Remove] Programs is a convenient way to +check or modify your existing install.) + +.. _postinstallation-script: + +The Postinstallation script +--------------------------- + +Starting with Python 2.3, a postinstallation script can be specified with the +:option:`--install-script` option. The basename of the script must be +specified, and the script filename must also be listed in the scripts argument +to the setup function. + +This script will be run at installation time on the target system after all the +files have been copied, with ``argv[1]`` set to :option:`-install`, and again at +uninstallation time before the files are removed with ``argv[1]`` set to +:option:`-remove`. + +The installation script runs embedded in the windows installer, every output +(``sys.stdout``, ``sys.stderr``) is redirected into a buffer and will be +displayed in the GUI after the script has finished. + +Some functions especially useful in this context are available as additional +built-in functions in the installation script. + + +.. function:: directory_created(path) + file_created(path) + + These functions should be called when a directory or file is created by the + postinstall script at installation time. It will register *path* with the + uninstaller, so that it will be removed when the distribution is uninstalled. + To be safe, directories are only removed if they are empty. + + +.. function:: get_special_folder_path(csidl_string) + + This function can be used to retrieve special folder locations on Windows like + the Start Menu or the Desktop. It returns the full path to the folder. + *csidl_string* must be one of the following strings:: + + "CSIDL_APPDATA" + + "CSIDL_COMMON_STARTMENU" + "CSIDL_STARTMENU" + + "CSIDL_COMMON_DESKTOPDIRECTORY" + "CSIDL_DESKTOPDIRECTORY" + + "CSIDL_COMMON_STARTUP" + "CSIDL_STARTUP" + + "CSIDL_COMMON_PROGRAMS" + "CSIDL_PROGRAMS" + + "CSIDL_FONTS" + + If the folder cannot be retrieved, :exc:`OSError` is raised. + + Which folders are available depends on the exact Windows version, and probably + also the configuration. For details refer to Microsoft's documentation of the + c:function:`SHGetSpecialFolderPath` function. + + +.. function:: create_shortcut(target, description, filename[, arguments[, workdir[, iconpath[, iconindex]]]]) + + This function creates a shortcut. *target* is the path to the program to be + started by the shortcut. *description* is the description of the shortcut. + *filename* is the title of the shortcut that the user will see. *arguments* + specifies the command-line arguments, if any. *workdir* is the working directory + for the program. *iconpath* is the file containing the icon for the shortcut, + and *iconindex* is the index of the icon in the file *iconpath*. Again, for + details consult the Microsoft documentation for the :class:`IShellLink` + interface. + + +Vista User Access Control (UAC) +=============================== + +Starting with Python 2.6, bdist_wininst supports a :option:`--user-access-control` +option. The default is 'none' (meaning no UAC handling is done), and other +valid values are 'auto' (meaning prompt for UAC elevation if Python was +installed for all users) and 'force' (meaning always prompt for elevation). diff --git a/docs/source/command_hooks.rst b/docs/source/distutils/commandhooks.rst rename from docs/source/command_hooks.rst rename to docs/source/distutils/commandhooks.rst diff --git a/docs/source/distutils/commandref.rst b/docs/source/distutils/commandref.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/commandref.rst @@ -0,0 +1,60 @@ +.. _reference: + +***************** +Command Reference +***************** + +.. % \section{Building modules: the \protect\command{build} command family} +.. % \label{build-cmds} +.. % \subsubsection{\protect\command{build}} +.. % \label{build-cmd} +.. % \subsubsection{\protect\command{build\_py}} +.. % \label{build-py-cmd} +.. % \subsubsection{\protect\command{build\_ext}} +.. % \label{build-ext-cmd} +.. % \subsubsection{\protect\command{build\_clib}} +.. % \label{build-clib-cmd} + + +.. _install-cmd: + +Installing modules: the :command:`install` command family +========================================================= + +The install command ensures that the build commands have been run and then runs +the subcommands :command:`install_lib`, :command:`install_data` and +:command:`install_scripts`. + +.. % \subsubsection{\protect\command{install\_lib}} +.. % \label{install-lib-cmd} + + +.. _install-data-cmd: + +:command:`install_data` +----------------------- + +This command installs all data files provided with the distribution. + + +.. _install-scripts-cmd: + +:command:`install_scripts` +-------------------------- + +This command installs all (Python) scripts in the distribution. + +.. % \subsection{Cleaning up: the \protect\command{clean} command} +.. % \label{clean-cmd} + + +.. % \section{Creating a built distribution: the +.. % \protect\command{bdist} command family} +.. % \label{bdist-cmds} + +.. % \subsection{\protect\command{bdist}} +.. % \subsection{\protect\command{bdist\_dumb}} +.. % \subsection{\protect\command{bdist\_rpm}} +.. % \subsection{\protect\command{bdist\_wininst}} + + diff --git a/docs/source/distutils/configfile.rst b/docs/source/distutils/configfile.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/configfile.rst @@ -0,0 +1,131 @@ +.. _setup-config: + +************************************ +Writing the Setup Configuration File +************************************ + +Often, it's not possible to write down everything needed to build a distribution +*a priori*: you may need to get some information from the user, or from the +user's system, in order to proceed. As long as that information is fairly +simple---a list of directories to search for C header files or libraries, for +example---then providing a configuration file, :file:`setup.cfg`, for users to +edit is a cheap and easy way to solicit it. Configuration files also let you +provide default values for any command option, which the installer can then +override either on the command line or by editing the config file. + +The setup configuration file is a useful middle-ground between the setup script +---which, ideally, would be opaque to installers [#]_---and the command line to +the setup script, which is outside of your control and entirely up to the +installer. In fact, :file:`setup.cfg` (and any other Distutils configuration +files present on the target system) are processed after the contents of the +setup script, but before the command line. This has several useful +consequences: + +.. If you have more advanced needs, such as determining which extensions to + build based on what capabilities are present on the target system, then you + need the Distutils auto-configuration facility. This started to appear in + Distutils 0.9 but, as of this writing, isn't mature or stable enough yet + for real-world use. + +* installers can override some of what you put in :file:`setup.py` by editing + :file:`setup.cfg` + +* you can provide non-standard defaults for options that are not easily set in + :file:`setup.py` + +* installers can override anything in :file:`setup.cfg` using the command-line + options to :file:`setup.py` + +The basic syntax of the configuration file is simple:: + + [command] + option = value + ... + +where *command* is one of the Distutils commands (e.g. :command:`build_py`, +:command:`install`), and *option* is one of the options that command supports. +Any number of options can be supplied for each command, and any number of +command sections can be included in the file. Blank lines are ignored, as are +comments, which run from a ``'#'`` character until the end of the line. Long +option values can be split across multiple lines simply by indenting the +continuation lines. + +You can find out the list of options supported by a particular command with the +universal :option:`--help` option, e.g. :: + + > python setup.py --help build_ext + [...] + Options for 'build_ext' command: + --build-lib (-b) directory for compiled extension modules + --build-temp (-t) directory for temporary files (build by-products) + --inplace (-i) ignore build-lib and put compiled extensions into the + source directory alongside your pure Python modules + --include-dirs (-I) list of directories to search for header files + --define (-D) C preprocessor macros to define + --undef (-U) C preprocessor macros to undefine + --swig-opts list of SWIG command-line options + [...] + +.. XXX do we want to support ``setup.py --help metadata``? + +Note that an option spelled :option:`--foo-bar` on the command line is spelled +:option:`foo_bar` in configuration files. + +For example, say you want your extensions to be built "in-place"---that is, you +have an extension :mod:`pkg.ext`, and you want the compiled extension file +(:file:`ext.so` on Unix, say) to be put in the same source directory as your +pure Python modules :mod:`pkg.mod1` and :mod:`pkg.mod2`. You can always use the +:option:`--inplace` option on the command line to ensure this:: + + python setup.py build_ext --inplace + +But this requires that you always specify the :command:`build_ext` command +explicitly, and remember to provide :option:`--inplace`. An easier way is to +"set and forget" this option, by encoding it in :file:`setup.cfg`, the +configuration file for this distribution:: + + [build_ext] + inplace = 1 + +This will affect all builds of this module distribution, whether or not you +explicitly specify :command:`build_ext`. If you include :file:`setup.cfg` in +your source distribution, it will also affect end-user builds---which is +probably a bad idea for this option, since always building extensions in-place +would break installation of the module distribution. In certain peculiar cases, +though, modules are built right in their installation directory, so this is +conceivably a useful ability. (Distributing extensions that expect to be built +in their installation directory is almost always a bad idea, though.) + +Another example: certain commands take a lot of options that don't change from +run to run; for example, :command:`bdist_rpm` needs to know everything required +to generate a "spec" file for creating an RPM distribution. Some of this +information comes from the setup script, and some is automatically generated by +the Distutils (such as the list of files installed). But some of it has to be +supplied as options to :command:`bdist_rpm`, which would be very tedious to do +on the command line for every run. Hence, here is a snippet from the Distutils' +own :file:`setup.cfg`:: + + [bdist_rpm] + release = 1 + packager = Greg Ward + doc_files = CHANGES.txt + README.txt + USAGE.txt + doc/ + examples/ + +Note that the :option:`doc_files` option is simply a whitespace-separated string +split across multiple lines for readability. + + +.. seealso:: + + :ref:`inst-config-syntax` in "Installing Python Projects" + More information on the configuration files is available in the manual for + system administrators. + + +.. rubric:: Footnotes + +.. [#] This ideal probably won't be achieved until auto-configuration is fully + supported by the Distutils. diff --git a/docs/source/distutils/examples.rst b/docs/source/distutils/examples.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/examples.rst @@ -0,0 +1,332 @@ +.. _examples: + +******** +Examples +******** + +This chapter provides a number of basic examples to help get started with +Distutils2. + + +.. _pure-mod: + +Pure Python distribution (by module) +==================================== + +If you're just distributing a couple of modules, especially if they don't live +in a particular package, you can specify them individually using the +:option:`py_modules` option in the setup script. + +In the simplest case, you'll have two files to worry about: a setup script and +the single module you're distributing, :file:`foo.py` in this example:: + + / + setup.py + foo.py + +(In all diagrams in this section, ** will refer to the distribution root +directory.) A minimal setup script to describe this situation would be:: + + from distutils2.core import setup + setup(name='foo', + version='1.0', + py_modules=['foo']) + +Note that the name of the distribution is specified independently with the +:option:`name` option, and there's no rule that says it has to be the same as +the name of the sole module in the distribution (although that's probably a good +convention to follow). However, the distribution name is used to generate +filenames, so you should stick to letters, digits, underscores, and hyphens. + +Since :option:`py_modules` is a list, you can of course specify multiple +modules, e.g. if you're distributing modules :mod:`foo` and :mod:`bar`, your +setup might look like this:: + + / + setup.py + foo.py + bar.py + +and the setup script might be :: + + from distutils2.core import setup + setup(name='foobar', + version='1.0', + py_modules=['foo', 'bar']) + +You can put module source files into another directory, but if you have enough +modules to do that, it's probably easier to specify modules by package rather +than listing them individually. + + +.. _pure-pkg: + +Pure Python distribution (by package) +===================================== + +If you have more than a couple of modules to distribute, especially if they are +in multiple packages, it's probably easier to specify whole packages rather than +individual modules. This works even if your modules are not in a package; you +can just tell the Distutils to process modules from the root package, and that +works the same as any other package (except that you don't have to have an +:file:`__init__.py` file). + +The setup script from the last example could also be written as :: + + from distutils2.core import setup + setup(name='foobar', + version='1.0', + packages=['']) + +(The empty string stands for the root package.) + +If those two files are moved into a subdirectory, but remain in the root +package, e.g.:: + + / + setup.py + src/ + foo.py + bar.py + +then you would still specify the root package, but you have to tell the +Distutils where source files in the root package live:: + + from distutils2.core import setup + setup(name='foobar', + version='1.0', + package_dir={'': 'src'}, + packages=['']) + +More typically, though, you will want to distribute multiple modules in the same +package (or in sub-packages). For example, if the :mod:`foo` and :mod:`bar` +modules belong in package :mod:`foobar`, one way to lay out your source tree is + +:: + + / + setup.py + foobar/ + __init__.py + foo.py + bar.py + +This is in fact the default layout expected by the Distutils, and the one that +requires the least work to describe in your setup script:: + + from distutils2.core import setup + setup(name='foobar', + version='1.0', + packages=['foobar']) + +If you want to put modules in directories not named for their package, then you +need to use the :option:`package_dir` option again. For example, if the +:file:`src` directory holds modules in the :mod:`foobar` package:: + + / + setup.py + src/ + __init__.py + foo.py + bar.py + +an appropriate setup script would be :: + + from distutils2.core import setup + setup(name='foobar', + version='1.0', + package_dir={'foobar': 'src'}, + packages=['foobar']) + +Or, you might put modules from your main package right in the distribution +root:: + + / + setup.py + __init__.py + foo.py + bar.py + +in which case your setup script would be :: + + from distutils2.core import setup + setup(name='foobar', + version='1.0', + package_dir={'foobar': ''}, + packages=['foobar']) + +(The empty string also stands for the current directory.) + +If you have sub-packages, they must be explicitly listed in :option:`packages`, +but any entries in :option:`package_dir` automatically extend to sub-packages. +(In other words, the Distutils does *not* scan your source tree, trying to +figure out which directories correspond to Python packages by looking for +:file:`__init__.py` files.) Thus, if the default layout grows a sub-package:: + + / + setup.py + foobar/ + __init__.py + foo.py + bar.py + subfoo/ + __init__.py + blah.py + +then the corresponding setup script would be :: + + from distutils2.core import setup + setup(name='foobar', + version='1.0', + packages=['foobar', 'foobar.subfoo']) + +(Again, the empty string in :option:`package_dir` stands for the current +directory.) + + +.. _single-ext: + +Single extension module +======================= + +Extension modules are specified using the :option:`ext_modules` option. +:option:`package_dir` has no effect on where extension source files are found; +it only affects the source for pure Python modules. The simplest case, a +single extension module in a single C source file, is:: + + / + setup.py + foo.c + +If the :mod:`foo` extension belongs in the root package, the setup script for +this could be :: + + from distutils2.core import setup, Extension + setup(name='foobar', + version='1.0', + ext_modules=[Extension('foo', ['foo.c'])]) + +If the extension actually belongs in a package, say :mod:`foopkg`, then + +With exactly the same source tree layout, this extension can be put in the +:mod:`foopkg` package simply by changing the name of the extension:: + + from distutils2.core import setup, Extension + setup(name='foobar', + version='1.0', + packages=['foopkg'], + ext_modules=[Extension('foopkg.foo', ['foo.c'])]) + + +Checking metadata +================= + +The ``check`` command allows you to verify if your project's metadata +meets the minimum requirements to build a distribution. + +To run it, just call it using your :file:`setup.py` script. If something is +missing, ``check`` will display a warning. + +Let's take an example with a simple script:: + + from distutils2.core import setup + + setup(name='foobar') + +Running the ``check`` command will display some warnings:: + + $ python setup.py check + running check + warning: check: missing required metadata: version, home_page + warning: check: missing metadata: either (author and author_email) or + (maintainer and maintainer_email) must be supplied + + +If you use the reStructuredText syntax in the ``long_description`` field and +`Docutils `_ is installed you can check if +the syntax is fine with the ``check`` command, using the ``restructuredtext`` +option. + +For example, if the :file:`setup.py` script is changed like this:: + + from distutils2.core import setup + + desc = """\ + Welcome to foobar! + =============== + + This is the description of the ``foobar`` project. + """ + + setup(name='foobar', + version='1.0', + author=u'Tarek Ziad??', + author_email='tarek at ziade.org', + summary='Foobar utilities' + description=desc, + home_page='http://example.com') + +Where the long description is broken, ``check`` will be able to detect it +by using the :mod:`docutils` parser:: + + $ python setup.py check --restructuredtext + running check + warning: check: Title underline too short. (line 2) + warning: check: Could not finish the parsing. + + +.. _reading-metadata: + +Reading the metadata +==================== + +The :func:`distutils2.core.setup` function provides a command-line interface +that allows you to query the metadata fields of a project through the +:file:`setup.py` script of a given project:: + + $ python setup.py --name + foobar + +This call reads the ``name`` metadata by running the +:func:`distutils2.core.setup` function. When a source or binary +distribution is created with Distutils, the metadata fields are written +in a static file called :file:`PKG-INFO`. When a Distutils-based project is +installed in Python, the :file:`PKG-INFO` file is copied alongside the modules +and packages of the distribution under :file:`NAME-VERSION-pyX.X.egg-info`, +where ``NAME`` is the name of the project, ``VERSION`` its version as defined +in the Metadata, and ``pyX.X`` the major and minor version of Python like +``2.7`` or ``3.2``. + +You can read back this static file, by using the +:class:`distutils2.dist.DistributionMetadata` class and its +:func:`read_pkg_file` method:: + + >>> from distutils2.dist import DistributionMetadata + >>> metadata = DistributionMetadata() + >>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info')) + >>> metadata.name + 'distribute' + >>> metadata.version + '0.6.8' + >>> metadata.description + 'Easily download, build, install, upgrade, and uninstall Python packages' + +Notice that the class can also be instantiated with a metadata file path to +loads its values:: + + >>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info' + >>> DistributionMetadata(pkg_info_path).name + 'distribute' + + +.. XXX These comments have been here for at least ten years. Write the + sections or delete the comments (we can maybe ask Greg Ward about + the planned contents). (Unindent to make them section titles) + + .. multiple-ext:: + + Multiple extension modules + ========================== + + Putting it all together + ======================= diff --git a/docs/source/distutils/extending.rst b/docs/source/distutils/extending.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/extending.rst @@ -0,0 +1,95 @@ +.. _extending-distutils: + +******************* +Extending Distutils +******************* + +Distutils can be extended in various ways. Most extensions take the form of new +commands or replacements for existing commands. New commands may be written to +support new types of platform-specific packaging, for example, while +replacements for existing commands may be made to modify details of how the +command operates on a package. + +Most extensions of the distutils are made within :file:`setup.py` scripts that +want to modify existing commands; many simply add a few file extensions that +should be copied into packages in addition to :file:`.py` files as a +convenience. + +Most distutils command implementations are subclasses of the +:class:`distutils2.cmd.Command` class. New commands may directly inherit from +:class:`Command`, while replacements often derive from :class:`Command` +indirectly, directly subclassing the command they are replacing. Commands are +required to derive from :class:`Command`. + +.. .. _extend-existing: + Extending existing commands + =========================== + + +.. .. _new-commands: + Writing new commands + ==================== + + +Integrating new commands +======================== + +There are different ways to integrate new command implementations into +distutils. The most difficult is to lobby for the inclusion of the new features +in distutils itself, and wait for (and require) a version of Python that +provides that support. This is really hard for many reasons. + +The most common, and possibly the most reasonable for most needs, is to include +the new implementations with your :file:`setup.py` script, and cause the +:func:`distutils2.core.setup` function use them:: + + from distutils2.core import setup + from distutils2.command.build_py import build_py as _build_py + + class build_py(_build_py): + """Specialized Python source builder.""" + + # implement whatever needs to be different... + + setup(..., cmdclass={'build_py': build_py}) + +This approach is most valuable if the new implementations must be used to use a +particular package, as everyone interested in the package will need to have the +new command implementation. + +Beginning with Python 2.4, a third option is available, intended to allow new +commands to be added which can support existing :file:`setup.py` scripts without +requiring modifications to the Python installation. This is expected to allow +third-party extensions to provide support for additional packaging systems, but +the commands can be used for anything distutils commands can be used for. A new +configuration option, :option:`command_packages` (command-line option +:option:`--command-packages`), can be used to specify additional packages to be +searched for modules implementing commands. Like all distutils options, this +can be specified on the command line or in a configuration file. This option +can only be set in the ``[global]`` section of a configuration file, or before +any commands on the command line. If set in a configuration file, it can be +overridden from the command line; setting it to an empty string on the command +line causes the default to be used. This should never be set in a configuration +file provided with a package. + +This new option can be used to add any number of packages to the list of +packages searched for command implementations; multiple package names should be +separated by commas. When not specified, the search is only performed in the +:mod:`distutils2.command` package. When :file:`setup.py` is run with the option +:option:`--command-packages` :option:`distcmds,buildcmds`, however, the packages +:mod:`distutils2.command`, :mod:`distcmds`, and :mod:`buildcmds` will be searched +in that order. New commands are expected to be implemented in modules of the +same name as the command by classes sharing the same name. Given the example +command-line option above, the command :command:`bdist_openpkg` could be +implemented by the class :class:`distcmds.bdist_openpkg.bdist_openpkg` or +:class:`buildcmds.bdist_openpkg.bdist_openpkg`. + + +Adding new distribution types +============================= + +Commands that create distributions (files in the :file:`dist/` directory) need +to add ``(command, filename)`` pairs to ``self.distribution.dist_files`` so that +:command:`upload` can upload it to PyPI. The *filename* in the pair contains no +path information, only the name of the file itself. In dry-run mode, pairs +should still be added to represent what would have been created. diff --git a/docs/source/index.rst b/docs/source/distutils/index.rst copy from docs/source/index.rst copy to docs/source/distutils/index.rst --- a/docs/source/index.rst +++ b/docs/source/distutils/index.rst @@ -1,29 +1,33 @@ -.. Distutils2 documentation master file, created by - sphinx-quickstart on Sun Feb 28 15:23:06 2010. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +.. _distutils-index: -Welcome to Distutils2's documentation! -====================================== +############################### + Distributing Python Projects +############################### -Contents: +:Authors: Greg Ward, Anthony Baxter and Distutils2 contributors +:Email: distutils-sig at python.org +:Release: |version| +:Date: |today| + +This document describes the Python Distribution Utilities ("Distutils2") from +the developer's point of view, describing how to use the Distutils to make +Python applications, packages or modules easily available to a wider audience +with very little overhead for build/release/install mechanics. .. toctree:: :maxdepth: 2 + :numbered: - metadata - pkgutil - depgraph - commands - command_hooks - test_framework - projects-index - version - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - + introduction + setupscript + configfile + sourcedist + builtdist + packageindex + uploading + examples + extending + commandhooks + commandref + newcommands + apiref diff --git a/docs/source/distutils/introduction.rst b/docs/source/distutils/introduction.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/introduction.rst @@ -0,0 +1,193 @@ +.. _distutils-intro: + +***************************** +An Introduction to Distutils2 +***************************** + +This document covers using Distutils2 to distribute your Python modules, +concentrating on the role of developer/distributor; if you're looking for +information on installing Python modules, you should refer to the +:ref:`install-index` chapter. + +Throughout this documentation, the terms "Distutils", "the Distutils" and +"Distutils2" will be used with the same meaning. + +.. _distutils-concepts: + +Concepts & Terminology +====================== + +Using the Distutils is quite simple, both for module developers and for +users/administrators installing third-party modules. As a developer, your +responsibilities (apart from writing solid, well-documented and well-tested +code, of course!) are: + +* write a setup script (:file:`setup.py` by convention) + +* (optional) write a setup configuration file + +* create a source distribution + +* (optional) create one or more built (binary) distributions + +Each of these tasks is covered in this document. + +Not all module developers have access to a multitude of platforms, so it's not +always feasible to expect them to create a multitude of built distributions. It +is hoped that a class of intermediaries, called *packagers*, will arise to +address this need. Packagers will take source distributions released by module +developers, build them on one or more platforms, and release the resulting built +distributions. Thus, users on the most popular platforms will be able to +install most popular Python module distributions in the most natural way for +their platform, without having to run a single setup script or compile a line of +code. + + +.. _distutils-simple-example: + +A Simple Example +================ + +The setup script is usually quite simple, although since it's written in Python, +there are no arbitrary limits to what you can do with it, though you should be +careful about putting arbitrarily expensive operations in your setup script. +Unlike, say, Autoconf-style configure scripts, the setup script may be run +multiple times in the course of building and installing your module +distribution. + +If all you want to do is distribute a module called :mod:`foo`, contained in a +file :file:`foo.py`, then your setup script can be as simple as this:: + + from distutils2.core import setup + setup(name='foo', + version='1.0', + py_modules=['foo']) + +Some observations: + +* most information that you supply to the Distutils is supplied as keyword + arguments to the :func:`setup` function + +* those keyword arguments fall into two categories: package metadata (name, + version number) and information about what's in the package (a list of pure + Python modules, in this case) + +* modules are specified by module name, not filename (the same will hold true + for packages and extensions) + +* it's recommended that you supply a little more metadata, in particular your + name, email address and a URL for the project (see section :ref:`setup-script` + for an example) + +To create a source distribution for this module, you would create a setup +script, :file:`setup.py`, containing the above code, and run:: + + python setup.py sdist + +which will create an archive file (e.g., tarball on Unix, ZIP file on Windows) +containing your setup script :file:`setup.py`, and your module :file:`foo.py`. +The archive file will be named :file:`foo-1.0.tar.gz` (or :file:`.zip`), and +will unpack into a directory :file:`foo-1.0`. + +If an end-user wishes to install your :mod:`foo` module, all she has to do is +download :file:`foo-1.0.tar.gz` (or :file:`.zip`), unpack it, and---from the +:file:`foo-1.0` directory---run :: + + python setup.py install + +which will ultimately copy :file:`foo.py` to the appropriate directory for +third-party modules in their Python installation. + +This simple example demonstrates some fundamental concepts of the Distutils. +First, both developers and installers have the same basic user interface, i.e. +the setup script. The difference is which Distutils *commands* they use: the +:command:`sdist` command is almost exclusively for module developers, while +:command:`install` is more often for installers (although most developers will +want to install their own code occasionally). + +If you want to make things really easy for your users, you can create one or +more built distributions for them. For instance, if you are running on a +Windows machine, and want to make things easy for other Windows users, you can +create an executable installer (the most appropriate type of built distribution +for this platform) with the :command:`bdist_wininst` command. For example:: + + python setup.py bdist_wininst + +will create an executable installer, :file:`foo-1.0.win32.exe`, in the current +directory. You can find out what distribution formats are available at any time +by running :: + + python setup.py bdist --help-formats + + +.. _python-terms: + +General Python terminology +========================== + +If you're reading this document, you probably have a good idea of what modules, +extensions, and so forth are. Nevertheless, just to be sure that everyone is +operating from a common starting point, we offer the following glossary of +common Python terms: + +module + the basic unit of code reusability in Python: a block of code imported by some + other code. Three types of modules concern us here: pure Python modules, + extension modules, and packages. + +pure Python module + a module written in Python and contained in a single :file:`.py` file (and + possibly associated :file:`.pyc` and/or :file:`.pyo` files). Sometimes referred + to as a "pure module." + +extension module + a module written in the low-level language of the Python implementation: C/C++ + for Python, Java for Jython. Typically contained in a single dynamically + loadable pre-compiled file, e.g. a shared object (:file:`.so`) file for Python + extensions on Unix, a DLL (given the :file:`.pyd` extension) for Python + extensions on Windows, or a Java class file for Jython extensions. (Note that + currently, the Distutils only handles C/C++ extensions for Python.) + +package + a module that contains other modules; typically contained in a directory in the + filesystem and distinguished from other directories by the presence of a file + :file:`__init__.py`. + +root package + the root of the hierarchy of packages. (This isn't really a package, since it + doesn't have an :file:`__init__.py` file. But we have to call it something.) + The vast majority of the standard library is in the root package, as are many + small, standalone third-party modules that don't belong to a larger module + collection. Unlike regular packages, modules in the root package can be found in + many directories: in fact, every directory listed in ``sys.path`` contributes + modules to the root package. + + +.. _distutils-term: + +Distutils-specific terminology +============================== + +The following terms apply more specifically to the domain of distributing Python +modules using the Distutils: + +module distribution + a collection of Python modules distributed together as a single downloadable + resource and meant to be installed *en masse*. Examples of some well-known + module distributions are Numeric Python, PyXML, PIL (the Python Imaging + Library), or mxBase. (This would be called a *package*, except that term is + already taken in the Python context: a single module distribution may contain + zero, one, or many Python packages.) + +pure module distribution + a module distribution that contains only pure Python modules and packages. + Sometimes referred to as a "pure distribution." + +non-pure module distribution + a module distribution that contains at least one extension module. Sometimes + referred to as a "non-pure distribution." + +distribution root + the top-level directory of your source tree (or source distribution); the + directory where :file:`setup.py` exists. Generally :file:`setup.py` will be + run from this directory. diff --git a/docs/source/commands.rst b/docs/source/distutils/newcommands.rst rename from docs/source/commands.rst rename to docs/source/distutils/newcommands.rst diff --git a/docs/source/distutils/packageindex.rst b/docs/source/distutils/packageindex.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/packageindex.rst @@ -0,0 +1,104 @@ +.. _package-index: + +********************************** +Registering with the Package Index +********************************** + +The Python Package Index (PyPI) holds metadata describing distributions +packaged with distutils. The distutils command :command:`register` is used to +submit your distribution's metadata to the index. It is invoked as follows:: + + python setup.py register + +Distutils will respond with the following prompt:: + + running register + We need to know who you are, so please choose either: + 1. use your existing login, + 2. register as a new user, + 3. have the server generate a new password for you (and email it to you), or + 4. quit + Your selection [default 1]: + +Note: if your username and password are saved locally, you will not see this +menu. + +If you have not registered with PyPI, then you will need to do so now. You +should choose option 2, and enter your details as required. Soon after +submitting your details, you will receive an email which will be used to confirm +your registration. + +Once you are registered, you may choose option 1 from the menu. You will be +prompted for your PyPI username and password, and :command:`register` will then +submit your metadata to the index. + +You may submit any number of versions of your distribution to the index. If you +alter the metadata for a particular version, you may submit it again and the +index will be updated. + +PyPI holds a record for each (name, version) combination submitted. The first +user to submit information for a given name is designated the Owner of that +name. They may submit changes through the :command:`register` command or through +the web interface. They may also designate other users as Owners or Maintainers. +Maintainers may edit the package information, but not designate other Owners or +Maintainers. + +By default PyPI will list all versions of a given package. To hide certain +versions, the Hidden property should be set to yes. This must be edited through +the web interface. + + +.. _pypirc: + +The .pypirc file +================ + +The format of the :file:`.pypirc` file is as follows:: + + [distutils] + index-servers = + pypi + + [pypi] + repository: + username: + password: + +The *distutils* section defines a *index-servers* variable that lists the +name of all sections describing a repository. + +Each section describing a repository defines three variables: + +- *repository*, that defines the url of the PyPI server. Defaults to + ``http://www.python.org/pypi``. +- *username*, which is the registered username on the PyPI server. +- *password*, that will be used to authenticate. If omitted the user + will be prompt to type it when needed. + +If you want to define another server a new section can be created and +listed in the *index-servers* variable:: + + [distutils] + index-servers = + pypi + other + + [pypi] + repository: + username: + password: + + [other] + repository: http://example.com/pypi + username: + password: + +:command:`register` can then be called with the -r option to point the +repository to work with:: + + python setup.py register -r http://example.com/pypi + +For convenience, the name of the section that describes the repository +may also be used:: + + python setup.py register -r other diff --git a/docs/source/distutils/setupscript.rst b/docs/source/distutils/setupscript.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/setupscript.rst @@ -0,0 +1,686 @@ +.. _setup-script: + +************************ +Writing the Setup Script +************************ + +The setup script is the center of all activity in building, distributing, and +installing modules using the Distutils. The main purpose of the setup script is +to describe your module distribution to the Distutils, so that the various +commands that operate on your modules do the right thing. As we saw in section +:ref:`distutils-simple-example` above, the setup script consists mainly of a +call to :func:`setup`, and most information supplied to the Distutils by the +module developer is supplied as keyword arguments to :func:`setup`. + +Here's a slightly more involved example, which we'll follow for the next couple +of sections: a setup script that could be used for Distutils2 itself:: + + #!/usr/bin/env python + + from distutils2.core import setup, find_packages + + setup(name='Distutils2', + version='1.0', + summary='Python Distribution Utilities', + keywords=['packaging', 'distutils2'], + author=u'Tarek Ziad??', + author_email='tarek at ziade.org', + home_page='http://bitbucket.org/tarek/distutils2/wiki/Home', + license='PSF', + packages=find_packages()) + + +There are only two differences between this and the trivial one-file +distribution presented in section :ref:`distutils-simple-example`: more +metadata, and the specification of pure Python modules by package, rather than +by module. This is important since the Distutils consist of a couple of dozen +modules split into (so far) two packages; an explicit list of every module +would be tedious to generate and difficult to maintain. For more information +on the additional metadata, see section :ref:`metadata`. + +Note that any pathnames (files or directories) supplied in the setup script +should be written using the Unix convention, i.e. slash-separated. The +Distutils will take care of converting this platform-neutral representation into +whatever is appropriate on your current platform before actually using the +pathname. This makes your setup script portable across operating systems, which +of course is one of the major goals of the Distutils. In this spirit, all +pathnames in this document are slash-separated. + +This, of course, only applies to pathnames given to Distutils functions. If +you, for example, use standard Python functions such as :func:`glob.glob` or +:func:`os.listdir` to specify files, you should be careful to write portable +code instead of hardcoding path separators:: + + glob.glob(os.path.join('mydir', 'subdir', '*.html')) + os.listdir(os.path.join('mydir', 'subdir')) + + +.. _listing-packages: + +Listing whole packages +====================== + +The :option:`packages` option tells the Distutils to process (build, distribute, +install, etc.) all pure Python modules found in each package mentioned in the +:option:`packages` list. In order to do this, of course, there has to be a +correspondence between package names and directories in the filesystem. The +default correspondence is the most obvious one, i.e. package :mod:`distutils2` is +found in the directory :file:`distutils2` relative to the distribution root. +Thus, when you say ``packages = ['foo']`` in your setup script, you are +promising that the Distutils will find a file :file:`foo/__init__.py` (which +might be spelled differently on your system, but you get the idea) relative to +the directory where your setup script lives. If you break this promise, the +Distutils will issue a warning but still process the broken package anyways. + +If you use a different convention to lay out your source directory, that's no +problem: you just have to supply the :option:`package_dir` option to tell the +Distutils about your convention. For example, say you keep all Python source +under :file:`lib`, so that modules in the "root package" (i.e., not in any +package at all) are in :file:`lib`, modules in the :mod:`foo` package are in +:file:`lib/foo`, and so forth. Then you would put :: + + package_dir = {'': 'lib'} + +in your setup script. The keys to this dictionary are package names, and an +empty package name stands for the root package. The values are directory names +relative to your distribution root. In this case, when you say ``packages = +['foo']``, you are promising that the file :file:`lib/foo/__init__.py` exists. + +Another possible convention is to put the :mod:`foo` package right in +:file:`lib`, the :mod:`foo.bar` package in :file:`lib/bar`, etc. This would be +written in the setup script as :: + + package_dir = {'foo': 'lib'} + +A ``package: dir`` entry in the :option:`package_dir` dictionary implicitly +applies to all packages below *package*, so the :mod:`foo.bar` case is +automatically handled here. In this example, having ``packages = ['foo', +'foo.bar']`` tells the Distutils to look for :file:`lib/__init__.py` and +:file:`lib/bar/__init__.py`. (Keep in mind that although :option:`package_dir` +applies recursively, you must explicitly list all packages in +:option:`packages`: the Distutils will *not* recursively scan your source tree +looking for any directory with an :file:`__init__.py` file.) + + +.. _listing-modules: + +Listing individual modules +========================== + +For a small module distribution, you might prefer to list all modules rather +than listing packages---especially the case of a single module that goes in the +"root package" (i.e., no package at all). This simplest case was shown in +section :ref:`distutils-simple-example`; here is a slightly more involved +example:: + + py_modules = ['mod1', 'pkg.mod2'] + +This describes two modules, one of them in the "root" package, the other in the +:mod:`pkg` package. Again, the default package/directory layout implies that +these two modules can be found in :file:`mod1.py` and :file:`pkg/mod2.py`, and +that :file:`pkg/__init__.py` exists as well. And again, you can override the +package/directory correspondence using the :option:`package_dir` option. + + +.. _describing-extensions: + +Describing extension modules +============================ + +Just as writing Python extension modules is a bit more complicated than writing +pure Python modules, describing them to the Distutils is a bit more complicated. +Unlike pure modules, it's not enough just to list modules or packages and expect +the Distutils to go out and find the right files; you have to specify the +extension name, source file(s), and any compile/link requirements (include +directories, libraries to link with, etc.). + +.. XXX read over this section + +All of this is done through another keyword argument to :func:`setup`, the +:option:`ext_modules` option. :option:`ext_modules` is just a list of +:class:`Extension` instances, each of which describes a single extension module. +Suppose your distribution includes a single extension, called :mod:`foo` and +implemented by :file:`foo.c`. If no additional instructions to the +compiler/linker are needed, describing this extension is quite simple:: + + Extension('foo', ['foo.c']) + +The :class:`Extension` class can be imported from :mod:`distutils2.core` along +with :func:`setup`. Thus, the setup script for a module distribution that +contains only this one extension and nothing else might be:: + + from distutils2.core import setup, Extension + setup(name='foo', + version='1.0', + ext_modules=[Extension('foo', ['foo.c'])]) + +The :class:`Extension` class (actually, the underlying extension-building +machinery implemented by the :command:`build_ext` command) supports a great deal +of flexibility in describing Python extensions, which is explained in the +following sections. + + +Extension names and packages +---------------------------- + +The first argument to the :class:`Extension` constructor is always the name of +the extension, including any package names. For example, :: + + Extension('foo', ['src/foo1.c', 'src/foo2.c']) + +describes an extension that lives in the root package, while :: + + Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c']) + +describes the same extension in the :mod:`pkg` package. The source files and +resulting object code are identical in both cases; the only difference is where +in the filesystem (and therefore where in Python's namespace hierarchy) the +resulting extension lives. + +If you have a number of extensions all in the same package (or all under the +same base package), use the :option:`ext_package` keyword argument to +:func:`setup`. For example, :: + + setup(..., + ext_package='pkg', + ext_modules=[Extension('foo', ['foo.c']), + Extension('subpkg.bar', ['bar.c'])]) + +will compile :file:`foo.c` to the extension :mod:`pkg.foo`, and :file:`bar.c` to +:mod:`pkg.subpkg.bar`. + + +Extension source files +---------------------- + +The second argument to the :class:`Extension` constructor is a list of source +files. Since the Distutils currently only support C, C++, and Objective-C +extensions, these are normally C/C++/Objective-C source files. (Be sure to use +appropriate extensions to distinguish C++\ source files: :file:`.cc` and +:file:`.cpp` seem to be recognized by both Unix and Windows compilers.) + +However, you can also include SWIG interface (:file:`.i`) files in the list; the +:command:`build_ext` command knows how to deal with SWIG extensions: it will run +SWIG on the interface file and compile the resulting C/C++ file into your +extension. + +.. XXX SWIG support is rough around the edges and largely untested! + +This warning notwithstanding, options to SWIG can be currently passed like +this:: + + setup(..., + ext_modules=[Extension('_foo', ['foo.i'], + swig_opts=['-modern', '-I../include'])], + py_modules=['foo']) + +Or on the command line like this:: + + > python setup.py build_ext --swig-opts="-modern -I../include" + +On some platforms, you can include non-source files that are processed by the +compiler and included in your extension. Currently, this just means Windows +message text (:file:`.mc`) files and resource definition (:file:`.rc`) files for +Visual C++. These will be compiled to binary resource (:file:`.res`) files and +linked into the executable. + + +Preprocessor options +-------------------- + +Three optional arguments to :class:`Extension` will help if you need to specify +include directories to search or preprocessor macros to define/undefine: +``include_dirs``, ``define_macros``, and ``undef_macros``. + +For example, if your extension requires header files in the :file:`include` +directory under your distribution root, use the ``include_dirs`` option:: + + Extension('foo', ['foo.c'], include_dirs=['include']) + +You can specify absolute directories there; if you know that your extension will +only be built on Unix systems with X11R6 installed to :file:`/usr`, you can get +away with :: + + Extension('foo', ['foo.c'], include_dirs=['/usr/include/X11']) + +You should avoid this sort of non-portable usage if you plan to distribute your +code: it's probably better to write C code like :: + + #include + +If you need to include header files from some other Python extension, you can +take advantage of the fact that header files are installed in a consistent way +by the Distutils :command:`install_header` command. For example, the Numerical +Python header files are installed (on a standard Unix installation) to +:file:`/usr/local/include/python1.5/Numerical`. (The exact location will differ +according to your platform and Python installation.) Since the Python include +directory---\ :file:`/usr/local/include/python1.5` in this case---is always +included in the search path when building Python extensions, the best approach +is to write C code like :: + + #include + +.. TODO check if it's d2.sysconfig or the new sysconfig module now + +If you must put the :file:`Numerical` include directory right into your header +search path, though, you can find that directory using the Distutils +:mod:`distutils2.sysconfig` module:: + + from distutils2.sysconfig import get_python_inc + incdir = os.path.join(get_python_inc(plat_specific=1), 'Numerical') + setup(..., + Extension(..., include_dirs=[incdir])) + +Even though this is quite portable---it will work on any Python installation, +regardless of platform---it's probably easier to just write your C code in the +sensible way. + +You can define and undefine preprocessor macros with the ``define_macros`` and +``undef_macros`` options. ``define_macros`` takes a list of ``(name, value)`` +tuples, where ``name`` is the name of the macro to define (a string) and +``value`` is its value: either a string or ``None``. (Defining a macro ``FOO`` +to ``None`` is the equivalent of a bare ``#define FOO`` in your C source: with +most compilers, this sets ``FOO`` to the string ``1``.) ``undef_macros`` is +just a list of macros to undefine. + +For example:: + + Extension(..., + define_macros=[('NDEBUG', '1'), + ('HAVE_STRFTIME', None)], + undef_macros=['HAVE_FOO', 'HAVE_BAR']) + +is the equivalent of having this at the top of every C source file:: + + #define NDEBUG 1 + #define HAVE_STRFTIME + #undef HAVE_FOO + #undef HAVE_BAR + + +Library options +--------------- + +You can also specify the libraries to link against when building your extension, +and the directories to search for those libraries. The ``libraries`` option is +a list of libraries to link against, ``library_dirs`` is a list of directories +to search for libraries at link-time, and ``runtime_library_dirs`` is a list of +directories to search for shared (dynamically loaded) libraries at run-time. + +For example, if you need to link against libraries known to be in the standard +library search path on target systems :: + + Extension(..., + libraries=['gdbm', 'readline']) + +If you need to link with libraries in a non-standard location, you'll have to +include the location in ``library_dirs``:: + + Extension(..., + library_dirs=['/usr/X11R6/lib'], + libraries=['X11', 'Xt']) + +(Again, this sort of non-portable construct should be avoided if you intend to +distribute your code.) + +.. XXX Should mention clib libraries here or somewhere else! + + +Other options +------------- + +There are still some other options which can be used to handle special cases. + +The :option:`optional` option is a boolean; if it is true, +a build failure in the extension will not abort the build process, but +instead simply not install the failing extension. + +The :option:`extra_objects` option is a list of object files to be passed to the +linker. These files must not have extensions, as the default extension for the +compiler is used. + +:option:`extra_compile_args` and :option:`extra_link_args` can be used to +specify additional command-line options for the respective compiler and linker +command lines. + +:option:`export_symbols` is only useful on Windows. It can contain a list of +symbols (functions or variables) to be exported. This option is not needed when +building compiled extensions: Distutils will automatically add ``initmodule`` +to the list of exported symbols. + +The :option:`depends` option is a list of files that the extension depends on +(for example header files). The build command will call the compiler on the +sources to rebuild extension if any on this files has been modified since the +previous build. + +Relationships between Distributions and Packages +================================================ + +.. FIXME rewrite to update to PEP 345 (but without dist/release confusion) + +A distribution may relate to packages in three specific ways: + +#. It can require packages or modules. + +#. It can provide packages or modules. + +#. It can obsolete packages or modules. + +These relationships can be specified using keyword arguments to the +:func:`distutils2.core.setup` function. + +Dependencies on other Python modules and packages can be specified by supplying +the *requires* keyword argument to :func:`setup`. The value must be a list of +strings. Each string specifies a package that is required, and optionally what +versions are sufficient. + +To specify that any version of a module or package is required, the string +should consist entirely of the module or package name. Examples include +``'mymodule'`` and ``'xml.parsers.expat'``. + +If specific versions are required, a sequence of qualifiers can be supplied in +parentheses. Each qualifier may consist of a comparison operator and a version +number. The accepted comparison operators are:: + + < > == + <= >= != + +These can be combined by using multiple qualifiers separated by commas (and +optional whitespace). In this case, all of the qualifiers must be matched; a +logical AND is used to combine the evaluations. + +Let's look at a bunch of examples: + ++-------------------------+----------------------------------------------+ +| Requires Expression | Explanation | ++=========================+==============================================+ +| ``==1.0`` | Only version ``1.0`` is compatible | ++-------------------------+----------------------------------------------+ +| ``>1.0, !=1.5.1, <2.0`` | Any version after ``1.0`` and before ``2.0`` | +| | is compatible, except ``1.5.1`` | ++-------------------------+----------------------------------------------+ + +Now that we can specify dependencies, we also need to be able to specify what we +provide that other distributions can require. This is done using the *provides* +keyword argument to :func:`setup`. The value for this keyword is a list of +strings, each of which names a Python module or package, and optionally +identifies the version. If the version is not specified, it is assumed to match +that of the distribution. + +Some examples: + ++---------------------+----------------------------------------------+ +| Provides Expression | Explanation | ++=====================+==============================================+ +| ``mypkg`` | Provide ``mypkg``, using the distribution | +| | version | ++---------------------+----------------------------------------------+ +| ``mypkg (1.1)`` | Provide ``mypkg`` version 1.1, regardless of | +| | the distribution version | ++---------------------+----------------------------------------------+ + +A package can declare that it obsoletes other packages using the *obsoletes* +keyword argument. The value for this is similar to that of the *requires* +keyword: a list of strings giving module or package specifiers. Each specifier +consists of a module or package name optionally followed by one or more version +qualifiers. Version qualifiers are given in parentheses after the module or +package name. + +The versions identified by the qualifiers are those that are obsoleted by the +distribution being described. If no qualifiers are given, all versions of the +named module or package are understood to be obsoleted. + +.. _distutils-installing-scripts: + +Installing Scripts +================== + +So far we have been dealing with pure and non-pure Python modules, which are +usually not run by themselves but imported by scripts. + +Scripts are files containing Python source code, intended to be started from the +command line. Scripts don't require Distutils to do anything very complicated. +The only clever feature is that if the first line of the script starts with +``#!`` and contains the word "python", the Distutils will adjust the first line +to refer to the current interpreter location. By default, it is replaced with +the current interpreter location. The :option:`--executable` (or :option:`-e`) +option will allow the interpreter path to be explicitly overridden. + +The :option:`scripts` option simply is a list of files to be handled in this +way. From the PyXML setup script:: + + setup(..., + scripts=['scripts/xmlproc_parse', 'scripts/xmlproc_val']) + +All the scripts will also be added to the ``MANIFEST`` file if no template is +provided. See :ref:`manifest`. + +.. _distutils-installing-package-data: + +Installing Package Data +======================= + +Often, additional files need to be installed into a package. These files are +often data that's closely related to the package's implementation, or text files +containing documentation that might be of interest to programmers using the +package. These files are called :dfn:`package data`. + +Package data can be added to packages using the ``package_data`` keyword +argument to the :func:`setup` function. The value must be a mapping from +package name to a list of relative path names that should be copied into the +package. The paths are interpreted as relative to the directory containing the +package (information from the ``package_dir`` mapping is used if appropriate); +that is, the files are expected to be part of the package in the source +directories. They may contain glob patterns as well. + +The path names may contain directory portions; any necessary directories will be +created in the installation. + +For example, if a package should contain a subdirectory with several data files, +the files can be arranged like this in the source tree:: + + setup.py + src/ + mypkg/ + __init__.py + module.py + data/ + tables.dat + spoons.dat + forks.dat + +The corresponding call to :func:`setup` might be:: + + setup(..., + packages=['mypkg'], + package_dir={'mypkg': 'src/mypkg'}, + package_data={'mypkg': ['data/*.dat']}) + + +All the files that match ``package_data`` will be added to the ``MANIFEST`` +file if no template is provided. See :ref:`manifest`. + + +.. _distutils2-additional-files: + +Installing Additional Files +=========================== + +The :option:`data_files` option can be used to specify additional files needed +by the module distribution: configuration files, message catalogs, data files, +anything which doesn't fit in the previous categories. + +:option:`data_files` specifies a sequence of (*directory*, *files*) pairs in the +following way:: + + setup(..., + data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']), + ('config', ['cfg/data.cfg']), + ('/etc/init.d', ['init-script'])]) + +Note that you can specify the directory names where the data files will be +installed, but you cannot rename the data files themselves. + +Each (*directory*, *files*) pair in the sequence specifies the installation +directory and the files to install there. If *directory* is a relative path, it +is interpreted relative to the installation prefix (Python's ``sys.prefix`` for +pure-Python packages, ``sys.exec_prefix`` for packages that contain extension +modules). Each file name in *files* is interpreted relative to the +:file:`setup.py` script at the top of the package source distribution. No +directory information from *files* is used to determine the final location of +the installed file; only the name of the file is used. + +You can specify the :option:`data_files` options as a simple sequence of files +without specifying a target directory, but this is not recommended, and the +:command:`install` command will print a warning in this case. To install data +files directly in the target directory, an empty string should be given as the +directory. + +All the files that match ``data_files`` will be added to the ``MANIFEST`` file +if no template is provided. See :ref:`manifest`. + + + +.. _metadata: + +Metadata reference +================== + +The setup script may include additional metadata beyond the name and version. +This table describes required and additional information: + ++----------------------+---------------------------+-----------------+--------+ +| Meta-Data | Description | Value | Notes | ++======================+===========================+=================+========+ +| ``name`` | name of the project | short string | \(1) | ++----------------------+---------------------------+-----------------+--------+ +| ``version`` | version of this release | short string | (1)(2) | ++----------------------+---------------------------+-----------------+--------+ +| ``author`` | project author's name | short string | \(3) | ++----------------------+---------------------------+-----------------+--------+ +| ``author_email`` | email address of the | email address | \(3) | +| | project author | | | ++----------------------+---------------------------+-----------------+--------+ +| ``maintainer`` | project maintainer's name | short string | \(3) | ++----------------------+---------------------------+-----------------+--------+ +| ``maintainer_email`` | email address of the | email address | \(3) | +| | project maintainer | | | ++----------------------+---------------------------+-----------------+--------+ +| ``home_page`` | home page for the project | URL | \(1) | ++----------------------+---------------------------+-----------------+--------+ +| ``summary`` | short description of the | short string | | +| | project | | | ++----------------------+---------------------------+-----------------+--------+ +| ``description`` | longer description of the | long string | \(5) | +| | project | | | ++----------------------+---------------------------+-----------------+--------+ +| ``download_url`` | location where the | URL | | +| | project may be downloaded | | | ++----------------------+---------------------------+-----------------+--------+ +| ``classifiers`` | a list of classifiers | list of strings | \(4) | ++----------------------+---------------------------+-----------------+--------+ +| ``platforms`` | a list of platforms | list of strings | | ++----------------------+---------------------------+-----------------+--------+ +| ``license`` | license for the release | short string | \(6) | ++----------------------+---------------------------+-----------------+--------+ + +Notes: + +(1) + These fields are required. + +(2) + It is recommended that versions take the form *major.minor[.patch[.sub]]*. + +(3) + Either the author or the maintainer must be identified. + +(4) + The list of classifiers is available from the `PyPI website + `_. See also :mod:`distutils2.mkpkg`. + +(5) + The ``description`` field is used by PyPI when you are registering a + release, to build its PyPI page. + +(6) + The ``license`` field is a text indicating the license covering the + distribution where the license is not a selection from the "License" Trove + classifiers. See the ``Classifier`` field. Notice that + there's a ``licence`` distribution option which is deprecated but still + acts as an alias for ``license``. + +'short string' + A single line of text, not more than 200 characters. + +'long string' + Multiple lines of plain text in reStructuredText format (see + http://docutils.sf.net/). + +'list of strings' + See below. + +In Python 2.x, "string value" means a unicode object. If a byte string (str or +bytes) is given, it has to be valid ASCII. + +.. TODO move this section to the version document, keep a summary, add a link + +Encoding the version information is an art in itself. Python projects generally +adhere to the version format *major.minor[.patch][sub]*. The major number is 0 +for initial, experimental releases of software. It is incremented for releases +that represent major milestones in a project. The minor number is incremented +when important new features are added to the project. The patch number +increments when bug-fix releases are made. Additional trailing version +information is sometimes used to indicate sub-releases. These are +"a1,a2,...,aN" (for alpha releases, where functionality and API may change), +"b1,b2,...,bN" (for beta releases, which only fix bugs) and "pr1,pr2,...,prN" +(for final pre-release release testing). Some examples: + +0.1.0 + the first, experimental release of a project + +1.0.1a2 + the second alpha release of the first patch version of 1.0 + +:option:`classifiers` are specified in a Python list:: + + setup(..., + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: Console', + 'Environment :: Web Environment', + 'Intended Audience :: End Users/Desktop', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Python Software Foundation License', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX', + 'Programming Language :: Python', + 'Topic :: Communications :: Email', + 'Topic :: Office/Business', + 'Topic :: Software Development :: Bug Tracking', + ]) + + +Debugging the setup script +========================== + +Sometimes things go wrong, and the setup script doesn't do what the developer +wants. + +Distutils catches any exceptions when running the setup script, and print a +simple error message before the script is terminated. The motivation for this +behaviour is to not confuse administrators who don't know much about Python and +are trying to install a project. If they get a big long traceback from deep +inside the guts of Distutils, they may think the project or the Python +installation is broken because they don't read all the way down to the bottom +and see that it's a permission problem. + +.. FIXME DISTUTILS_DEBUG is dead, document logging/warnings here + +On the other hand, this doesn't help the developer to find the cause of the +failure. For this purpose, the DISTUTILS_DEBUG environment variable can be set +to anything except an empty string, and Distutils2 will now print detailed +information about what it is doing, and prints the full traceback in case an +exception occurs. diff --git a/docs/source/distutils/sourcedist.rst b/docs/source/distutils/sourcedist.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/sourcedist.rst @@ -0,0 +1,267 @@ +.. _source-dist: + +****************************** +Creating a Source Distribution +****************************** + +As shown in section :ref:`distutils-simple-example`, you use the :command:`sdist` command +to create a source distribution. In the simplest case, :: + + python setup.py sdist + +(assuming you haven't specified any :command:`sdist` options in the setup script +or config file), :command:`sdist` creates the archive of the default format for +the current platform. The default format is a gzip'ed tar file +(:file:`.tar.gz`) on Unix, and ZIP file on Windows. + +You can specify as many formats as you like using the :option:`--formats` +option, for example:: + + python setup.py sdist --formats=gztar,zip + +to create a gzipped tarball and a zip file. The available formats are: + ++-----------+-------------------------+---------+ +| Format | Description | Notes | ++===========+=========================+=========+ +| ``zip`` | zip file (:file:`.zip`) | (1),(3) | ++-----------+-------------------------+---------+ +| ``gztar`` | gzip'ed tar file | \(2) | +| | (:file:`.tar.gz`) | | ++-----------+-------------------------+---------+ +| ``bztar`` | bzip2'ed tar file | | +| | (:file:`.tar.bz2`) | | ++-----------+-------------------------+---------+ +| ``ztar`` | compressed tar file | \(4) | +| | (:file:`.tar.Z`) | | ++-----------+-------------------------+---------+ +| ``tar`` | tar file (:file:`.tar`) | | ++-----------+-------------------------+---------+ + +Notes: + +(1) + default on Windows + +(2) + default on Unix + +(3) + requires either external :program:`zip` utility or :mod:`zipfile` module (part + of the standard Python library since Python 1.6) + +(4) + requires the :program:`compress` program. Notice that this format is now + pending for deprecation and will be removed in the future versions of Python. + +When using any ``tar`` format (``gztar``, ``bztar``, ``ztar`` or +``tar``) under Unix, you can specify the ``owner`` and ``group`` names +that will be set for each member of the archive. + +For example, if you want all files of the archive to be owned by root:: + + python setup.py sdist --owner=root --group=root + + +.. _manifest: + +Specifying the files to distribute +================================== + +If you don't supply an explicit list of files (or instructions on how to +generate one), the :command:`sdist` command puts a minimal default set into the +source distribution: + +* all Python source files implied by the :option:`py_modules` and + :option:`packages` options + +* all C source files mentioned in the :option:`ext_modules` or + :option:`libraries` options + +* scripts identified by the :option:`scripts` option + See :ref:`distutils-installing-scripts`. + +* anything that looks like a test script: :file:`test/test\*.py` (currently, the + Distutils don't do anything with test scripts except include them in source + distributions, but in the future there will be a standard for testing Python + module distributions) + +* :file:`README.txt` (or :file:`README`), :file:`setup.py` (or whatever you + called your setup script), and :file:`setup.cfg` + +* all files that matches the ``package_data`` metadata. + See :ref:`distutils-installing-package-data`. + +* all files that matches the ``data_files`` metadata. + See :ref:`distutils-additional-files`. + +Sometimes this is enough, but usually you will want to specify additional files +to distribute. The typical way to do this is to write a *manifest template*, +called :file:`MANIFEST.in` by default. The manifest template is just a list of +instructions for how to generate your manifest file, :file:`MANIFEST`, which is +the exact list of files to include in your source distribution. The +:command:`sdist` command processes this template and generates a manifest based +on its instructions and what it finds in the filesystem. + +If you prefer to roll your own manifest file, the format is simple: one filename +per line, regular files (or symlinks to them) only. If you do supply your own +:file:`MANIFEST`, you must specify everything: the default set of files +described above does not apply in this case. + +See :ref:`manifest_template` section for a syntax reference. + +.. _manifest-options: + +Manifest-related options +======================== + +The normal course of operations for the :command:`sdist` command is as follows: + +* if the manifest file, :file:`MANIFEST` doesn't exist, read :file:`MANIFEST.in` + and create the manifest + +* if neither :file:`MANIFEST` nor :file:`MANIFEST.in` exist, create a manifest + with just the default file set + +* if either :file:`MANIFEST.in` or the setup script (:file:`setup.py`) are more + recent than :file:`MANIFEST`, recreate :file:`MANIFEST` by reading + :file:`MANIFEST.in` + +* use the list of files now in :file:`MANIFEST` (either just generated or read + in) to create the source distribution archive(s) + +There are a couple of options that modify this behaviour. First, use the +:option:`--no-defaults` and :option:`--no-prune` to disable the standard +"include" and "exclude" sets. + +Second, you might just want to (re)generate the manifest, but not create a +source distribution:: + + python setup.py sdist --manifest-only + +:option:`-o` is a sortcut for :option:`--manifest-only`. + +.. _manifest_template: + +The MANIFEST.in template +======================== + +A :file:`MANIFEST.in` file can be added in a project to define the list of +files to include in the distribution built by the :command:`sdist` command. + +When :command:`sdist` is run, it will look for the :file:`MANIFEST.in` file +and interpret it to generate the :file:`MANIFEST` file that contains the +list of files that will be included in the package. + +This mechanism can be used when the default list of files is not enough. +(See :ref:`manifest`). + +Principle +--------- + +The manifest template has one command per line, where each command specifies a +set of files to include or exclude from the source distribution. For an +example, let's look at the Distutils' own manifest template:: + + include *.txt + recursive-include examples *.txt *.py + prune examples/sample?/build + +The meanings should be fairly clear: include all files in the distribution root +matching :file:`\*.txt`, all files anywhere under the :file:`examples` directory +matching :file:`\*.txt` or :file:`\*.py`, and exclude all directories matching +:file:`examples/sample?/build`. All of this is done *after* the standard +include set, so you can exclude files from the standard set with explicit +instructions in the manifest template. (Or, you can use the +:option:`--no-defaults` option to disable the standard set entirely.) + +The order of commands in the manifest template matters: initially, we have the +list of default files as described above, and each command in the template adds +to or removes from that list of files. Once we have fully processed the +manifest template, we remove files that should not be included in the source +distribution: + +* all files in the Distutils "build" tree (default :file:`build/`) + +* all files in directories named :file:`RCS`, :file:`CVS`, :file:`.svn`, + :file:`.hg`, :file:`.git`, :file:`.bzr` or :file:`_darcs` + +Now we have our complete list of files, which is written to the manifest for +future reference, and then used to build the source distribution archive(s). + +You can disable the default set of included files with the +:option:`--no-defaults` option, and you can disable the standard exclude set +with :option:`--no-prune`. + +Following the Distutils' own manifest template, let's trace how the +:command:`sdist` command builds the list of files to include in the Distutils +source distribution: + +#. include all Python source files in the :file:`distutils2` and + :file:`distutils2/command` subdirectories (because packages corresponding to + those two directories were mentioned in the :option:`packages` option in the + setup script---see section :ref:`setup-script`) + +#. include :file:`README.txt`, :file:`setup.py`, and :file:`setup.cfg` (standard + files) + +#. include :file:`test/test\*.py` (standard files) + +#. include :file:`\*.txt` in the distribution root (this will find + :file:`README.txt` a second time, but such redundancies are weeded out later) + +#. include anything matching :file:`\*.txt` or :file:`\*.py` in the sub-tree + under :file:`examples`, + +#. exclude all files in the sub-trees starting at directories matching + :file:`examples/sample?/build`\ ---this may exclude files included by the + previous two steps, so it's important that the ``prune`` command in the manifest + template comes after the ``recursive-include`` command + +#. exclude the entire :file:`build` tree, and any :file:`RCS`, :file:`CVS`, + :file:`.svn`, :file:`.hg`, :file:`.git`, :file:`.bzr` and :file:`_darcs` + directories + +Just like in the setup script, file and directory names in the manifest template +should always be slash-separated; the Distutils will take care of converting +them to the standard representation on your platform. That way, the manifest +template is portable across operating systems. + +Commands +-------- + +The manifest template commands are: + ++-------------------------------------------+-----------------------------------------------+ +| Command | Description | ++===========================================+===============================================+ +| :command:`include pat1 pat2 ...` | include all files matching any of the listed | +| | patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`exclude pat1 pat2 ...` | exclude all files matching any of the listed | +| | patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`recursive-include dir pat1 pat2 | include all files under *dir* matching any of | +| ...` | the listed patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`recursive-exclude dir pat1 pat2 | exclude all files under *dir* matching any of | +| ...` | the listed patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`global-include pat1 pat2 ...` | include all files anywhere in the source tree | +| | matching --- & any of the listed patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`global-exclude pat1 pat2 ...` | exclude all files anywhere in the source tree | +| | matching --- & any of the listed patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`prune dir` | exclude all files under *dir* | ++-------------------------------------------+-----------------------------------------------+ +| :command:`graft dir` | include all files under *dir* | ++-------------------------------------------+-----------------------------------------------+ + +The patterns here are Unix-style "glob" patterns: ``*`` matches any sequence of +regular filename characters, ``?`` matches any single regular filename +character, and ``[range]`` matches any of the characters in *range* (e.g., +``a-z``, ``a-zA-Z``, ``a-f0-9_.``). The definition of "regular filename +character" is platform-specific: on Unix it is anything except slash; on Windows +anything except backslash or colon. + diff --git a/docs/source/distutils/uploading.rst b/docs/source/distutils/uploading.rst new file mode 100644 --- /dev/null +++ b/docs/source/distutils/uploading.rst @@ -0,0 +1,80 @@ +.. _package-upload: + +*************************************** +Uploading Packages to the Package Index +*************************************** + +The Python Package Index (PyPI) not only stores the package info, but also the +package data if the author of the package wishes to. The distutils command +:command:`upload` pushes the distribution files to PyPI. + +The command is invoked immediately after building one or more distribution +files. For example, the command :: + + python setup.py sdist bdist_wininst upload + +will cause the source distribution and the Windows installer to be uploaded to +PyPI. Note that these will be uploaded even if they are built using an earlier +invocation of :file:`setup.py`, but that only distributions named on the command +line for the invocation including the :command:`upload` command are uploaded. + +The :command:`upload` command uses the username, password, and repository URL +from the :file:`$HOME/.pypirc` file (see section :ref:`pypirc` for more on this +file). If a :command:`register` command was previously called in the same +command, and if the password was entered in the prompt, :command:`upload` will +reuse the entered password. This is useful if you do not want to store a clear +text password in the :file:`$HOME/.pypirc` file. + +You can specify another PyPI server with the :option:`--repository=*url*` +option:: + + python setup.py sdist bdist_wininst upload -r http://example.com/pypi + +See section :ref:`pypirc` for more on defining several servers. + +You can use the :option:`--sign` option to tell :command:`upload` to sign each +uploaded file using GPG (GNU Privacy Guard). The :program:`gpg` program must +be available for execution on the system :envvar:`PATH`. You can also specify +which key to use for signing using the :option:`--identity=*name*` option. + +Other :command:`upload` options include :option:`--repository=` or +:option:`--repository=
` where *url* is the url of the server and +*section* the name of the section in :file:`$HOME/.pypirc`, and +:option:`--show-response` (which displays the full response text from the PyPI +server for help in debugging upload problems). + +PyPI package display +==================== + +The ``description`` field plays a special role at PyPI. It is used by +the server to display a home page for the registered package. + +If you use the `reStructuredText `_ +syntax for this field, PyPI will parse it and display an HTML output for +the package home page. + +The ``description`` field can be filled from a text file located in the +project:: + + from distutils2.core import setup + + fp = open('README.txt') + try: + description = fp.read() + finally: + fp.close() + + setup(name='Distutils2', + description=description) + +In that case, :file:`README.txt` is a regular reStructuredText text file located +in the root of the package besides :file:`setup.py`. + +To prevent registering broken reStructuredText content, you can use the +:program:`rst2html` program that is provided by the :mod:`docutils` package +and check the ``description`` from the command line:: + + $ python setup.py --description | rst2html.py > output.html + +:mod:`docutils` will display a warning if there's something wrong with your +syntax. diff --git a/docs/source/index.rst b/docs/source/index.rst --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,24 +1,74 @@ -.. Distutils2 documentation master file, created by - sphinx-quickstart on Sun Feb 28 15:23:06 2010. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +.. Distutils2 doc site master file + This doc serves as project homepage and documentation preview + (on distutils2.notmyidea.org) until distutils2 is merged back into + the Python standard library. + This file should contain the root `toctree` directive. -Welcome to Distutils2's documentation! -====================================== -Contents: +Welcome to Distutils2! +====================== + +Distutils2 is the new, improved version of the Python Distribution Utilities, +a library used to package, distribute, build and install Python projects. + +- `Origin of the project`__ +- `Main code repository`__ +- `Clones of this repository`__ +- `Bug tracker`__ (some bugs may be assigned only to Distutils) +- `Teams that worked on Distutils2 for Google Summer of Code 2010`__ (links to + their code repositories and weblogs) + +.. __: http://tarekziade.wordpress.com/2010/03/03/ + the-fate-of-distutils-pycon-summit-packaging-sprint-detailed-report/ +.. __: http://bitbucket.org/tarek/distutils2/ +.. __: http://bitbucket.org/tarek/distutils2/descendants/ +.. __: http://bugs.python.org/issue?%40sort0=activity&%40sortdir0=on&%40sort1= + &%40group0=priority&%40group1=&%40columns=title%2Cid%2Cactivity%2Cstatus + &%40filter=components%2Cstatus&status=1&components=25&%40pagesize=50 + &%40startwith=0 +.. __: http://bitbucket.org/tarek/distutils2/wiki/GSoC_2010_teams + +If you???re looking for information on how to contribute, head to +:doc:`devresources`. + + +Documentation +============= + +These documents are the in-development version of Distutils2's documentation, +started from the existing Distutils documentation and updated by the +Distutils2 group (GSoC students, mentors, volunteers). + +Please remember that this is a work in progress. The documentation is not +complete, not spell-checked, and uses different styles. + +The documentation is split in three sections, as in the standard Python docs: + +:doc:`install/index` + A guide for for end-users wanting to install a Python application or + library. + +:doc:`distutils/index` + A guide for Python developers wanting to package and distribute their + project. + +:doc:`library/distutils2` + A reference for developers wanting to use standalone building blocks like + :mod:`~distutils2.version` or :mod:`~distutils2.metadata`, or extend + Distutils2 itself. Since :PEP:`376` is partly implemented in the + :mod:`pkgutil` module, its updated documentation is also included: + :doc:`library/pkgutil`. + .. toctree:: - :maxdepth: 2 + :hidden: - metadata - pkgutil - depgraph - commands - command_hooks - test_framework - projects-index - version + devresources + install/index + distutils/index + library/distutils2 + library/pkgutil + Indices and tables ================== @@ -26,4 +76,3 @@ * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/docs/source/install/index.rst b/docs/source/install/index.rst new file mode 100644 --- /dev/null +++ b/docs/source/install/index.rst @@ -0,0 +1,997 @@ +.. highlightlang:: none + +.. _install-index: + +****************************** + Installing Python Projects +****************************** + +:Author: Greg Ward and Distutils2 contributors +:Release: |version| +:Date: |today| + +.. TODO: Fill in XXX comments + +.. The audience for this document includes people who don't know anything + about Python and aren't about to learn the language just in order to + install and maintain it for their users, i.e. system administrators. + Thus, I have to be sure to explain the basics at some point: + sys.path and PYTHONPATH at least. Should probably give pointers to + other docs on "import site", PYTHONSTARTUP, PYTHONHOME, etc. + + Finally, it might be useful to include all the material from my "Care + and Feeding of a Python Installation" talk in here somewhere. Yow! + +.. topic:: Abstract + + This document describes the Python Distribution Utilities ("Distutils2") from + the end-user's point-of-view, describing how to extend the capabilities of a + standard Python installation by building and installing third-party Python + modules and extensions. + + +.. _inst-intro: + +Introduction +============ + +Although Python's extensive standard library covers many programming needs, +there often comes a time when you need to add some new functionality to your +Python installation in the form of third-party modules. This might be necessary +to support your own programming, or to support an application that you want to +use and that happens to be written in Python. + +In the past, there has been little support for adding third-party modules to an +existing Python installation. With the introduction of the Python Distribution +Utilities (Distutils for short) in Python 2.0, this changed. + +This document is aimed primarily at the people who need to install third-party +Python modules: end-users and system administrators who just need to get some +Python application running, and existing Python programmers who want to add some +new goodies to their toolbox. You don't need to know Python to read this +document; there will be some brief forays into using Python's interactive mode +to explore your installation, but that's it. If you're looking for information +on how to distribute your own Python modules so that others may use them, see +the :ref:`distutils-index` manual. + + +.. _inst-trivial-install: + +Best case: trivial installation +------------------------------- + +In the best case, someone will have prepared a special version of the module +distribution you want to install that is targeted specifically at your platform +and is installed just like any other software on your platform. For example, +the module developer might make an executable installer available for Windows +users, an RPM package for users of RPM-based Linux systems (Red Hat, SuSE, +Mandrake, and many others), a Debian package for users of Debian-based Linux +systems, and so forth. + +In that case, you would download the installer appropriate to your platform and +do the obvious thing with it: run it if it's an executable installer, ``rpm +--install`` it if it's an RPM, etc. You don't need to run Python or a setup +script, you don't need to compile anything---you might not even need to read any +instructions (although it's always a good idea to do so anyways). + +Of course, things will not always be that easy. You might be interested in a +module distribution that doesn't have an easy-to-use installer for your +platform. In that case, you'll have to start with the source distribution +released by the module's author/maintainer. Installing from a source +distribution is not too hard, as long as the modules are packaged in the +standard way. The bulk of this document is about building and installing +modules from standard source distributions. + + +.. _inst-new-standard: + +The new standard: Distutils +--------------------------- + +If you download a module source distribution, you can tell pretty quickly if it +was packaged and distributed in the standard way, i.e. using the Distutils. +First, the distribution's name and version number will be featured prominently +in the name of the downloaded archive, e.g. :file:`foo-1.0.tar.gz` or +:file:`widget-0.9.7.zip`. Next, the archive will unpack into a similarly-named +directory: :file:`foo-1.0` or :file:`widget-0.9.7`. Additionally, the +distribution will contain a setup script :file:`setup.py`, and a file named +:file:`README.txt` or possibly just :file:`README`, which should explain that +building and installing the module distribution is a simple matter of running :: + + python setup.py install + +If all these things are true, then you already know how to build and install the +modules you've just downloaded: Run the command above. Unless you need to +install things in a non-standard way or customize the build process, you don't +really need this manual. Or rather, the above command is everything you need to +get out of this manual. + + +.. _inst-standard-install: + +Standard Build and Install +========================== + +As described in section :ref:`inst-new-standard`, building and installing a module +distribution using the Distutils is usually one simple command:: + + python setup.py install + +On Unix, you'd run this command from a shell prompt; on Windows, you have to +open a command prompt window ("DOS box") and do it there; on Mac OS X, you open +a :command:`Terminal` window to get a shell prompt. + + +.. _inst-platform-variations: + +Platform variations +------------------- + +You should always run the setup command from the distribution root directory, +i.e. the top-level subdirectory that the module source distribution unpacks +into. For example, if you've just downloaded a module source distribution +:file:`foo-1.0.tar.gz` onto a Unix system, the normal thing to do is:: + + gunzip -c foo-1.0.tar.gz | tar xf - # unpacks into directory foo-1.0 + cd foo-1.0 + python setup.py install + +On Windows, you'd probably download :file:`foo-1.0.zip`. If you downloaded the +archive file to :file:`C:\\Temp`, then it would unpack into +:file:`C:\\Temp\\foo-1.0`; you can use either a archive manipulator with a +graphical user interface (such as WinZip) or a command-line tool (such as +:program:`unzip` or :program:`pkunzip`) to unpack the archive. Then, open a +command prompt window ("DOS box"), and run:: + + cd c:\Temp\foo-1.0 + python setup.py install + + +.. _inst-splitting-up: + +Splitting the job up +-------------------- + +Running ``setup.py install`` builds and installs all modules in one run. If you +prefer to work incrementally---especially useful if you want to customize the +build process, or if things are going wrong---you can use the setup script to do +one thing at a time. This is particularly helpful when the build and install +will be done by different users---for example, you might want to build a module +distribution and hand it off to a system administrator for installation (or do +it yourself, with super-user privileges). + +For example, you can build everything in one step, and then install everything +in a second step, by invoking the setup script twice:: + + python setup.py build + python setup.py install + +If you do this, you will notice that running the :command:`install` command +first runs the :command:`build` command, which---in this case---quickly notices +that it has nothing to do, since everything in the :file:`build` directory is +up-to-date. + +You may not need this ability to break things down often if all you do is +install modules downloaded off the 'net, but it's very handy for more advanced +tasks. If you get into distributing your own Python modules and extensions, +you'll run lots of individual Distutils commands on their own. + + +.. _inst-how-build-works: + +How building works +------------------ + +As implied above, the :command:`build` command is responsible for putting the +files to install into a *build directory*. By default, this is :file:`build` +under the distribution root; if you're excessively concerned with speed, or want +to keep the source tree pristine, you can change the build directory with the +:option:`--build-base` option. For example:: + + python setup.py build --build-base /tmp/pybuild/foo-1.0 + +(Or you could do this permanently with a directive in your system or personal +Distutils configuration file; see section :ref:`inst-config-files`.) Normally, +this isn't necessary. + +The default layout for the build tree is as follows:: + + --- build/ --- lib/ + or + --- build/ --- lib./ + temp./ + +where ```` expands to a brief description of the current OS/hardware +platform and Python version. The first form, with just a :file:`lib` directory, +is used for "pure module distributions"---that is, module distributions that +include only pure Python modules. If a module distribution contains any +extensions (modules written in C/C++), then the second form, with two ```` +directories, is used. In that case, the :file:`temp.{plat}` directory holds +temporary files generated by the compile/link process that don't actually get +installed. In either case, the :file:`lib` (or :file:`lib.{plat}`) directory +contains all Python modules (pure Python and extensions) that will be installed. + +In the future, more directories will be added to handle Python scripts, +documentation, binary executables, and whatever else is needed to handle the job +of installing Python modules and applications. + + +.. _inst-how-install-works: + +How installation works +---------------------- + +After the :command:`build` command runs (whether you run it explicitly, or the +:command:`install` command does it for you), the work of the :command:`install` +command is relatively simple: all it has to do is copy everything under +:file:`build/lib` (or :file:`build/lib.{plat}`) to your chosen installation +directory. + +If you don't choose an installation directory---i.e., if you just run ``setup.py +install``\ ---then the :command:`install` command installs to the standard +location for third-party Python modules. This location varies by platform and +by how you built/installed Python itself. On Unix (and Mac OS X, which is also +Unix-based), it also depends on whether the module distribution being installed +is pure Python or contains extensions ("non-pure"): + ++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+ +| Platform | Standard installation location | Default value | Notes | ++=================+=====================================================+==================================================+=======+ +| Unix (pure) | :file:`{prefix}/lib/python{X.Y}/site-packages` | :file:`/usr/local/lib/python{X.Y}/site-packages` | \(1) | ++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+ +| Unix (non-pure) | :file:`{exec-prefix}/lib/python{X.Y}/site-packages` | :file:`/usr/local/lib/python{X.Y}/site-packages` | \(1) | ++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+ +| Windows | :file:`{prefix}` | :file:`C:\\Python` | \(2) | ++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+ + +Notes: + +(1) + Most Linux distributions include Python as a standard part of the system, so + :file:`{prefix}` and :file:`{exec-prefix}` are usually both :file:`/usr` on + Linux. If you build Python yourself on Linux (or any Unix-like system), the + default :file:`{prefix}` and :file:`{exec-prefix}` are :file:`/usr/local`. + +(2) + The default installation directory on Windows was :file:`C:\\Program + Files\\Python` under Python 1.6a1, 1.5.2, and earlier. + +:file:`{prefix}` and :file:`{exec-prefix}` stand for the directories that Python +is installed to, and where it finds its libraries at run-time. They are always +the same under Windows, and very often the same under Unix and Mac OS X. You +can find out what your Python installation uses for :file:`{prefix}` and +:file:`{exec-prefix}` by running Python in interactive mode and typing a few +simple commands. Under Unix, just type ``python`` at the shell prompt. Under +Windows, choose :menuselection:`Start --> Programs --> Python X.Y --> +Python (command line)`. Once the interpreter is started, you type Python code +at the prompt. For example, on my Linux system, I type the three Python +statements shown below, and get the output as shown, to find out my +:file:`{prefix}` and :file:`{exec-prefix}`:: + + Python 2.4 (#26, Aug 7 2004, 17:19:02) + Type "help", "copyright", "credits" or "license" for more information. + >>> import sys + >>> sys.prefix + '/usr' + >>> sys.exec_prefix + '/usr' + +If you don't want to install modules to the standard location, or if you don't +have permission to write there, then you need to read about alternate +installations in section :ref:`inst-alt-install`. If you want to customize your +installation directories more heavily, see section :ref:`inst-custom-install` on +custom installations. + + +.. _inst-alt-install: + +Alternate Installation +====================== + +Often, it is necessary or desirable to install modules to a location other than +the standard location for third-party Python modules. For example, on a Unix +system you might not have permission to write to the standard third-party module +directory. Or you might wish to try out a module before making it a standard +part of your local Python installation. This is especially true when upgrading +a distribution already present: you want to make sure your existing base of +scripts still works with the new version before actually upgrading. + +The Distutils :command:`install` command is designed to make installing module +distributions to an alternate location simple and painless. The basic idea is +that you supply a base directory for the installation, and the +:command:`install` command picks a set of directories (called an *installation +scheme*) under this base directory in which to install files. The details +differ across platforms, so read whichever of the following sections applies to +you. + + +.. _inst-alt-install-prefix: + +Alternate installation: the home scheme +--------------------------------------- + +The idea behind the "home scheme" is that you build and maintain a personal +stash of Python modules. This scheme's name is derived from the idea of a +"home" directory on Unix, since it's not unusual for a Unix user to make their +home directory have a layout similar to :file:`/usr/` or :file:`/usr/local/`. +This scheme can be used by anyone, regardless of the operating system they +are installing for. + +Installing a new module distribution is as simple as :: + + python setup.py install --home + +where you can supply any directory you like for the :option:`--home` option. On +Unix, lazy typists can just type a tilde (``~``); the :command:`install` command +will expand this to your home directory:: + + python setup.py install --home ~ + +The :option:`--home` option defines the installation base directory. Files are +installed to the following directories under the installation base as follows: + ++------------------------------+---------------------------+-----------------------------+ +| Type of file | Installation Directory | Override option | ++==============================+===========================+=============================+ +| pure module distribution | :file:`{home}/lib/python` | :option:`--install-purelib` | ++------------------------------+---------------------------+-----------------------------+ +| non-pure module distribution | :file:`{home}/lib/python` | :option:`--install-platlib` | ++------------------------------+---------------------------+-----------------------------+ +| scripts | :file:`{home}/bin` | :option:`--install-scripts` | ++------------------------------+---------------------------+-----------------------------+ +| data | :file:`{home}/share` | :option:`--install-data` | ++------------------------------+---------------------------+-----------------------------+ + + +.. _inst-alt-install-home: + +Alternate installation: Unix (the prefix scheme) +------------------------------------------------ + +The "prefix scheme" is useful when you wish to use one Python installation to +perform the build/install (i.e., to run the setup script), but install modules +into the third-party module directory of a different Python installation (or +something that looks like a different Python installation). If this sounds a +trifle unusual, it is---that's why the "home scheme" comes first. However, +there are at least two known cases where the prefix scheme will be useful. + +First, consider that many Linux distributions put Python in :file:`/usr`, rather +than the more traditional :file:`/usr/local`. This is entirely appropriate, +since in those cases Python is part of "the system" rather than a local add-on. +However, if you are installing Python modules from source, you probably want +them to go in :file:`/usr/local/lib/python2.{X}` rather than +:file:`/usr/lib/python2.{X}`. This can be done with :: + + /usr/bin/python setup.py install --prefix /usr/local + +Another possibility is a network filesystem where the name used to write to a +remote directory is different from the name used to read it: for example, the +Python interpreter accessed as :file:`/usr/local/bin/python` might search for +modules in :file:`/usr/local/lib/python2.{X}`, but those modules would have to +be installed to, say, :file:`/mnt/{@server}/export/lib/python2.{X}`. This could +be done with :: + + /usr/local/bin/python setup.py install --prefix=/mnt/@server/export + +In either case, the :option:`--prefix` option defines the installation base, and +the :option:`--exec-prefix` option defines the platform-specific installation +base, which is used for platform-specific files. (Currently, this just means +non-pure module distributions, but could be expanded to C libraries, binary +executables, etc.) If :option:`--exec-prefix` is not supplied, it defaults to +:option:`--prefix`. Files are installed as follows: + ++------------------------------+-----------------------------------------------------+-----------------------------+ +| Type of file | Installation Directory | Override option | ++==============================+=====================================================+=============================+ +| pure module distribution | :file:`{prefix}/lib/python{X.Y}/site-packages` | :option:`--install-purelib` | ++------------------------------+-----------------------------------------------------+-----------------------------+ +| non-pure module distribution | :file:`{exec-prefix}/lib/python{X.Y}/site-packages` | :option:`--install-platlib` | ++------------------------------+-----------------------------------------------------+-----------------------------+ +| scripts | :file:`{prefix}/bin` | :option:`--install-scripts` | ++------------------------------+-----------------------------------------------------+-----------------------------+ +| data | :file:`{prefix}/share` | :option:`--install-data` | ++------------------------------+-----------------------------------------------------+-----------------------------+ + +There is no requirement that :option:`--prefix` or :option:`--exec-prefix` +actually point to an alternate Python installation; if the directories listed +above do not already exist, they are created at installation time. + +Incidentally, the real reason the prefix scheme is important is simply that a +standard Unix installation uses the prefix scheme, but with :option:`--prefix` +and :option:`--exec-prefix` supplied by Python itself as ``sys.prefix`` and +``sys.exec_prefix``. Thus, you might think you'll never use the prefix scheme, +but every time you run ``python setup.py install`` without any other options, +you're using it. + +Note that installing extensions to an alternate Python installation has no +effect on how those extensions are built: in particular, the Python header files +(:file:`Python.h` and friends) installed with the Python interpreter used to run +the setup script will be used in compiling extensions. It is your +responsibility to ensure that the interpreter used to run extensions installed +in this way is compatible with the interpreter used to build them. The best way +to do this is to ensure that the two interpreters are the same version of Python +(possibly different builds, or possibly copies of the same build). (Of course, +if your :option:`--prefix` and :option:`--exec-prefix` don't even point to an +alternate Python installation, this is immaterial.) + + +.. _inst-alt-install-windows: + +Alternate installation: Windows (the prefix scheme) +--------------------------------------------------- + +Windows has no concept of a user's home directory, and since the standard Python +installation under Windows is simpler than under Unix, the :option:`--prefix` +option has traditionally been used to install additional packages in separate +locations on Windows. :: + + python setup.py install --prefix "\Temp\Python" + +to install modules to the :file:`\\Temp\\Python` directory on the current drive. + +The installation base is defined by the :option:`--prefix` option; the +:option:`--exec-prefix` option is not supported under Windows. Files are +installed as follows: + ++------------------------------+---------------------------+-----------------------------+ +| Type of file | Installation Directory | Override option | ++==============================+===========================+=============================+ +| pure module distribution | :file:`{prefix}` | :option:`--install-purelib` | ++------------------------------+---------------------------+-----------------------------+ +| non-pure module distribution | :file:`{prefix}` | :option:`--install-platlib` | ++------------------------------+---------------------------+-----------------------------+ +| scripts | :file:`{prefix}\\Scripts` | :option:`--install-scripts` | ++------------------------------+---------------------------+-----------------------------+ +| data | :file:`{prefix}\\Data` | :option:`--install-data` | ++------------------------------+---------------------------+-----------------------------+ + + +.. _inst-custom-install: + +Custom Installation +=================== + +Sometimes, the alternate installation schemes described in section +:ref:`inst-alt-install` just don't do what you want. You might want to tweak +just one or two directories while keeping everything under the same base +directory, or you might want to completely redefine the installation scheme. +In either case, you're creating a *custom installation scheme*. + +You probably noticed the column of "override options" in the tables describing +the alternate installation schemes above. Those options are how you define a +custom installation scheme. These override options can be relative, absolute, +or explicitly defined in terms of one of the installation base directories. +(There are two installation base directories, and they are normally the same--- +they only differ when you use the Unix "prefix scheme" and supply different +:option:`--prefix` and :option:`--exec-prefix` options.) + +For example, say you're installing a module distribution to your home directory +under Unix---but you want scripts to go in :file:`~/scripts` rather than +:file:`~/bin`. As you might expect, you can override this directory with the +:option:`--install-scripts` option; in this case, it makes most sense to supply +a relative path, which will be interpreted relative to the installation base +directory (your home directory, in this case):: + + python setup.py install --home ~ --install-scripts scripts + +Another Unix example: suppose your Python installation was built and installed +with a prefix of :file:`/usr/local/python`, so under a standard installation +scripts will wind up in :file:`/usr/local/python/bin`. If you want them in +:file:`/usr/local/bin` instead, you would supply this absolute directory for the +:option:`--install-scripts` option:: + + python setup.py install --install-scripts /usr/local/bin + +(This performs an installation using the "prefix scheme," where the prefix is +whatever your Python interpreter was installed with--- :file:`/usr/local/python` +in this case.) + +If you maintain Python on Windows, you might want third-party modules to live in +a subdirectory of :file:`{prefix}`, rather than right in :file:`{prefix}` +itself. This is almost as easy as customizing the script installation directory +---you just have to remember that there are two types of modules to worry about, +pure modules and non-pure modules (i.e., modules from a non-pure distribution). +For example:: + + python setup.py install --install-purelib Site --install-platlib Site + +The specified installation directories are relative to :file:`{prefix}`. Of +course, you also have to ensure that these directories are in Python's module +search path, such as by putting a :file:`.pth` file in :file:`{prefix}`. See +section :ref:`inst-search-path` to find out how to modify Python's search path. + +If you want to define an entire installation scheme, you just have to supply all +of the installation directory options. The recommended way to do this is to +supply relative paths; for example, if you want to maintain all Python +module-related files under :file:`python` in your home directory, and you want a +separate directory for each platform that you use your home directory from, you +might define the following installation scheme:: + + python setup.py install --home ~ \ + --install-purelib python/lib \ + --install-platlib python/'lib.$PLAT' \ + --install-scripts python/scripts + --install-data python/data + +or, equivalently, :: + + python setup.py install --home ~/python \ + --install-purelib lib \ + --install-platlib 'lib.$PLAT' \ + --install-scripts scripts + --install-data data + +``$PLAT`` is not (necessarily) an environment variable---it will be expanded by +the Distutils as it parses your command line options, just as it does when +parsing your configuration file(s). + +Obviously, specifying the entire installation scheme every time you install a +new module distribution would be very tedious. Thus, you can put these options +into your Distutils config file (see section :ref:`inst-config-files`):: + + [install] + install-base = $HOME + install-purelib = python/lib + install-platlib = python/lib.$PLAT + install-scripts = python/scripts + install-data = python/data + +or, equivalently, :: + + [install] + install-base = $HOME/python + install-purelib = lib + install-platlib = lib.$PLAT + install-scripts = scripts + install-data = data + +Note that these two are *not* equivalent if you supply a different installation +base directory when you run the setup script. For example, :: + + python setup.py install --install-base /tmp + +would install pure modules to :file:`{/tmp/python/lib}` in the first case, and +to :file:`{/tmp/lib}` in the second case. (For the second case, you probably +want to supply an installation base of :file:`/tmp/python`.) + +You probably noticed the use of ``$HOME`` and ``$PLAT`` in the sample +configuration file input. These are Distutils configuration variables, which +bear a strong resemblance to environment variables. In fact, you can use +environment variables in config files on platforms that have such a notion but +the Distutils additionally define a few extra variables that may not be in your +environment, such as ``$PLAT``. (And of course, on systems that don't have +environment variables, such as Mac OS 9, the configuration variables supplied by +the Distutils are the only ones you can use.) See section :ref:`inst-config-files` +for details. + +.. XXX need some Windows examples---when would custom installation schemes be + needed on those platforms? + + +.. XXX I'm not sure where this section should go. + +.. _inst-search-path: + +Modifying Python's Search Path +------------------------------ + +When the Python interpreter executes an :keyword:`import` statement, it searches +for both Python code and extension modules along a search path. A default value +for the path is configured into the Python binary when the interpreter is built. +You can determine the path by importing the :mod:`sys` module and printing the +value of ``sys.path``. :: + + $ python + Python 2.2 (#11, Oct 3 2002, 13:31:27) + [GCC 2.96 20000731 (Red Hat Linux 7.3 2.96-112)] on linux2 + Type "help", "copyright", "credits" or "license" for more information. + >>> import sys + >>> sys.path + ['', '/usr/local/lib/python2.3', '/usr/local/lib/python2.3/plat-linux2', + '/usr/local/lib/python2.3/lib-tk', '/usr/local/lib/python2.3/lib-dynload', + '/usr/local/lib/python2.3/site-packages'] + >>> + +The null string in ``sys.path`` represents the current working directory. + +The expected convention for locally installed packages is to put them in the +:file:`{...}/site-packages/` directory, but you may want to install Python +modules into some arbitrary directory. For example, your site may have a +convention of keeping all software related to the web server under :file:`/www`. +Add-on Python modules might then belong in :file:`/www/python`, and in order to +import them, this directory must be added to ``sys.path``. There are several +different ways to add the directory. + +The most convenient way is to add a path configuration file to a directory +that's already on Python's path, usually to the :file:`.../site-packages/` +directory. Path configuration files have an extension of :file:`.pth`, and each +line must contain a single path that will be appended to ``sys.path``. (Because +the new paths are appended to ``sys.path``, modules in the added directories +will not override standard modules. This means you can't use this mechanism for +installing fixed versions of standard modules.) + +Paths can be absolute or relative, in which case they're relative to the +directory containing the :file:`.pth` file. See the documentation of +the :mod:`site` module for more information. + +A slightly less convenient way is to edit the :file:`site.py` file in Python's +standard library, and modify ``sys.path``. :file:`site.py` is automatically +imported when the Python interpreter is executed, unless the :option:`-S` switch +is supplied to suppress this behaviour. So you could simply edit +:file:`site.py` and add two lines to it:: + + import sys + sys.path.append('/www/python/') + +However, if you reinstall the same major version of Python (perhaps when +upgrading from 2.2 to 2.2.2, for example) :file:`site.py` will be overwritten by +the stock version. You'd have to remember that it was modified and save a copy +before doing the installation. + +There are two environment variables that can modify ``sys.path``. +:envvar:`PYTHONHOME` sets an alternate value for the prefix of the Python +installation. For example, if :envvar:`PYTHONHOME` is set to ``/www/python``, +the search path will be set to ``['', '/www/python/lib/pythonX.Y/', +'/www/python/lib/pythonX.Y/plat-linux2', ...]``. + +The :envvar:`PYTHONPATH` variable can be set to a list of paths that will be +added to the beginning of ``sys.path``. For example, if :envvar:`PYTHONPATH` is +set to ``/www/python:/opt/py``, the search path will begin with +``['/www/python', '/opt/py']``. (Note that directories must exist in order to +be added to ``sys.path``; the :mod:`site` module removes paths that don't +exist.) + +Finally, ``sys.path`` is just a regular Python list, so any Python application +can modify it by adding or removing entries. + + +.. _inst-config-files: + +Distutils Configuration Files +============================= + +As mentioned above, you can use Distutils configuration files to record personal +or site preferences for any Distutils options. That is, any option to any +command can be stored in one of two or three (depending on your platform) +configuration files, which will be consulted before the command-line is parsed. +This means that configuration files will override default values, and the +command-line will in turn override configuration files. Furthermore, if +multiple configuration files apply, values from "earlier" files are overridden +by "later" files. + + +.. _inst-config-filenames: + +Location and names of config files +---------------------------------- + +The names and locations of the configuration files vary slightly across +platforms. On Unix and Mac OS X, the three configuration files (in the order +they are processed) are: + ++--------------+----------------------------------------------------------+-------+ +| Type of file | Location and filename | Notes | ++==============+==========================================================+=======+ +| system | :file:`{prefix}/lib/python{ver}/distutils/distutils.cfg` | \(1) | ++--------------+----------------------------------------------------------+-------+ +| personal | :file:`$HOME/.pydistutils.cfg` | \(2) | ++--------------+----------------------------------------------------------+-------+ +| local | :file:`setup.cfg` | \(3) | ++--------------+----------------------------------------------------------+-------+ + +And on Windows, the configuration files are: + ++--------------+-------------------------------------------------+-------+ +| Type of file | Location and filename | Notes | ++==============+=================================================+=======+ +| system | :file:`{prefix}\\Lib\\distutils\\distutils.cfg` | \(4) | ++--------------+-------------------------------------------------+-------+ +| personal | :file:`%HOME%\\pydistutils.cfg` | \(5) | ++--------------+-------------------------------------------------+-------+ +| local | :file:`setup.cfg` | \(3) | ++--------------+-------------------------------------------------+-------+ + +On all platforms, the "personal" file can be temporarily disabled by +passing the `--no-user-cfg` option. + +Notes: + +(1) + Strictly speaking, the system-wide configuration file lives in the directory + where the Distutils are installed; under Python 1.6 and later on Unix, this + is as shown. For Python 1.5.2, the Distutils will normally be installed to + :file:`{prefix}/lib/python1.5/site-packages/distutils`, so the system + configuration file should be put there under Python 1.5.2. + +(2) + On Unix, if the :envvar:`HOME` environment variable is not defined, the + user's home directory will be determined with the :func:`getpwuid` function + from the standard :mod:`pwd` module. This is done by the + :func:`os.path.expanduser` function used by Distutils. + +(3) + I.e., in the current directory (usually the location of the setup script). + +(4) + (See also note (1).) Python's default installation prefix is + :file:`C:\\Python`, so the system configuration file is normally + :file:`C:\\Python\\Lib\\distutils\\distutils.cfg`. + +(5) + On Windows, if the :envvar:`HOME` environment variable is not defined, + :envvar:`USERPROFILE` then :envvar:`HOMEDRIVE` and :envvar:`HOMEPATH` will + be tried. This is done by the :func:`os.path.expanduser` function used + by Distutils. + + +.. _inst-config-syntax: + +Syntax of config files +---------------------- + +The Distutils configuration files all have the same syntax. The config files +are grouped into sections. There is one section for each Distutils command, +plus a ``global`` section for global options that affect every command. Each +section consists of one option per line, specified as ``option = value``. + +For example, the following is a complete config file that just forces all +commands to run quietly by default:: + + [global] + verbose = 0 + +If this is installed as the system config file, it will affect all processing +of any Python module distribution by any user on the current system. If it is +installed as your personal config file (on systems that support them), it will +affect only module distributions processed by you. And if it is used as the +:file:`setup.cfg` for a particular module distribution, it affects only that +distribution. + +You could override the default "build base" directory and make the +:command:`build\*` commands always forcibly rebuild all files with the +following:: + + [build] + build-base = blib + force = 1 + +which corresponds to the command-line arguments :: + + python setup.py build --build-base blib --force + +except that including the :command:`build` command on the command-line means +that command will be run. Including a particular command in config files has no +such implication; it only means that if the command is run, the options in the +config file will apply. (Or if other commands that derive values from it are +run, they will use the values in the config file.) + +You can find out the complete list of options for any command using the +:option:`--help` option, e.g.:: + + python setup.py build --help + +and you can find out the complete list of global options by using +:option:`--help` without a command:: + + python setup.py --help + +See also the "Reference" section of the "Distributing Python Modules" manual. + + +.. _inst-building-ext: + +Building Extensions: Tips and Tricks +==================================== + +Whenever possible, the Distutils try to use the configuration information made +available by the Python interpreter used to run the :file:`setup.py` script. +For example, the same compiler and linker flags used to compile Python will also +be used for compiling extensions. Usually this will work well, but in +complicated situations this might be inappropriate. This section discusses how +to override the usual Distutils behaviour. + + +.. _inst-tweak-flags: + +Tweaking compiler/linker flags +------------------------------ + +Compiling a Python extension written in C or C++ will sometimes require +specifying custom flags for the compiler and linker in order to use a particular +library or produce a special kind of object code. This is especially true if the +extension hasn't been tested on your platform, or if you're trying to +cross-compile Python. + +.. TODO update to new setup.cfg + +In the most general case, the extension author might have foreseen that +compiling the extensions would be complicated, and provided a :file:`Setup` file +for you to edit. This will likely only be done if the module distribution +contains many separate extension modules, or if they often require elaborate +sets of compiler flags in order to work. + +A :file:`Setup` file, if present, is parsed in order to get a list of extensions +to build. Each line in a :file:`Setup` describes a single module. Lines have +the following structure:: + + module ... [sourcefile ...] [cpparg ...] [library ...] + + +Let's examine each of the fields in turn. + +* *module* is the name of the extension module to be built, and should be a + valid Python identifier. You can't just change this in order to rename a module + (edits to the source code would also be needed), so this should be left alone. + +* *sourcefile* is anything that's likely to be a source code file, at least + judging by the filename. Filenames ending in :file:`.c` are assumed to be + written in C, filenames ending in :file:`.C`, :file:`.cc`, and :file:`.c++` are + assumed to be C++, and filenames ending in :file:`.m` or :file:`.mm` are assumed + to be in Objective C. + +* *cpparg* is an argument for the C preprocessor, and is anything starting with + :option:`-I`, :option:`-D`, :option:`-U` or :option:`-C`. + +* *library* is anything ending in :file:`.a` or beginning with :option:`-l` or + :option:`-L`. + +If a particular platform requires a special library on your platform, you can +add it by editing the :file:`Setup` file and running ``python setup.py build``. +For example, if the module defined by the line :: + + foo foomodule.c + +must be linked with the math library :file:`libm.a` on your platform, simply add +:option:`-lm` to the line:: + + foo foomodule.c -lm + +Arbitrary switches intended for the compiler or the linker can be supplied with +the :option:`-Xcompiler` *arg* and :option:`-Xlinker` *arg* options:: + + foo foomodule.c -Xcompiler -o32 -Xlinker -shared -lm + +The next option after :option:`-Xcompiler` and :option:`-Xlinker` will be +appended to the proper command line, so in the above example the compiler will +be passed the :option:`-o32` option, and the linker will be passed +:option:`-shared`. If a compiler option requires an argument, you'll have to +supply multiple :option:`-Xcompiler` options; for example, to pass ``-x c++`` +the :file:`Setup` file would have to contain ``-Xcompiler -x -Xcompiler c++``. + +Compiler flags can also be supplied through setting the :envvar:`CFLAGS` +environment variable. If set, the contents of :envvar:`CFLAGS` will be added to +the compiler flags specified in the :file:`Setup` file. + + +.. _inst-non-ms-compilers: + +Using non-Microsoft compilers on Windows +---------------------------------------- + +.. sectionauthor:: Rene Liebscher + + + +Borland/CodeGear C++ +^^^^^^^^^^^^^^^^^^^^ + +This subsection describes the necessary steps to use Distutils with the Borland +C++ compiler version 5.5. First you have to know that Borland's object file +format (OMF) is different from the format used by the Python version you can +download from the Python or ActiveState Web site. (Python is built with +Microsoft Visual C++, which uses COFF as the object file format.) For this +reason you have to convert Python's library :file:`python25.lib` into the +Borland format. You can do this as follows: + +.. Should we mention that users have to create cfg-files for the compiler? +.. see also http://community.borland.com/article/0,1410,21205,00.html + +:: + + coff2omf python25.lib python25_bcpp.lib + +The :file:`coff2omf` program comes with the Borland compiler. The file +:file:`python25.lib` is in the :file:`Libs` directory of your Python +installation. If your extension uses other libraries (zlib, ...) you have to +convert them too. + +The converted files have to reside in the same directories as the normal +libraries. + +How does Distutils manage to use these libraries with their changed names? If +the extension needs a library (eg. :file:`foo`) Distutils checks first if it +finds a library with suffix :file:`_bcpp` (eg. :file:`foo_bcpp.lib`) and then +uses this library. In the case it doesn't find such a special library it uses +the default name (:file:`foo.lib`.) [#]_ + +To let Distutils compile your extension with Borland C++ you now have to type:: + + python setup.py build --compiler bcpp + +If you want to use the Borland C++ compiler as the default, you could specify +this in your personal or system-wide configuration file for Distutils (see +section :ref:`inst-config-files`.) + + +.. seealso:: + + `C++Builder Compiler `_ + Information about the free C++ compiler from Borland, including links to the + download pages. + + `Creating Python Extensions Using Borland's Free Compiler `_ + Document describing how to use Borland's free command-line C++ compiler to build + Python. + + +GNU C / Cygwin / MinGW +^^^^^^^^^^^^^^^^^^^^^^ + +These instructions only apply if you're using a version of Python prior to +2.4.1 with a MinGW prior to 3.0.0 (with binutils-2.13.90-20030111-1). + +This section describes the necessary steps to use Distutils with the GNU C/C++ +compilers in their Cygwin and MinGW distributions. [#]_ For a Python interpreter +that was built with Cygwin, everything should work without any of these +following steps. + +These compilers require some special libraries. This task is more complex than +for Borland's C++, because there is no program to convert the library. First +you have to create a list of symbols which the Python DLL exports. (You can find +a good program for this task at +http://www.emmestech.com/software/pexports-0.43/download_pexports.html). + +.. I don't understand what the next line means. --amk +.. (inclusive the references on data structures.) + +:: + + pexports python25.dll > python25.def + +The location of an installed :file:`python25.dll` will depend on the +installation options and the version and language of Windows. In a "just for +me" installation, it will appear in the root of the installation directory. In +a shared installation, it will be located in the system directory. + +Then you can create from these information an import library for gcc. :: + + /cygwin/bin/dlltool --dllname python25.dll --def python25.def --output-lib libpython25.a + +The resulting library has to be placed in the same directory as +:file:`python25.lib`. (Should be the :file:`libs` directory under your Python +installation directory.) + +If your extension uses other libraries (zlib,...) you might have to convert +them too. The converted files have to reside in the same directories as the +normal libraries do. + +To let Distutils compile your extension with Cygwin you now have to type :: + + python setup.py build --compiler cygwin + +and for Cygwin in no-cygwin mode [#]_ or for MinGW type:: + + python setup.py build --compiler mingw32 + +If you want to use any of these options/compilers as default, you should +consider to write it in your personal or system-wide configuration file for +Distutils (see section :ref:`inst-config-files`.) + + +.. seealso:: + + `Building Python modules on MS Windows platform with MinGW `_ + Information about building the required libraries for the MinGW + environment. + + +.. rubric:: Footnotes + +.. [#] This also means you could replace all existing COFF-libraries with + OMF-libraries of the same name. + +.. [#] Check http://sources.redhat.com/cygwin/ and http://www.mingw.org/ for + more information + +.. [#] Then you have no POSIX emulation available, but you also don't need + :file:`cygwin1.dll`. diff --git a/docs/source/depgraph.rst b/docs/source/library/distutils2.depgraph.rst rename from docs/source/depgraph.rst rename to docs/source/library/distutils2.depgraph.rst --- a/docs/source/depgraph.rst +++ b/docs/source/library/distutils2.depgraph.rst @@ -60,7 +60,7 @@ Now, we proceed with generating a graphical representation of the graph. First we write it to a file, and then we generate a PNG image using the ``dot`` -command line tool:: +command-line tool:: from distutils2.depgraph import graph_to_dot f = open('output.dot', 'w') @@ -75,7 +75,7 @@ An example output image is: -.. figure:: images/depgraph_output.png +.. figure:: ../images/depgraph_output.png :alt: An example dot output If you want to include ``.egg`` and ``.egg-info`` distributions as well, then diff --git a/docs/source/projects-index.client.rst b/docs/source/library/distutils2.index.client.rst rename from docs/source/projects-index.client.rst rename to docs/source/library/distutils2.index.client.rst diff --git a/docs/source/projects-index.dist.rst b/docs/source/library/distutils2.index.dist.rst rename from docs/source/projects-index.dist.rst rename to docs/source/library/distutils2.index.dist.rst --- a/docs/source/projects-index.dist.rst +++ b/docs/source/library/distutils2.index.dist.rst @@ -3,10 +3,10 @@ ================================================== Informations coming from indexes are represented by the classes present in the -`dist` module. +:mod:`distutils2.index.dist` module. -APIs -==== +API +=== Keep in mind that each project (eg. FooBar) can have several releases (eg. 1.1, 1.2, 1.3), and each of these releases can be provided in multiple @@ -37,9 +37,9 @@ ReleasesList ------------ -The `dist` module also provides another class, to work with lists of -:class:`distutils.index.dist.ReleaseInfo` classes. It allow to filter -and order results. +The :mod:`~distutils2.index.dist` module also provides another class, to work +with lists of :class:`distutils2.index.dist.ReleaseInfo` classes. It allow to +filter and order results. .. autoclass:: distutils2.index.dist.ReleasesList :members: @@ -100,6 +100,8 @@ >>> r.fetch_metadata() +.. XXX add proper roles to these constructs + Like this, it's possible to retrieve project's releases (`fetch_releases`), releases metadata (`fetch_metadata` and releases distributions (`fetch_distributions` informations). diff --git a/docs/source/projects-index.rst b/docs/source/library/distutils2.index.rst rename from docs/source/projects-index.rst rename to docs/source/library/distutils2.index.rst --- a/docs/source/projects-index.rst +++ b/docs/source/library/distutils2.index.rst @@ -2,27 +2,30 @@ Query Python Package Indexes (PyPI) =================================== -Distutils2 provides facilities to access python package informations stored in -indexes. The main Python Package Index is available at http://pypi.python.org. +Distutils2 queries PyPI to get information about projects or download those +projects. The low-level facilities used internally are also part of the public +API destined to be used by other tools. -.. note:: The tools provided in distutils2 are not limited to query pypi, and - can be used for others indexes, if they respect the same interfaces. +The :mod:`distutils2.index` package provides those facilities, which can be +used to access informations about Python projects registered at indexes, the +main one being PyPI, located ad http://pypi.python.org/. There is two ways to retrieve data from these indexes: using the *simple* API, and using *XML-RPC*. The first one is a set of HTML pages avalaibles at `http://pypi.python.org/simple/`, and the second one contains a set of XML-RPC -methods. +methods. Mirrors typically implement the simple interface. If you dont care about which API to use, the best thing to do is to let distutils2 decide this for you, by using :class:`distutils2.index.ClientWrapper`. Of course, you can rely too on :class:`distutils2.index.simple.Crawler` and -:class:`distutils.index.xmlrpc.Client` if you need to use these specific APIs. +:class:`distutils2.index.xmlrpc.Client` if you need to use a specific API. .. toctree:: :maxdepth: 2 + :numbered: - projects-index.client.rst - projects-index.dist.rst - projects-index.simple.rst - projects-index.xmlrpc.rst + distutils2.index.client + distutils2.index.dist + distutils2.index.simple + distutils2.index.xmlrpc diff --git a/docs/source/projects-index.simple.rst b/docs/source/library/distutils2.index.simple.rst rename from docs/source/projects-index.simple.rst rename to docs/source/library/distutils2.index.simple.rst --- a/docs/source/projects-index.simple.rst +++ b/docs/source/library/distutils2.index.simple.rst @@ -85,7 +85,7 @@ downloads. It's possible to tell the PyPIClient to follow external links by setting the -`follow_externals` attribute, on instanciation or after:: +`follow_externals` attribute, on instantiation or after:: >>> client = Crawler(follow_externals=True) @@ -112,7 +112,7 @@ You also can specify mirrors to fallback on in case the first index_url you provided doesnt respond, or not correctly. The default behavior for `Crawler` is to use the list provided by Python.org DNS records, as -described in the :pep:`381` about mirroring infrastructure. +described in the :PEP:`381` about mirroring infrastructure. If you don't want to rely on these, you could specify the list of mirrors you want to try by specifying the `mirrors` attribute. It's a simple iterable:: diff --git a/docs/source/projects-index.xmlrpc.rst b/docs/source/library/distutils2.index.xmlrpc.rst rename from docs/source/projects-index.xmlrpc.rst rename to docs/source/library/distutils2.index.xmlrpc.rst diff --git a/docs/source/metadata.rst b/docs/source/library/distutils2.metadata.rst rename from docs/source/metadata.rst rename to docs/source/library/distutils2.metadata.rst --- a/docs/source/metadata.rst +++ b/docs/source/library/distutils2.metadata.rst @@ -3,15 +3,15 @@ ======== Distutils2 provides a :class:`DistributionMetadata` class that can read and -write Metadata files. This class is compatible with all versions of Metadata: +write metadata files. This class is compatible with all metadata versions: -* 1.0 : :pep:`241` -* 1.1 : :pep:`314` -* 1.2 : :pep:`345` +* 1.0: :PEP:`241` +* 1.1: :PEP:`314` +* 1.2: :PEP:`345` -The :pep:`345` implementation supports the micro-language for the environment +The :PEP:`345` implementation supports the micro-language for the environment markers, and displays warnings when versions that are supposed to be -:pep:`386` are violating the scheme. +:PEP:`386` are violating the scheme. Reading metadata @@ -44,7 +44,7 @@ ['bar'] If you want to provide your own execution context, let's say to test the -Metadata under a particular environment that is not the current environment, +metadata under a particular environment that is not the current environment, you can provide your own values in the ``execution_context`` option, which is the dict that may contain one or more keys of the context the micro-language expects. @@ -78,7 +78,7 @@ Conflict checking and best version ================================== -Some fields in :pep:`345` have to follow a version scheme in their versions +Some fields in :PEP:`345` have to follow a version scheme in their versions predicate. When the scheme is violated, a warning is emitted:: >>> from distutils2.metadata import DistributionMetadata @@ -88,5 +88,4 @@ >>> metadata['Requires-Dist'] = ['Funky (1.2)'] - .. TODO talk about check() diff --git a/docs/source/library/distutils2.rst b/docs/source/library/distutils2.rst new file mode 100644 --- /dev/null +++ b/docs/source/library/distutils2.rst @@ -0,0 +1,43 @@ +:mod:`distutils2` --- Packaging support +======================================= + +.. module:: distutils2 + :synopsis: Packaging system and building blocks for other packaging systems +.. sectionauthor:: Distutils2 contributors + + +The :mod:`distutils2` package provides support for building, packaging, +distributing and installing additional projects into a Python installation. +Projects may be include modules, extension modules, packages and scripts. +:mod:`distutils2` also provides building blocks for other packaging systems +that do not use the command system. + +This manual is the reference documentation for those standalone building +blocks and for extending Distutils2. If you're looking for the user-centric +guides to install a project or package your own code, head to `See also`__. + + +.. toctree:: + :maxdepth: 2 + :numbered: + + distutils2.version + distutils2.metadata + distutils2.depgraph + distutils2.index + distutils2.tests.pypi_server + + +.. __: + +.. seealso:: + + :doc:`../distutils/index` + The manual for developers of Python projects who want to package and + distribute them. This describes how to use :mod:`distutils2` to make + projects easily found and added to an existing Python installation. + + :doc:`../install/index` + A user-centered manual which includes information on adding projects + into an existing Python installation. You do not need to be a Python + programmer to read this manual. diff --git a/docs/source/test_framework.rst b/docs/source/library/distutils2.tests.pypi_server.rst rename from docs/source/test_framework.rst rename to docs/source/library/distutils2.tests.pypi_server.rst --- a/docs/source/test_framework.rst +++ b/docs/source/library/distutils2.tests.pypi_server.rst @@ -78,7 +78,7 @@ def test_somthing(self, server): # your tests goes here -The decorator will instanciate the server for you, and run and stop it just +The decorator will instantiate the server for you, and run and stop it just before and after your method call. You also can pass the server initializer, just like this:: diff --git a/docs/source/version.rst b/docs/source/library/distutils2.version.rst rename from docs/source/version.rst rename to docs/source/library/distutils2.version.rst diff --git a/docs/source/pkgutil.rst b/docs/source/library/pkgutil.rst rename from docs/source/pkgutil.rst rename to docs/source/library/pkgutil.rst --- a/docs/source/pkgutil.rst +++ b/docs/source/library/pkgutil.rst @@ -1,38 +1,91 @@ -======= -pkgutil -======= +:mod:`pkgutil` --- Package utilities +==================================== -Introduction -============ +.. module:: pkgutil + :synopsis: Utilities to support packages. -This module provides the necessary functions to provide support for -the "Importer Protocol" as described in :pep:`302` and for working with -the database of installed Python distributions which is specified in -:pep:`376`. In addition to the functions required in :pep:`376`, back support -for older ``.egg`` and ``.egg-info`` distributions is provided as well. These -distributions are represented by the class -:class:`distutils2._backport.pkgutil.EggInfoDistribution` and -most functions provide an extra argument ``use_egg_info`` which indicates if -they should consider these old styled distributions. In this document, -first a complete documentation of the functions and classes -is provided and then several use cases are presented. +.. TODO Follow the reST conventions used in the stdlib + +This module provides functions to manipulate packages, as well as +the necessary functions to provide support for the "Importer Protocol" as +described in :PEP:`302` and for working with the database of installed Python +distributions which is specified in :PEP:`376`. In addition to the functions +required in :PEP:`376`, back support for older ``.egg`` and ``.egg-info`` +distributions is provided as well. These distributions are represented by the +class :class:`~distutils2._backport.pkgutil.EggInfoDistribution` and most +functions provide an extra argument ``use_egg_info`` which indicates if +they should consider these old styled distributions. This document details +first the functions and classes available and then presents several use cases. + + +.. function:: extend_path(path, name) + + Extend the search path for the modules which comprise a package. Intended use is + to place the following code in a package's :file:`__init__.py`:: + + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) + + This will add to the package's ``__path__`` all subdirectories of directories on + ``sys.path`` named after the package. This is useful if one wants to distribute + different parts of a single logical package as multiple directories. + + It also looks for :file:`\*.pkg` files beginning where ``*`` matches the *name* + argument. This feature is similar to :file:`\*.pth` files (see the :mod:`site` + module for more information), except that it doesn't special-case lines starting + with ``import``. A :file:`\*.pkg` file is trusted at face value: apart from + checking for duplicates, all entries found in a :file:`\*.pkg` file are added to + the path, regardless of whether they exist on the filesystem. (This is a + feature.) + + If the input path is not a list (as is the case for frozen packages) it is + returned unchanged. The input path is not modified; an extended copy is + returned. Items are only appended to the copy at the end. + + It is assumed that ``sys.path`` is a sequence. Items of ``sys.path`` that are + not strings referring to existing directories are ignored. Unicode items on + ``sys.path`` that cause errors when used as filenames may cause this function + to raise an exception (in line with :func:`os.path.isdir` behavior). + +.. function:: get_data(package, resource) + + Get a resource from a package. + + This is a wrapper for the :pep:`302` loader :func:`get_data` API. The package + argument should be the name of a package, in standard module format + (foo.bar). The resource argument should be in the form of a relative + filename, using ``/`` as the path separator. The parent directory name + ``..`` is not allowed, and nor is a rooted name (starting with a ``/``). + + The function returns a binary string that is the contents of the + specified resource. + + For packages located in the filesystem, which have already been imported, + this is the rough equivalent of:: + + d = os.path.dirname(sys.modules[package].__file__) + data = open(os.path.join(d, resource), 'rb').read() + + If the package cannot be located or loaded, or it uses a :pep:`302` loader + which does not support :func:`get_data`, then None is returned. + + +API Reference +============= + +.. automodule:: distutils2._backport.pkgutil + :members: Caching +++++++ For performance purposes, the list of distributions is being internally cached. It is enabled by default, but you can turn it off or clear -it using -:func:`distutils2._backport.pkgutil.enable_cache`, -:func:`distutils2._backport.pkgutil.disable_cache` and -:func:`distutils2._backport.pkgutil.clear_cache`. +it using :func:`~distutils2._backport.pkgutil.enable_cache`, +:func:`~distutils2._backport.pkgutil.disable_cache` and +:func:`~distutils2._backport.pkgutil.clear_cache`. -API Reference -============= - -.. automodule:: distutils2._backport.pkgutil - :members: Example Usage ============= diff --git a/src/CONTRIBUTORS.txt b/src/CONTRIBUTORS.txt --- a/src/CONTRIBUTORS.txt +++ b/src/CONTRIBUTORS.txt @@ -9,6 +9,7 @@ Thanks to: +- Ali Afshar - ??ric Araujo - Pior Bastida - Titus Brown diff --git a/src/distutils2/command/bdist_msi.py b/src/distutils2/command/bdist_msi.py --- a/src/distutils2/command/bdist_msi.py +++ b/src/distutils2/command/bdist_msi.py @@ -148,7 +148,7 @@ if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,) else: self.versions = list(self.all_versions) diff --git a/src/distutils2/command/bdist_wininst.py b/src/distutils2/command/bdist_wininst.py --- a/src/distutils2/command/bdist_wininst.py +++ b/src/distutils2/command/bdist_wininst.py @@ -96,7 +96,7 @@ short_version = get_python_version() if self.target_version and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,) self.target_version = short_version diff --git a/src/distutils2/command/build_ext.py b/src/distutils2/command/build_ext.py --- a/src/distutils2/command/build_ext.py +++ b/src/distutils2/command/build_ext.py @@ -101,7 +101,7 @@ ('swig-cpp', None, "make SWIG create C++ files (default is C)"), ('swig-opts=', None, - "list of SWIG command line options"), + "list of SWIG command-line options"), ('swig=', None, "path to the SWIG executable"), ] @@ -384,7 +384,7 @@ self.compiler_obj.initialize(self.plat_name) # And make sure that any compile/link-related options (which might - # come from the command-line or from the setup script) are set in + # come from the command line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: @@ -471,7 +471,7 @@ # guess it's useful) # The environment variable should take precedence, and # any sensible compiler will give precedence to later - # command line args. Hence we combine them in order: + # command-line args. Hence we combine them in order: extra_args = ext.extra_compile_args or [] macros = ext.define_macros[:] diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -97,7 +97,7 @@ # just to be safe. self.force = None - # The 'help' flag is just used for command-line parsing, so + # The 'help' flag is just used for command line parsing, so # none of that complicated bureaucracy is needed. self.help = 0 @@ -128,7 +128,7 @@ # finalize_options() # decide on the final values for all options; this is called # after all possible intervention from the outside world - # (command-line, option file, etc.) has been processed + # (command line, option file, etc.) has been processed # run() # run the command: do whatever it is we're here to do, # controlled by the command's various option values @@ -137,7 +137,7 @@ """Set default values for all the options that this command supports. Note that these defaults may be overridden by other commands, by the setup script, by config files, or by the - command-line. Thus, this is not the place to code dependencies + command line. Thus, this is not the place to code dependencies between options; generally, 'initialize_options()' implementations are just a bunch of "self.foo = None" assignments. @@ -149,7 +149,7 @@ def finalize_options(self): """Set final values for all the options that this command supports. This is always called as late as possible, ie. after any option - assignments from the command-line or from other commands have been + assignments from the command line or from other commands have been done. Thus, this is the place to code option dependencies: if 'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as long as 'foo' still has the same value it was assigned in @@ -179,7 +179,7 @@ """A command's raison d'etre: carry out the action it exists to perform, controlled by the options initialized in 'initialize_options()', customized by other commands, the setup - script, the command-line, and config files, and finalized in + script, the command line and config files, and finalized in 'finalize_options()'. All terminal output and filesystem interaction should be done by 'run()'. diff --git a/src/distutils2/compiler/bcppcompiler.py b/src/distutils2/compiler/bcppcompiler.py --- a/src/distutils2/compiler/bcppcompiler.py +++ b/src/distutils2/compiler/bcppcompiler.py @@ -249,7 +249,7 @@ # list of object files ld_args.extend(objects) - # XXX the command-line syntax for Borland C++ is a bit wonky; + # XXX the command line syntax for Borland C++ is a bit wonky; # certain filenames are jammed together in one big string, but # comma-delimited. This doesn't mesh too well with the # Unix-centric attitude (with a DOS/Windows quoting hack) of diff --git a/src/distutils2/compiler/ccompiler.py b/src/distutils2/compiler/ccompiler.py --- a/src/distutils2/compiler/ccompiler.py +++ b/src/distutils2/compiler/ccompiler.py @@ -191,7 +191,7 @@ linker_exe linker used to create binary executables archiver static library creator - On platforms with a command-line (Unix, DOS/Windows), each of these + On platforms with a command line (Unix, DOS/Windows), each of these is a string that will be split into executable name and (optional) list of arguments. (Splitting the string is done similarly to how Unix shells operate: words are delimited by spaces, but quotes and @@ -597,7 +597,7 @@ output debug symbols in (or alongside) the object file(s). 'extra_preargs' and 'extra_postargs' are implementation- dependent. - On platforms that have the notion of a command-line (e.g. Unix, + On platforms that have the notion of a command line (e.g. Unix, DOS/Windows), they are most likely lists of strings: extra command-line arguments to prepand/append to the compiler command line. On other platforms, consult the implementation class diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -86,7 +86,7 @@ and the next command are used to set attributes of the current command object. - When the entire command-line has been successfully parsed, calls the + When the entire command line has been successfully parsed, calls the 'run()' method on each command object in turn. This method will be driven entirely by the Distribution object (which each command object has a reference to, thanks to its constructor), and the @@ -127,7 +127,7 @@ return dist # Parse the command line and override config files; any - # command-line errors are the end user's fault, so turn them into + # command line errors are the end user's fault, so turn them into # SystemExit to suppress tracebacks. try: ok = dist.parse_command_line() @@ -159,7 +159,7 @@ return the Distribution instance that drives things. This is useful if you need to find out the distribution metadata (passed as keyword args from 'script' to 'setup()', or the contents of the - config files or command-line. + config files or command line. 'script_name' is a file that will be run with 'execfile()'; 'sys.argv[0]' will be replaced with 'script' for the duration of the @@ -176,8 +176,8 @@ stop after config files have been parsed (and their data stored in the Distribution instance) commandline - stop after the command-line ('sys.argv[1:]' or 'script_args') - have been parsed (and the data stored in the Distribution) + stop after the command line ('sys.argv[1:]' or 'script_args') + has been parsed (and the data stored in the Distribution) run [default] stop after all commands have been run (the same as if 'setup()' had been called in the usual way diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -164,12 +164,12 @@ # 'script_name' and 'script_args' are usually set to sys.argv[0] # and sys.argv[1:], but they can be overridden when the caller is - # not necessarily a setup script run from the command-line. + # not necessarily a setup script run from the command line. self.script_name = None self.script_args = None # 'command_options' is where we store command options between - # parsing them (from config files, the command-line, etc.) and when + # parsing them (from config files, the command line, etc.) and when # they are actually needed -- ie. when the command in question is # instantiated. It is a dictionary of dictionaries of 2-tuples: # command_options = { command_name : { option : (source, value) } } @@ -424,9 +424,9 @@ command class -- thus, we have to be able to load command classes in order to parse the command line. Any error in that 'options' attribute raises DistutilsGetoptError; any error on the - command-line raises DistutilsArgError. If no Distutils commands + command line raises DistutilsArgError. If no Distutils commands were found on the command line, raises DistutilsArgError. Return - true if command-line was successfully parsed and we should carry + true if command line was successfully parsed and we should carry on with executing commands; false if no errors but we shouldn't execute commands (currently, this only happens if user asks for help). @@ -573,7 +573,7 @@ if help_option_found: return - # Put the options from the command-line into their official + # Put the options from the command line into their official # holding pen, the 'command_options' dictionary. opt_dict = self.get_option_dict(command) for (name, value) in vars(opts).items(): @@ -594,7 +594,7 @@ def _show_help(self, parser, global_options=1, display_options=1, commands=[]): - """Show help for the setup script command-line in the form of + """Show help for the setup script command line in the form of several lists of command-line options. 'parser' should be a FancyGetopt instance; do not expect it to be returned in the same state, as its option table will be reset to make it diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py --- a/src/distutils2/fancy_getopt.py +++ b/src/distutils2/fancy_getopt.py @@ -71,7 +71,7 @@ # These keep track of the information in the option table. We # don't actually populate these structures until we're ready to - # parse the command-line, since the 'option_table' passed in here + # parse the command line, since the 'option_table' passed in here # isn't necessarily the final word. self.short_opts = [] self.long_opts = [] @@ -80,7 +80,7 @@ self.takes_arg = {} # And 'option_order' is filled up in 'getopt()'; it records the - # original order of options (and their values) on the command-line, + # original order of options (and their values) on the command line, # but expands short options, converts aliases, etc. self.option_order = [] diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -245,7 +245,7 @@ self.assertRaises(ValueError, dist.announce, args, kwargs) def test_find_config_files_disable(self): - # Ticket #1180: Allow user to disable their home config file. + # Bug #1180: Allow users to disable their own config file. temp_home = self.mkdtemp() if os.name == 'posix': user_filename = os.path.join(temp_home, ".pydistutils.cfg") diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -352,6 +352,21 @@ self.assertRaises(ImportError, resolve_dotted_name, "distutils2.tests.test_util.UtilTestCase.nonexistent_attribute") + def test_import_nested_first_time(self): + tmp_dir = self.mkdtemp() + os.makedirs(os.path.join(tmp_dir, 'a', 'b')) + self.write_file(os.path.join(tmp_dir, 'a', '__init__.py'), '') + self.write_file(os.path.join(tmp_dir, 'a', 'b', '__init__.py'), '') + self.write_file(os.path.join(tmp_dir, 'a', 'b', 'c.py'), 'class Foo: pass') + + try: + sys.path.append(tmp_dir) + resolve_dotted_name("a.b.c.Foo") + # assert nothing raised + finally: + sys.path.remove(tmp_dir) + + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_run_2to3_on_code(self): content = "print 'test'" diff --git a/src/distutils2/util.py b/src/distutils2/util.py --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -636,25 +636,29 @@ packages.append(package_name) return packages +def resolve_dotted_name(name): + """Resolves the name and returns the corresponding object.""" + parts = name.split('.') + cursor = len(parts) + module_name, rest = parts[:cursor], parts[cursor:] -def resolve_dotted_name(dotted_name): - module_name, rest = dotted_name.split('.')[0], dotted_name.split('.')[1:] - while len(rest) > 0: + while cursor > 0: try: - ret = __import__(module_name) + ret = __import__('.'.join(module_name)) break except ImportError: - if rest == []: + if cursor == 0: raise - module_name += ('.' + rest[0]) - rest = rest[1:] - while len(rest) > 0: + cursor -= 1 + module_name = parts[:cursor] + rest = parts[cursor:] + + for part in parts[1:]: try: - ret = getattr(ret, rest.pop(0)) + ret = getattr(ret, part) except AttributeError: raise ImportError return ret - # utility functions for 2to3 support def run_2to3(files, doctests_only=False, fixer_names=None, options=None, diff --git a/src/setup.py b/src/setup.py --- a/src/setup.py +++ b/src/setup.py @@ -104,7 +104,7 @@ add_dir_to_list(compiler.library_dirs, '/usr/lib') add_dir_to_list(compiler.include_dirs, '/usr/include') - # look in command line supplied paths + # look in paths supplied on the command line if SSL_LIBDIR: add_dir_to_list(compiler.library_dirs, SSL_LIBDIR) if SSL_INCDIR: diff --git a/src/tests.sh b/src/tests.sh --- a/src/tests.sh +++ b/src/tests.sh @@ -37,4 +37,4 @@ else echo Success fi -echo Good job, commit now! +echo "Good job, commit now! (or add tests)" -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: added a fall-through to a default unittest2 test discovery Message-ID: tarek.ziade pushed d92dcd5dbe63 to distutils2: http://hg.python.org/distutils2/rev/d92dcd5dbe63 changeset: 623:d92dcd5dbe63 user: Konrad Delong date: Sat Aug 14 22:31:26 2010 +0200 summary: added a fall-through to a default unittest2 test discovery files: src/distutils2/command/test.py, src/distutils2/tests/test_test.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -1,4 +1,5 @@ -import os, sys +import os +import sys from distutils2 import log from distutils2.core import Command from distutils2._backport.pkgutil import get_distribution @@ -28,8 +29,18 @@ if get_distribution(requirement) is None: warnings.warn("The test dependency %s is not installed which may couse the tests to fail." % requirement, RuntimeWarning) - if not self.suite and not self.runner: - self.announce("Either 'suite' or 'runner' option must be specified", log.ERROR) + if not self.suite and not self.runner and self.get_ut_with_discovery() is None: + self.announce("No test discovery available. Please specify the 'suite' or 'runner' option or install unittest2.", log.ERROR) + + def get_ut_with_discovery(self): + if hasattr(unittest.TestLoader, "discover"): + return unittest + else: + try: + import unittest2 + return unittest2 + except ImportError: + return None def run(self): prev_cwd = os.getcwd() @@ -45,6 +56,10 @@ if self.runner: resolve_name(self.runner)() elif self.suite: - unittest.main(None, None, [unittest.__file__, '--verbose', self.suite]) + unittest.TextTestRunner(verbosity=self.verbose + 1).run(resolve_name(self.suite)()) + elif self.get_ut_with_discovery(): + discovery_ut = self.get_ut_with_discovery() + test_suite = discovery_ut.TestLoader().discover(os.curdir) + discovery_ut.TextTestRunner(verbosity=self.verbose + 1).run(test_suite) finally: os.chdir(prev_cwd) diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -26,6 +26,40 @@ here = os.path.dirname(os.path.abspath(__file__)) +def with_mock_ut2_module(func): + def wrapper(*args): + class MockUTModule(object): pass + class MockUTClass(object): + def __init__(*_, **__): pass + def discover(self): pass + def run(self, _): pass + ut2 = MockUTModule() + ut2.TestLoader = MockUTClass + ut2.TextTestRunner = MockUTClass + orig_ut2 = sys.modules.get('unittest2', None) + try: + sys.modules['unittest2'] = ut2 + args += (ut2,) + func(*args) + finally: + if orig_ut2 is not None: + sys.modules['unittest2'] = orig_ut2 + else: + del sys.modules['unittest2'] + return wrapper + +def with_ut_isolated(func): + def wrapper(*args): + import unittest as ut1 + + orig_discover = getattr(ut1.TestLoader, 'discover', None) + try: + args += (ut1,) + return func(*args) + finally: + if orig_discover is not None: + ut1.TestLoader.discover = orig_discover + return wrapper class TestTest(TempdirManager, #LoggingSilencer, @@ -117,6 +151,38 @@ finally: sys.path.remove(tmp_dir) + def _test_gets_unittest_discovery(self): + import unittest as ut1 + + orig_discover = getattr(ut1.TestLoader, 'discover', None) + try: + self._test_gets_unittest_discovery(ut1) + finally: + if orig_discover is not None: + ut1.TestLoader.discover = orig_discover + + @with_ut_isolated + @with_mock_ut2_module + def test_gets_unittest_discovery(self, ut1, mock_ut2): + dist = Distribution() + cmd = test(dist) + ut1.TestLoader.discover = lambda: None + self.assertEqual(cmd.get_ut_with_discovery(), ut1) + + del ut1.TestLoader.discover + self.assertEqual(cmd.get_ut_with_discovery(), mock_ut2) + + @with_ut_isolated + @with_mock_ut2_module + def test_calls_discover(self, ut1, mock_ut2): + if hasattr(ut1.TestLoader, "discover"): + del ut1.TestLoader.discover + record = [] + mock_ut2.TestLoader.discover = lambda self, path: record.append(path) + dist = Distribution() + cmd = test(dist) + cmd.run() + self.assertEqual(record, [os.curdir]) def test_suite(): return unittest.makeSuite(TestTest) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: oh noez! whitespace! Message-ID: tarek.ziade pushed 4f9ba87eaf15 to distutils2: http://hg.python.org/distutils2/rev/4f9ba87eaf15 changeset: 622:4f9ba87eaf15 user: Konrad Delong date: Fri Aug 13 18:40:49 2010 +0200 summary: oh noez! whitespace! files: docs/source/distutils/newcommands.rst, src/distutils2/command/test.py, src/distutils2/tests/test_test.py diff --git a/docs/source/distutils/newcommands.rst b/docs/source/distutils/newcommands.rst --- a/docs/source/distutils/newcommands.rst +++ b/docs/source/distutils/newcommands.rst @@ -14,7 +14,7 @@ to be able to run a project's unit tests without actually deploying the project anywhere. The ``test`` command runs project's unit tests without actually deploying it, by temporarily putting the -project's source on ``sys.path``, after first running ``build_ext -i`` +project's source on ``sys.path``, after first running ``build_ext -i`` to ensure that any C extensions are built. You can use this command in one of two ways: either by specifying a @@ -26,13 +26,13 @@ ``--suite=NAME, -s NAME`` Specify the test suite (or module, class, or method) to be run (for example ``some_module.test_suite``). The default for this option can be - set by in your ``setup.cfg`` file. + set by in your ``setup.cfg`` file. [test] suite = my_package.tests.test_all ``--runner=NAME, -r NAME`` - Specify the test runner to be called. + Specify the test runner to be called. ``upload`` - Upload source and/or binary distributions to PyPI diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -1,6 +1,6 @@ import os, sys from distutils2 import log -from distutils2.core import Command +from distutils2.core import Command from distutils2._backport.pkgutil import get_distribution from distutils2.util import resolve_name import unittest @@ -16,11 +16,11 @@ ('runner=', None, "Test runner to be called."), ] - + def initialize_options(self): self.suite = None self.runner = None - + def finalize_options(self): self.build_lib = self.get_finalized_command("build").build_lib if self.distribution.tests_require: diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -68,7 +68,7 @@ temp_pkg_dir = join(self.mkdtemp(), dist_name) shutil.copytree(pkg_dir, temp_pkg_dir) return temp_pkg_dir - + def test_runs_simple_tests(self): self.pkg_dir = self.prepare_dist('simple_test') output = self.run_with_dist_cwd(self.pkg_dir) @@ -116,7 +116,7 @@ self.assertEqual(["runner called"], record) finally: sys.path.remove(tmp_dir) - + def test_suite(): return unittest.makeSuite(TestTest) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: merged upstream Message-ID: tarek.ziade pushed cc240818f9f9 to distutils2: http://hg.python.org/distutils2/rev/cc240818f9f9 changeset: 621:cc240818f9f9 parent: 620:b81715424b1c parent: 556:272a3087dd6e user: Konrad Delong date: Fri Aug 13 18:19:28 2010 +0200 summary: merged upstream files: src/distutils2/install_with_deps.py, src/distutils2/tests/test_install_with_deps.py diff --git a/docs/source/library/distutils2.version.rst b/docs/source/library/distutils2.version.rst --- a/docs/source/library/distutils2.version.rst +++ b/docs/source/library/distutils2.version.rst @@ -5,12 +5,12 @@ Distutils2 ships with a python package capable to work with version numbers. It's an implementation of version specifiers `as defined in PEP 345 `_ about -Metadata. +Metadatas. -`distutils2.version.NormalizedVersion` -====================================== +NormalizedVersion +================== -A Normalized version corresponds to a specific version of a distribution, as +A Normalized version is a specific version of a distribution, as described in the PEP 345. So, you can work with the `NormalizedVersion` like this:: @@ -31,11 +31,12 @@ NormalizedVersion is used internally by `VersionPredicate` to do his stuff. -`distutils2.version.suggest_normalized_version` ------------------------------------------------ +Suggest a normalized version +---------------------------- -You also can let the normalized version be suggested to you, using the -`suggest_normalized_version` function:: +Before this version scheme, there was existing others ones. Distutils2 provides +a tool that suggests a normalized version: the `suggest_normalized_version` +function:: >>> suggest_normalized_version('2.1-rc1') 2.1c1 @@ -46,8 +47,8 @@ >>> print suggest_normalized_version('not a version') None -`distutils2.version.VersionPredicate` -===================================== +Dealing with version predicates +=============================== `VersionPredicate` knows how to parse stuff like "ProjectName (>=version)", the class also provides a `match` method to test if a version number is the version @@ -59,6 +60,11 @@ >>> version.match("1.1.1") True -`is_valid_predicate` --------------------- +You could test if a predicate is a valid one, usng the `is_valid_predicate` +function:: + >>> is_valid_predicate('FooBar (1.1)') + True + >>> is_valid_predicate('FooBar (+1.1)') + False + diff --git a/src/distutils2/errors.py b/src/distutils2/errors.py --- a/src/distutils2/errors.py +++ b/src/distutils2/errors.py @@ -80,6 +80,10 @@ """Byte compile error.""" +class DistutilsIndexError(DistutilsError): + """Any problem occuring during using the indexes.""" + + # Exception classes used by the CCompiler implementation classes class CCompilerError(Exception): """Some compile/link operation failed.""" diff --git a/src/distutils2/index/errors.py b/src/distutils2/index/errors.py --- a/src/distutils2/index/errors.py +++ b/src/distutils2/index/errors.py @@ -2,30 +2,26 @@ All errors and exceptions raised by PyPiIndex classes. """ -from distutils2.errors import DistutilsError +from distutils2.errors import DistutilsIndexError -class IndexesError(DistutilsError): - """The base class for errors of the index python package.""" - - -class ProjectNotFound(IndexesError): +class ProjectNotFound(DistutilsIndexError): """Project has not been found""" -class DistributionNotFound(IndexesError): +class DistributionNotFound(DistutilsIndexError): """The release has not been found""" -class ReleaseNotFound(IndexesError): +class ReleaseNotFound(DistutilsIndexError): """The release has not been found""" -class CantParseArchiveName(IndexesError): +class CantParseArchiveName(DistutilsIndexError): """An archive name can't be parsed to find distribution name and version""" -class DownloadError(IndexesError): +class DownloadError(DistutilsIndexError): """An error has occurs while downloading""" @@ -33,13 +29,13 @@ """Compared hashes does not match""" -class UnsupportedHashName(IndexesError): +class UnsupportedHashName(DistutilsIndexError): """A unsupported hashname has been used""" -class UnableToDownload(IndexesError): +class UnableToDownload(DistutilsIndexError): """All mirrors have been tried, without success""" -class InvalidSearchField(IndexesError): +class InvalidSearchField(DistutilsIndexError): """An invalid search field has been used""" diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -17,7 +17,7 @@ from distutils2.index.base import BaseClient from distutils2.index.dist import (ReleasesList, EXTENSIONS, get_infos_from_url, MD5_HASH) -from distutils2.index.errors import (IndexesError, DownloadError, +from distutils2.index.errors import (DistutilsIndexError, DownloadError, UnableToDownload, CantParseArchiveName, ReleaseNotFound, ProjectNotFound) from distutils2.index.mirrors import get_mirrors @@ -395,7 +395,7 @@ fp = urllib2.urlopen(request) except (ValueError, httplib.InvalidURL), v: msg = ' '.join([str(arg) for arg in v.args]) - raise IndexesError('%s %s' % (url, msg)) + raise DistutilsIndexError('%s %s' % (url, msg)) except urllib2.HTTPError, v: return v except urllib2.URLError, v: diff --git a/src/distutils2/install_with_deps.py b/src/distutils2/install_tools.py rename from src/distutils2/install_with_deps.py rename to src/distutils2/install_tools.py --- a/src/distutils2/install_with_deps.py +++ b/src/distutils2/install_tools.py @@ -15,155 +15,79 @@ """ -def get_deps(requirements, index): - """Return the dependencies of a project, as a depgraph object. - - Build a :class depgraph.DependencyGraph: for the given requirements - - If the project does not uses Metadata < 1.1, dependencies can't be handled - from here, so it returns an empty list of dependencies. - - :param requirements: is a string containing the version predicate to take - the project name and version specifier from. - :param index: the index to use for making searches. - """ - deps = [] - release = index.get_release(requirements) - requires = release.metadata['Requires-Dist'] + release.metadata['Requires'] - deps.append(release) # include the release we are computing deps. - for req in requires: - deps.extend(get_deps(req, index)) - return deps - - -def install(requirements, index=None, interactive=True, upgrade=True, - prefer_source=True, prefer_final=True): - """Given a list of distributions to install, a list of distributions to - remove, and a list of conflicts, proceed and do what's needed to be done. - - :param requirements: is a *string* containing the requirements for this - project (for instance "FooBar 1.1" or "BarBaz (<1.2) - :param index: If an index is specified, use this one, otherwise, use - :class index.ClientWrapper: to get project metadatas. - :param interactive: if set to True, will prompt the user for interactions - of needed. If false, use the default values. - :param upgrade: If a project exists in a newer version, does the script - need to install the new one, or keep the already installed - version. - :param prefer_source: used to tell if the user prefer source distributions - over built dists. - :param prefer_final: if set to true, pick up the "final" versions (eg. - stable) over the beta, alpha (not final) ones. - """ - # get the default index if none is specified - if not index: - index = wrapper.WrapperClient() - - # check if the project is already installed. - installed_release = get_installed_release(requirements) - - # if a version that satisfy the requirements is already installed - if installed_release and (interactive or upgrade): - new_releases = index.get_releases(requirements) - if (new_releases.get_last(requirements).version > - installed_release.version): - if interactive: - # prompt the user to install the last version of the package. - # set upgrade here. - print "You want to install a package already installed on your" - "system. A new version exists, you could just use the version" - "you have, or upgrade to the latest version" - - upgrade = raw_input("Do you want to install the most recent one ? (Y/n)") or "Y" - if upgrade in ('Y', 'y'): - upgrade = True - else: - upgrade = False - if not upgrade: - return - - # create the depgraph from the dependencies of the release we want to - # install - graph = generate_graph(get_deps(requirements, index)) - from ipdb import set_trace - set_trace() - installed = [] # to uninstall on errors - try: - for release in graph.adjacency_list: - dist = release.get_distribution() - install(dist) - installed.append(dist) - print "%s have been installed on your system" % requirements - except: - print "an error has occured, uninstalling" - for dist in installed: - uninstall_dist(dist) - class InstallationException(Exception): pass -def get_install_info(requirements, index=None, already_installed=None): + +def _update_infos(infos, new_infos): + """extends the lists contained in the `info` dict with those contained + in the `new_info` one + """ + for key, value in infos.items(): + if key in new_infos: + infos[key].extend(new_infos[key]) + + +def get_infos(requirements, index=None, installed=None, + prefer_final=True): """Return the informations on what's going to be installed and upgraded. :param requirements: is a *string* containing the requirements for this project (for instance "FooBar 1.1" or "BarBaz (<1.2)") :param index: If an index is specified, use this one, otherwise, use :class index.ClientWrapper: to get project metadatas. - :param already_installed: a list of already installed distributions. + :param installed: a list of already installed distributions. + :param prefer_final: when picking up the releases, prefer a "final" one + over a beta/alpha/etc one. - The results are returned in a dict. For instance:: + The results are returned in a dict, containing all the operations + needed to install the given requirements:: >>> get_install_info("FooBar (<=1.2)") {'install': [], 'remove': [], 'conflict': []} Conflict contains all the conflicting distributions, if there is a conflict. - """ - def update_infos(new_infos, infos): - for key, value in infos.items(): - if key in new_infos: - infos[key].extend(new_infos[key]) - return new_infos if not index: index = wrapper.ClientWrapper() - logging.info("searching releases for %s" % requirements) - # 1. get all the releases that match the requirements + if not installed: + installed = get_distributions() + + # Get all the releases that match the requirements try: releases = index.get_releases(requirements) except (ReleaseNotFound, ProjectNotFound), e: - raise InstallationException('Release not found: "%s"' % requirements) + raise InstallationException('Release not found: "%s"' % requirements) - # 2. pick up a release, and try to get the dependency tree - release = releases.get_last(requirements) + # Pick up a release, and try to get the dependency tree + release = releases.get_last(requirements, prefer_final=prefer_final) + + # Iter since we found something without conflicts metadata = release.fetch_metadata() - # 3. get the distributions already_installed on the system - # 4. and add the one we want to install - if not already_installed: - already_installed = get_distributions() + # Get the distributions already_installed on the system + # and add the one we want to install - logging.info("fetching %s %s dependencies" % ( - release.name, release.version)) - distributions = already_installed + [release] + distributions = installed + [release] depgraph = generate_graph(distributions) - # store all the already_installed packages in a list, in case of rollback. - infos = {'install':[], 'remove': [], 'conflict': []} + # Store all the already_installed packages in a list, in case of rollback. + infos = {'install': [], 'remove': [], 'conflict': []} - # 5. get what the missing deps are + # Get what the missing deps are for dists in depgraph.missing.values(): if dists: logging.info("missing dependencies found, installing them") # we have missing deps for dist in dists: - update_infos(get_install_info(dist, index, already_installed), - infos) + _update_infos(infos, + get_infos(dist, index, installed)) - # 6. fill in the infos - existing = [d for d in already_installed if d.name == release.name] + # Fill in the infos + existing = [d for d in installed if d.name == release.name] if existing: infos['remove'].append(existing[0]) infos['conflict'].extend(depgraph.reverse_list[existing[0]]) diff --git a/src/distutils2/tests/test_install_with_deps.py b/src/distutils2/tests/test_install_tools.py rename from src/distutils2/tests/test_install_with_deps.py rename to src/distutils2/tests/test_install_tools.py --- a/src/distutils2/tests/test_install_with_deps.py +++ b/src/distutils2/tests/test_install_tools.py @@ -4,10 +4,11 @@ from distutils2.tests import run_unittest from distutils2.tests.support import unittest from distutils2.index.xmlrpc import Client -from distutils2.install_with_deps import (get_install_info, +from distutils2.install_tools import (get_infos, InstallationException) from distutils2.metadata import DistributionMetadata + class FakeDist(object): """A fake distribution object, for tests""" def __init__(self, name, version, deps): @@ -20,16 +21,25 @@ def __repr__(self): return '' % self.name + def get_fake_dists(dists): objects = [] for (name, version, deps) in dists: - objects.append(FakeDist(name, version, deps)) + objects.append(FakeDist(name, version, deps)) return objects + class TestInstallWithDeps(unittest.TestCase): def _get_client(self, server, *args, **kwargs): return Client(server.full_address, *args, **kwargs) + def _get_results(self, output): + """return a list of results""" + installed = [(o.name, '%s' % o.version) for o in output['install']] + remove = [(o.name, '%s' % o.version) for o in output['remove']] + conflict = [(o.name, '%s' % o.version) for o in output['conflict']] + return (installed, remove, conflict) + @use_xmlrpc_server() def test_existing_deps(self, server): # Test that the installer get the dependencies from the metadatas @@ -55,17 +65,17 @@ 'url': archive_path}, ]) installed = get_fake_dists([('bacon', '0.1', []),]) - output = get_install_info("choxie", index=client, - already_installed=installed) + output = get_infos("choxie", index=client, + installed=installed) # we dont have installed bacon as it's already installed on the system. self.assertEqual(0, len(output['remove'])) self.assertEqual(2, len(output['install'])) - readable_output = [(o.name, '%s' % o.version) + readable_output = [(o.name, '%s' % o.version) for o in output['install']] self.assertIn(('towel-stuff', '0.1'), readable_output) self.assertIn(('choxie', '2.0.0.9'), readable_output) - + @use_xmlrpc_server() def test_upgrade_existing_deps(self, server): # Tests that the existing distributions can be upgraded if needed. @@ -85,11 +95,11 @@ 'requires_dist': [], 'url': archive_path}, ]) - - output = get_install_info("choxie", index=client, already_installed= - get_fake_dists([('bacon', '0.1', []),])) + + output = get_infos("choxie", index=client, installed= + get_fake_dists([('bacon', '0.1', []),])) installed = [(o.name, '%s' % o.version) for o in output['install']] - + # we need bacon 0.2, but 0.1 is installed. # So we expect to remove 0.1 and to install 0.2 instead. remove = [(o.name, '%s' % o.version) for o in output['remove']] @@ -101,7 +111,7 @@ @use_xmlrpc_server() def test_conflicts(self, server): - # Tests that conflicts are detected + # Tests that conflicts are detected client = self._get_client(server) archive_path = '%s/distribution.tar.gz' % server.full_address server.xmlrpc.set_distributions([ @@ -118,16 +128,14 @@ 'requires_dist': [], 'url': archive_path}, ]) - already_installed = [('bacon', '0.1', []), - ('chicken', '1.1', ['bacon (0.1)'])] - output = get_install_info("choxie", index=client, already_installed= - get_fake_dists(already_installed)) - + already_installed = [('bacon', '0.1', []), + ('chicken', '1.1', ['bacon (0.1)'])] + output = get_infos("choxie", index=client, installed= + get_fake_dists(already_installed)) + # we need bacon 0.2, but 0.1 is installed. # So we expect to remove 0.1 and to install 0.2 instead. - installed = [(o.name, '%s' % o.version) for o in output['install']] - remove = [(o.name, '%s' % o.version) for o in output['remove']] - conflict = [(o.name, '%s' % o.version) for o in output['conflict']] + installed, remove, conflict = self._get_results(output) self.assertIn(('choxie', '2.0.0.9'), installed) self.assertIn(('towel-stuff', '0.1'), installed) self.assertIn(('bacon', '0.2'), installed) @@ -139,7 +147,7 @@ # Test that the isntalled raises an exception if the project does not # exists. client = self._get_client(server) - self.assertRaises(InstallationException, get_install_info, + self.assertRaises(InstallationException, get_infos, 'unexistant project', index=client) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: removed leftover code Message-ID: tarek.ziade pushed 37b2cdd1ac73 to distutils2: http://hg.python.org/distutils2/rev/37b2cdd1ac73 changeset: 624:37b2cdd1ac73 user: Konrad Delong date: Sat Aug 14 22:33:51 2010 +0200 summary: removed leftover code files: src/distutils2/tests/test_test.py diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -151,16 +151,6 @@ finally: sys.path.remove(tmp_dir) - def _test_gets_unittest_discovery(self): - import unittest as ut1 - - orig_discover = getattr(ut1.TestLoader, 'discover', None) - try: - self._test_gets_unittest_discovery(ut1) - finally: - if orig_discover is not None: - ut1.TestLoader.discover = orig_discover - @with_ut_isolated @with_mock_ut2_module def test_gets_unittest_discovery(self, ut1, mock_ut2): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: test discovery docs Message-ID: tarek.ziade pushed b04d295101e2 to distutils2: http://hg.python.org/distutils2/rev/b04d295101e2 changeset: 625:b04d295101e2 user: Konrad Delong date: Sat Aug 14 22:36:59 2010 +0200 summary: test discovery docs files: docs/source/distutils/newcommands.rst diff --git a/docs/source/distutils/newcommands.rst b/docs/source/distutils/newcommands.rst --- a/docs/source/distutils/newcommands.rst +++ b/docs/source/distutils/newcommands.rst @@ -23,6 +23,10 @@ results in the console. Both options: ``suite`` and ``runner`` accept dotted names that will be resolved into actual objects. +If none of these options are specified, distutils2 will try to perform test +discovery using either unittest2 (if installed) or unittest (if running on +recent Python version). + ``--suite=NAME, -s NAME`` Specify the test suite (or module, class, or method) to be run (for example ``some_module.test_suite``). The default for this option can be -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:22 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:22 +0200 Subject: [Python-checkins] distutils2: tests-require moved to be an option of test command Message-ID: tarek.ziade pushed 75019e7abbc9 to distutils2: http://hg.python.org/distutils2/rev/75019e7abbc9 changeset: 626:75019e7abbc9 user: Konrad Delong date: Sat Aug 14 23:28:01 2010 +0200 summary: tests-require moved to be an option of test command files: src/distutils2/command/test.py, src/distutils2/dist.py, src/distutils2/tests/test_test.py diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py --- a/src/distutils2/command/test.py +++ b/src/distutils2/command/test.py @@ -16,19 +16,21 @@ "Test suite to run (e.g. 'some_module.test_suite')"), ('runner=', None, "Test runner to be called."), + ('tests-require=', None, + "List of packages required to run the test suite."), ] def initialize_options(self): self.suite = None self.runner = None + self.tests_require = [] def finalize_options(self): self.build_lib = self.get_finalized_command("build").build_lib - if self.distribution.tests_require: - for requirement in self.distribution.tests_require: - if get_distribution(requirement) is None: - warnings.warn("The test dependency %s is not installed which may couse the tests to fail." % requirement, - RuntimeWarning) + for requirement in self.tests_require: + if get_distribution(requirement) is None: + warnings.warn("The test dependency %s is not installed which may couse the tests to fail." % requirement, + RuntimeWarning) if not self.suite and not self.runner and self.get_ut_with_discovery() is None: self.announce("No test discovery available. Please specify the 'suite' or 'runner' option or install unittest2.", log.ERROR) diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -108,8 +108,6 @@ "print the list of packages/modules provided"), ('requires', None, "print the list of packages/modules required"), - ('tests-require', None, - "print the list of packages/modules required to run the test suite"), ('obsoletes', None, "print the list of packages/modules made obsolete"), ('use-2to3', None, diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py --- a/src/distutils2/tests/test_test.py +++ b/src/distutils2/tests/test_test.py @@ -62,7 +62,6 @@ return wrapper class TestTest(TempdirManager, - #LoggingSilencer, unittest.TestCase): def setUp(self): @@ -123,9 +122,9 @@ def test_checks_requires(self): from distutils2.tests.with_support import examine_warnings dist = Distribution() - dist.tests_require = ['ohno_ohno-impossible_1234-name_stop-that!'] cmd = test(dist) def examinator(ws): + cmd.tests_require = ['ohno_ohno-impossible_1234-name_stop-that!'] cmd.ensure_finalized() self.assertEqual(1, len(ws)) warning = ws[0] -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Sep 19 10:20:21 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 19 Sep 2010 10:20:21 +0200 Subject: [Python-checkins] distutils2: merged upstream Message-ID: tarek.ziade pushed 4e01a65bfa99 to distutils2: http://hg.python.org/distutils2/rev/4e01a65bfa99 changeset: 606:4e01a65bfa99 parent: 605:cbcd1ec808dc parent: 386:04327e2e55e5 user: Konrad Delong date: Tue Jul 27 15:07:46 2010 +0200 summary: merged upstream files: src/distutils2/command/upload_docs.py, src/distutils2/dist.py, src/distutils2/pypi/pkg_resources.py, src/distutils2/spawn.py, src/distutils2/tests/pypiserver/test_link_priority/external/external.html, src/distutils2/tests/pypiserver/test_link_priority/simple/foobar/index.html, src/distutils2/tests/pypiserver/test_link_priority/simple/index.html, src/distutils2/tests/test_spawn.py diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -1,5 +1,9 @@ -.*\.pyc$ -.*\.pyo$ -^src/build -^docs/build -.*\.swp$ +syntax: glob +*.py[co] +__pycache__/ +configure.cache +build/ +MANIFEST +dist/ +*.swp +.coverage \ No newline at end of file diff --git a/docs/design/pep-0376.txt b/docs/design/pep-0376.txt --- a/docs/design/pep-0376.txt +++ b/docs/design/pep-0376.txt @@ -402,7 +402,7 @@ ``.egg-info`` directory that contains a PKG-INFO that matches `name` for the `name` metadata. - Notice that there should be at most one result. The first result founded + Notice that there should be at most one result. The first result found is returned. If the directory is not found, returns None. - ``get_file_users(path)`` -> iterator of ``Distribution`` instances. diff --git a/docs/design/wiki.rst b/docs/design/wiki.rst --- a/docs/design/wiki.rst +++ b/docs/design/wiki.rst @@ -399,7 +399,7 @@ the folder hierarchy from the module until we find a setup.cfg? A setup.cfg is necessary if you use distutils2, is it not? - -> information founded in setup.cfg will be put in the *FILES* file upon + -> information found in setup.cfg will be put in the *FILES* file upon installation in the egg-info directory. IOW in the unbuit-egg case, we would need to create that dir, then use pkgutil APIs. diff --git a/docs/source/_static/depgraph_big.png b/docs/source/_static/depgraph_big.png new file mode 100644 index 0000000000000000000000000000000000000000..8b8a27ccc4f695a149fc67f721f40e2798f8a8eb GIT binary patch literal 181858 zc$|EEcRbep_x~lLvPX7=hE+yF*(#M}W$#f|X7)~2l9fb}Eqi5V&y=#0dD*fuBQk&I zt?oy4d0+4M>vhg^oO51+6ysCHr$=Hir!lv$OR2cT{lvO at +xhkNp4p>U|d2O?;a5|M#+x zrR_fV|9wXmZa{I+)BoRVzV6N({_u&`*4D(ng`~a(L%~=jEv*xQjX0Qsp+#O=qVCd~ zni|FI-tNW3#M4__Tc?GEX|-~x;fpgWt3Cnu at Me{+6>4zv##W3 zl9iQJgqum}db+mu2=lnt!vFLA7o|~QM^79(_PDlILRwlnvDYHa%j&-ssmf8~Lf at sV zd?fNS-dp*=)>c_eAO8cvSYpiB;$lc$oh0T!=$%ro8rzvOcq}X|D;pcCKd+FHEA1KH zs-$-sp({E6j%A9=@W2eSfw<{-)$6BRDNS)27dnj zEa%SQD=8_-N9SslNLM;7#x;ywKYS5?HYz+VBt-M!!v}V5?(4U2AAc4c{Pxw07Zg+{ zPfDft)SC%3Sd>Vo_i%LU<*E_t>+9p;;YnuqvT}11B_$;tJAPcs%q*jaf{7_~cGgV5 zW+V>B=D&?i9+Fd1B5!{y68QW%5!@l$`SS$P7j>nrtuLB6;i9 zv4)0*&K%84#g+pUe0+RQgMy at VbQlMV&3#EuUA4vV{9SYV at s$A{O!@V3F1X$VF z$m!{y!CIy_kB&0?`1q{+{*6DT(?*Q>YI2F2I at qByH5JdPQa7vV!jnBSPesAQ$@zkbfs)eq_u|l{6=zehz!H<}3?!N}gZV_2eP$V+R$|2kEZ`^E+Z`DtewP982U`#~GDVIK_S661B>~AS3+?0~S!PK?15Oj5Q znRO)})hae)IjPZUQKG!TrbIk6I;ua<^-5h1fWzTUmCCw)Fy}BlMCY6XJ13{Ko*vUh z{hH^nX&huEnE3ek?$cQLw^x(yL|>fk&8E5PFpD824a4{9{Wt61oC|J#V%!qSa`x=e zxw$!E_uo19^uzP>^QmI2;lt71($R5j;0B#NeHtSrCAG4?jyERyQR%r7UfxMkNNQgC z5M_yQjXb^TXDC!2JYa!H9~c-Y(8|R;e){y7!_qL_>gwt(Il1d$Ok$nY?#}WPH?Mzt zidAE=%I4Xo_y7li6sE*kGv@ zVXj)XL(f_2;G-xzZO4y5;Gg$z?MRRa`}$Q5BdMZ7RpGc0V2!OvvKg(J?aOCwXl#tK z5GE%%V6AV$on3WadLDX8B-wtZ9X;K|8rMa=s;8Gs z+sD4Xz9j58Pt_hT7JM&V9J@^u;Lr4sK1{!>l7Uw9e7H_ zX$)3T*kg-VK3?>AY+JymPq)sTJI5+6u6N>EOG`_N>#rZrN`-`k)aMCg6%<-tUos7U z^(r7+J-=aSh{1W-j=*hmDWr0#f^ZY-%uMJ(L4KLF%;DD1eSSmu*#1gqp5IH at VA!li zi;`prW#y-u+;(<$vO=2*J#!AiUc18bal$?jN8|m4#=@>^X8^Y0`Oy8YFOM?&;uA~D z$vy9GJ9`LO_y%P|5%KY%k>?dy#l%j*3N>WjOSfH}xS^t=QU}RzHB{<&g09TG2OnW6 zP%s%8S>pOVvzO0h*kGMzsjO+DX)fvj4 at 70fq6j;F{P?X~x9U*h`}yIyxVm!eQT=aA ztj+!X{kJyy^b750zFbY?JnAV_Rb743`oh16*$8Q{wOBSeT(jk!tsp8^P4FLM8GSbu0W{zNBIl_n<`=zkORuQRj!u+Lj0zt?!SzVPJvveTv5s7PF`e*}2H$*b2wS0>egLqo!l)OIQYg0$HJdf at c2=JRyp-PZ4Zz z2lWwbH*Eo(W7L9S at hxY@yq^6~fug~>#PXjX;7HT8{c$Yj> zU!U*2ll6##nVA$`X|eMEq1(6A2d1XRi-RQ^W!9&hmPZh)Q`o6Pe%h at sWK+WnLJ`}?v&k^e^eJI)Iinm>jxtp4hdF#huD z0_cmI3F4l%Xz6NpmadJ~c-nk;!U+UVK+5#8VK$JIIUp>IG=P{PpmuN3hL+C=Pk=t8 zyX_2XMeZ;K%i^z at 4&&+8sIAo&X1ncmbGT&#NK0CA4`F;#rstiVifOU^X8ns!KSyhs zAp{#53Q&7PBoJov6^4wi%W##S*e>&I?V_G|+*P?m6F)$)7?m27}+TjNe9-ts7 zv&P0r*TF&<(`p+6xIy4bT3BQiGm5!U$)u12(ajE(@!*p%K21$MgZ4^XTwGS@^?%Wu z(Z$8Z)i~dqz1k);I<`6Lebda0T_%LypU?QC!KZMRm0!Q^RRj at V1l$E|Y8)(i2=_n) ztNonr5ql z)Ah92n}^ZD4+}#;!dPchyBC11be at FA3>>hO@$eAEC!&1}z;q|8^{b>)&zP;SEo at fm zyLS at +c>UGxLJ&R#-rnBBt}}0M$;vjs!`jZgbzU=-{Vx*FM80_Oq(kC>@!|IzpT_aJ za=S_12dz|R&Ys0SI{9M%d2%xA?%Efgt at -Ty0`CK_VyhwQo^*wlZ<+Ui`!XGV{ksts zXC)+<^}YU_t=V0>qf_BQ-YMz9me5m;fy`b{qqx)?`ub=lKZZ0;eU3ypiC=V(da%D)t5ITc zQY}v>sK~U#2teKC$7 at qwldrS^IEVK at mE1E|U^OIY*HJ4wTc=(=1o6(4hod!dBY-_)6y&5HPX7v)uBOoQ>CERuPYYfD0 at 4KZkI` zaUj=Ac{spm3&wAi9o zcJAH3TKz*5$i>YOchWD>JU0~;$(X!%?c@@~PeJ70t1u54{PSyObvjyqBG#tHr{16r zmUnd`n1}V^MR{}$?4vhwx7;BZU3M1B3Qaqh9Ok- at c8LBWI(PRC9zw((>)Rlz8{R+o z0b1n;A%ThD3vj$F_x zR;&p6_87r%2!jxc%UkmD0a)vn$2hpWPCqrCGfObRqsR(H{4f1Rtgsa)0W4aWy?1?B zt_6&xMe9N8zvynWc)?9gShGFPRRxcYsg_qAO4!Tt`P zdG{IgF7SK*cO_=uT?Jth at 4fG82`gg!@p(O0e$^9*E-q}2HfWp{wD8`$lg#LuLG1I| zjELgl<8xvK{)Sk^UCmrIv{o9$X5`7q$pr at WN95iJ9M{v+lNHLnT{K{+QD}5j&wcSS z6*aZd{_dZcOQs|q9v&~P>B*HM`^>Hl0&%-+&!rc-{xTT`Cf=HnI0zE=+-Aq-45 at i} ziH~^vy6f?K!1QTwa3eStJ^-w=*c*pzH1$@*nFQij9CGV;@xmlCXwOyOTFMlM=sPX>(@d}5zCpkIXNLYwC{WOr&HUyu!HReld zyEJ?SsTV|=kgAG`iiv+S;Y}(C7K8QKuGw#yl%+PKp?!Mp#30KNNCAhoJQmcA2H*TK zj5!PvfmK9=A-`(v1bQPV)CnH~raDk9QU6f}B8 at As`P;u2gTo~DX_-CO$(8TjqwCDO z32f!E|7XT-f7g|WNt8B4CZqw95-0&0TyWYrNbv3c`32ui0BfdE zV1Uyecf}X9G-zA0{OaHIj3j~GciC7i-qWj|T?AY4oI!}hdv}%KM at K>!DB1d!mYZ`X z5e$fx(Ps(JukLM*8uu3%$_gDOUdC-m>yN=aHBUioA(3SN<7<6}Qp(tP*X)nZ2U9)#2$ z>@7ci_N)O`va7=JVOs3JR8y8QF=2#w_hu+#JxI4zc(?A{IjkTj7uPBT!K6Qpo+OBt z0;feSKR-YA!>vy5O2v>c3O|k)b8ooMKR(-);xZkjPTOhS6hO)xT3jrE^ak6_(54}q z+7OwSo97R2(BYy^`Qt+47NgEYsbhqMjS$cU0GsgP0XS9_=Axpa2+)3FD`^lfgZalN zruT#QPc--wB0l;L-((=9KSK2IKK#!8bD$^?$I2hAcS{&^piD}=t~1tOVt?z}+_`@P z;Q~8E%ihlBO)#6v%F1w`2AokRKu}(nl|6ynCk`(D>GS7dhrn`O31L0H(nu;X7Vo|! zb+Erz2n2iP%o%ppq+oB~wdP=6*q-Y%ILT0A at t=;dR znwA6ak0QbYP>-^v$4-D>xv*FBR?M9m68j?{tU=Mh{~`_~`AI@*?mDp4tDny&jDd3U z1EbV^w<5Rs<42>uJY8gmU%Yr>JKM?ff3`0if^FDy6T7mx*;3=V6La>SqLC5n at 8wZa z9v&W0ucVMdD8 at 2wE2(no?7IMgxUAF at tTqtxjD-k|JeEyUet*Tm7vydTD5*&#bj~Mu zG$gPc-qU07sB$s9IOgW&2m#~W<~c|>*=~TwM4%NTWWxkjU=>#DG$KWl|CPaTFyeov zC`EsLy|mTgz3&T`=E5?*uoGr@jyf|yT1|+ayAX}Wz&pY%HvT| zqGSO{7UoY#!--`)%#dcQ!Q$f})dHK1q+sJs+T#Wiajfk2?+vAihw3sz#v^7#kN^*` z^82?8kW_{9a;Ry%Ye1A}*7)?aKe$ljkU>$*j#P^X+Kl8l6#Wl!3*0xYML}X|lv-+5 z;}`4hG*?$ubym6Bqv(YE`oOVMbJEq#Z36tjO at L%xw{^3ZQBfS&GymrB#5=jyM*!;+ zy!SO%A3GV(_L&n|VIC(Yo<@3)mzKz7z3XO_H3^VQlEzu9l&+_Fx|K#NWA$58kqY!v zF%!6V#wW(hijhoNP}EgTI at Bn#Hmj~5RiipXec)CDR7um22Q{1!Z1H*=zOUo za(-bryP$v!rER at 0!NcLu8IQ|F8r(?Etzex;4V#~xq!qG`>%RMcDtRw^EpDI(0l8Q8 zt6gNnE$7X4TXl6cWGx91E$^7Z;*Q!t8ZGarw* z$y5(m(3yGfd|GS|`?e$fHHInqc+nuQy=}vd`Cc$M<=t&8tQPB3*{NdQ`yO!HhI8F% zvX_14>yb8u0FZlo^(1xhjkzCWhjo*3*k)rOOO0|{7V*9HdpC{@)|Iqh?MoBfysUQe zu6x-UhzNyIGpDp+!_n2q2<665AF4u_vg(@Fsa|%mAf}Muh`M zd%Re-gZ;licKp2tH&P6Go*TU~s^h3h!Aoa$2)%BYJ*$hjx4mUd`{npvV^>qh_ zh3~{5tgZnb)Bh|Fu`-F!j)GuD><(Bo3E-nvapj*1u1tO(8y_Ee%FVEkpx!ea?6b@= z?31^J+d(Jr0n7?K{7wWRDJ3WO_3qH-qUFcubihWq-QC?}jEp(|-6CixL1h&cKeb$~ z at yg|zMqE-+#pRalyyflIC at vtBCtA4yU1n985ZHGCC&0$n?yd!Q-(~q5A?JQT=rjYO zJOOvmfTZ3JOnLRT<9vfj5$rjM%hHD#1mh?|zh!VJ-`+v*4`;bXFOYb_l$RDMPtg6> zQo6=~;w4Q?-t`C~Y59`l9vK4zp^c4=pTEB$cuEk3?ooT0Dx4WAoB&T%Ypq;QgMxg( zPJ;yW%dg$9X#E}XV05Foql46bro9=6RM_~$A=j%>b89>{X?t at RQ$lI-O|CbsmMIrn zTU%Fv3{M#v8iFX#TI(N-bB~ISo?I*&l>vvGsFt0imfe-BEmXC$R9P@!+3m!oo`;92 zf=m at +?9>BY2xy(yYcamC5bU%#I1VA?r zz}y6K#(MV3uqCs{3Jxn9+bWnmPAt`bNmtRf3wsSVy$R&?Nvptz%v*o~V0Ot)ogyw8 z(0Skvy05=q1J>c^@Q0$oz3-(5+QmtwlfEyP+~dIq0ct(ZR?q*wQ~2)=e_uoS4e3Hm z-B~)c=yI^P*;Qn!07_UoSB;H><2Zw$rER5^)g$eaxnHip9!biX;W06={U_!MA)9W= z$@$5qHC!VVJCmDlO=N1-BKn;GwgxF>ea{WmYUbdl8JQ1PWCzub5 at 1x$~C)!HF6W`J7>YK#=fR50`Te zaaAvGr}iy$vKQKRk6>*F1O*$ zE!E7o=QatJh;6>t%0&f)-#-T?ORIhHi5p6-hR`N}GM)e(F6_L-I9O(*zjJ=?pzPt= z)MxOxQA0Z-&P&h1^q*#D|CG6xXK~fkep5>>RxtM?N$W at cUq9d)sTj}u_frvC{fCj) zK|}!mh2h6;eJiOQ8n+#T%yt>~rzv>wg&IH-1CAFTgQyyn6Tl+`&Lnt`m`i+lb)gZg zQm|gN+xW_OqxGh4^uEoX$%mK3Z_U-rz}gp^bsgcUUZ;rn+MaV0qyAUAUWFX)OuIva znir=;oN_iN2P_4_OC}E{N9RqRdf3BrZ%8}=R3E6C#PV{D6Yp^Qr02uLx7i`uP?2+G z#Wr2=sbH_;S7XoH3JNztPoPVFu%AJnqIB>3SX6Nslh_I0dhnq50_#aNm$TTS_XMZ~U-|TFV2!{dbmi+ypza1pu$QH^5x+Q{^PA^;vvDw}*0A>L*SM{) zB0wTBgP=gx>4UL)Uve_C$B41#-jV%hKCHkw90f;Xxib08>po~PRP+To=u=iE1a#qO z9t-Sn1T*3~bBZ=Z#=N at MqB;3SFa`q~s$9~Nap#~+$Lb>i2X^!4__$B)?ljM>+qW|} zjsLxTE*o4P2M33kq7y%FBOO&j=tJ=JJywsNAS49Zup6tx0b%{F^>@i!bf#5;LXk<^ z3YY?13~-HVzoj62f$u%QU_8K`DoYN%tq66^;Q?hDpp|PPK}O!?DByB- at g$d;!CT5oT2PkDI~BV%KLJ4G!m&RiSroje=L^YVTJmLDP48GgnbGMDUYK`Z+A zMMZ^HLxO3s%DJ(+w^5OWR?t$TB<#b;2X&Cbb`wpfneN at S+$Yr}iI9B?vG0 z`T0?msi~y}5htYi^-8D3tYUTZYXL-3kc_JU;Ozegh%L^5>FbH1N=i*_nk%h0c&wKn z*f{?9^Zh4{;}{GT4b7dsOu)P8>1lQzp1{xdrS0ta#CLvPI&=QK at mQVD#=(8X5}tvc z9!l6x)b|1WS3k=;lC92@`tF at kSMrS*5$B7K53-Z7AdDa$@9a1%-pPl+*j+85WBx|F z9dFtm7X+%{%y|#?wt|0W0nQ8c^Zi at PG2aFYYUOlyJY=5J`rM5bynVw6u*6(0PM931%wY(>t4yVjFKh#LSUyJBE?FRxg`8Zq zcZ_8b)h2mrwpbmm>Rw}_t~>BF1M$AKvvX(R?m5aMM~9dHd-w6R0JcQv^l1#pkofp` z`AtbmCbrvPyc(3FZ9S#`CFMvzNhI)1_Y-D6)*lMY( zso}@A{U~m?6l^pq6h5WSIAl_Edk4)G;QL%Ri%!EO$ zV(p(j)@35q)KO7Us!V-~p-f`Wtg#iBKNVWc6FSdC>wDG5 at EMa)QwM+}gOyCO8Y=zN z*48XE>fw8n*|T`ivoGyHdw*;2IjIGaX9seU at 9xH?;li}gv_!1WpltoY^r=&!@d6VUE$(pkdCpd5TXz{|Ax6UI$u&b8+ z1xX*nOGT3Jy?=)jVDxw at Bwqc4`vnkI;jdo at fu)}WW<(t?fTaS!B+)-pks4*zx^Lre zdbYN;-GDoevc~^!E`_acrJ&2qfxc;mUfxOlhGu%tm7_YkjV<6Dvf_x!Nrfu28 at QK) z9f)~`ECs=t?PYojoB`}kmx67HT3J~MU!w6bi;_L7dr+jpdD6qfBXx57&!5|yf8e(= zPfySB at o^kdQc{!&#!6LHRbvYagv`v$E^cnJL+&mvrv(I525k%sPIGc{j*XAA5Fc^e zJC&7@@wm;z7oq{^hmnNfxlGD>C2GA!7^mLN#Mor9zUHube+mVA>Jy9~ejg_XuC2Vq$P#EPG6UwzlfNd?BLfvDH-b8bbU^0U!9f6nMhV<&*&#u5D(96a;FtfiTNb&8Ekll-B(y`n_y;Sf}aZN5M}ZINxO9 zE8KRT>plwWdF#%dhXt=~br-RfG=Q-Y*2 at D$1eneW$A_4 at Kfh*xZ$UxyNaVeUj67H` zjGeEsY5E*_o(~+sjnKP?C4)`~gq6AUga!j%$&@a3Y~0o3L`2s$HR&EbdPGJ^$(Ac& zJ64x4xD4=tI`cp_sCk53Y>%;!_3$x_^68zfo6OB`EtaPcAgjUROh*ya`2O}y z1$Fp>f`TqvyBBq2!*=r8j!B~80wz2n!bi|*aI8?jpqeDw!&1Tp||GYT4t9i^LF*u(P=3)K0%QHv=IminVg7w5U6D at nomNB5Kt@XZ4EXm#cBy zl%wbHjtdJ5Ti*r=-CMykXuz8YnET+t1NI9S{Ppsqt)ru&{-Ha){F~M7GP;l at yd3kx?lY2k=3)#Sd1#B$zjTRr at oFAOIQP!CxjZv7fY5O&HHR~)-b9;&0hFRB|8FneHf)q*{Dej9GD&v{$a z_K9r15w>GR6AuTQ2m8#JF_3};wz)-*!1i|wVvz1Z?URMReAJ*`S^L6c-1LhPF z_`0+^(MZCVEdm4KzmtFaY`igWa-&bbB~i*3na;pQA5`lfsb?Xs8SX3-V(aTl(4T;^tr)G{WZN<0JfN>KKHhfMRauU17<;ROKD|&`eX$ zeBkmr7wPfJfM>eCU)RQp9x7_-VFxaoag&~_#D9(T56 at TUs1PqFoDT|5051+Mn+yAj zzzRqB=+S0z?GlS=6;Dm3!J~?4&vK5Dw3?S_Wgg)lV?5#{%m`HdI69i>~3Hwxfv!2cQ?bz;w4>L|@>5xImmxin2JTl7WUEPGWNa=llzHLU$P zVB04r*{bo^3f>>nvJjxpe3G9*c8ZK_Y+WmO at 3a1$)uQY>n2MUGed_5wNyWt~T15D` zvUl#F3AbWBrn*;w?{#qx9!)q>@A(=XCn1rswB#0`4=HZP3Q9>ybuTVd>zNeMU42Ue zUvAY*@H^_>qMGBU@>he~L!n%#8^ZOdj1Y~>r zZ_w)Oi>qm9xL}QgpRFaICxp-4At4{1` z>O9++P_7aRy0g0fw~zGg<*<(615 at +i0HE}4LiGFqSQLS+op zq%0ftVtCk>m-|b6p?jvtxP`=HYdr9loE)mTfp`m55dVXRivD|>qhYD3sTTi;67!k% zw?N8u5Qp;{!q>0geVe816ETi*2~~3A#N11zM4mTGFy9Pi3 z+&l%V=?_==XGA9b*?c?C{@${;@$S~DYN_{B#7$H#RdtoSJHxTIlkdFrphSz$WyRnp zwz6;03m6M^!_8MR>kaDsj#Czx_q?-6FqV|Wh;PrbiEhuP$S)*#{?_EyF3lNi37Wab z47wG`nysHj9Tt`f*UBB{D6TrrPYLi2swygmQw@@kkOau2L|6y63QsuQx^)cb9Mz_} z%*YrSxv?}c+q8no|DQ)c-$>2*+y{H{=$U|H$b*P zSf`Cfym&z|=jB_E$?kpD-QCTZWVU+gq=Yj>BPtZVzj(5=xkIw*A}cK|1qJJ#TvNy0 zDnZs5c0G~4#Nyo0%15GpO^68Ny%o}di>rCb#*$OpMj}wEO zU-x=gsDX+EKw8M&GxT*lbVed-dip5nNnriPFJJD^Gt5j+`@`aJ5G~Ek9S52Nx({xC z0)GSFl>S*J?`GBc;7#S#*VM3LcOS7_Y<4eQa4#!=8Q5 at ROoxfddC&6v->M)vo6K_n zP`sM`?FBc9S8XO}ma4#hM!syb@^{I%clz&2J;h~G$aO0mq`-%xE<1b+2J_cEvs}E0 z`e)JSG93B>218wQ6v9N;j>*c(W=B)ZFh7+tl$rrS4%ix8e3R zUJRu!ih`xeWpQrJ_jtg0__kJp6R5?Ercd&8K0Kkmpk=T&^*KhuyJnp5ggVs)?vR|Eo{l z|`%`jLF3L{#S#BjZo1 zitlCu-5cTIWa*AVgUo8m%F0#+6%~#Xe5_;3t&l;7^Y&_PBzeIe)#Vp2?(OY$rQM-H zB?j2Ms-ziJ8X@{_afey5?ca-pAu_&qqAy;lR`_1LT(R!m_rN3kg=dFT~L<~4Cc6Rn~YP-sU0y6bHoqE`HKEsX-JYPyPXvA0>2tjLmg+=)sBD_Fq9PeM zuCbY!^VOs7Zf*^Ied-lCVDGm{42lNEAR^Y*9)adIZ=fj}AOel#_`D*M*e$)WfN$Y(H`iATvn~p-};*6yycr~E;HE=+QcVh)*1$^dx8pkD-l_`vj zjnOpR-wvS)4``HU&z>RZg4~>J at F)D#+A8hjBnXZQS?;YZ7fWnKiBy(1S+A<0AS%vu z!NI|A^$485SvZzR%@6wJPDct4XPgpoLh~tLTkYSB74f|m_q_TFIAbQkn`yavGrqe` z>05W&ox=N{DdNOEE7~dXk+h*c-?RFBvG@=1JQS;=<6V6B*G{iUbNXN)LROSD2D{eYpb-0eqxPeDNe9Zc!zxu+#6Dw^7n+}bJ&p3wXVMRqUe zDSe$*-!Z}1yW4r}2kjaRYn)?i^W)y{o^5Sp1ycSqhp^`|=0gAyqVUnQY+9`JWz;OS z6lD0Gr<;xHV)SCo(t{W at ssRFv8uSrFwqh2YLnK&KBk&Vje`XT?wh{ieN*#2K>fTgN zuT6hJb5 at Y3N-8R6h{NsF`S8jGSH5?2km^=C-GFFn{c>25Utb(5^GP_Vw1K(_flO}4 z>Pn7(bK96tvkkUrDM>C{sI%zvDalg}ZDQqo|6y%oBbWbaX=##8hzy%p0B+i0vRO;4 zooHXHD?E1bOSagT#?P!P-1&MMLv_Vf%Ih1Uu544|My>`Q`0(PURTFiy#qaf~M~@XR z;Mt_5rFBm>Tk*L*7Qw;6QI65d?v*XOp=wO7V^6FU%$eBhpF>wuYnPnR;%G-x`byn} zI=HeRp{l#+;aSr at 1IN3nc{YMzh+xN2I29BW$aW55Dc>$U%p&(bGu4GLGjZ1g%QcA ztA44eXR1~|GN8tM9H&noDn5YFNw>g)YnQ)cZG92=c4FNtzsoGO(+NxkI;Xb4?^l8a$60Vx%XUiX1 zSXjC at RSR>stWVcm3brkGStf=n)-Zpmz4K3;VM+M;`O%St2VY(xpDHU9IE+LtY6r(% zbIBjM)mGme8e$!h_{NXLy>Ln^6%JO`BbH`uQ{_hSt23 z&-TLZ-fT*k(a!HhG}Xxmw?fi+=7uUn_Uc5l3vfPC@@SY4UdN6dlLW8cm2{1usih?a ze0%reJ2kshLGYAF5aZM=HDH&rI6ef_WM{9i;J?D&x at _}jO#hJ6A1V0pT#wPjiTs7A5CrNwOAqVHH1B5d6~R^?(t*H9HZ7SwU4%)c6Ki-FRx++L>TMQ>}IQq zKi7{HRNPq4Ehp4iaJbrUS<1OX#qzLkzMa+9qk7sz#VwDxi)PV{;~XX`pAyGXeXYRJ z#ijh-%Ag}T%g at fvP8rJL{Yzj0ZTobc(NLVk!R{sS&QaDB7~ze%QSXDj{$lgUC%H;$ zGBWtEp}tK`*U at M(swPTHAE|I$_)*Nx=PG;S#!;jQcXvIee*9pSkYK)Yv#LmTa?e^^;2kc>APcpsdN-kx>&YEKh zu?GPGfr$Ty8xj>7=cUJ%w)>hVAF|f``oiVK04n;~7~tpC1hVLfiWuSBcn!^_8k(A~ zi*x&zH_nAbP|gsnbvirN+vchf0&wCH5Zp;xe)D8}B2L=N`k#VCYN~?79u-Jl#!_qS zGbvwuh_x33ApOvsKB>emnS$u5-{E`;1r at P5uB4tG6Dod;jTz)efcodw>#f0s7(f-uFN~<$qp+>y8-YYnN~Rt0&95(+qA^U=A4Y#{4yG|I8%k2n>`cBv=rG%f13C8c$ndGF z4w5=L!&VY=k3lS*upKp<9Xdt+E?nK30yk_LH#}SWe8Z{31rmxp#`TTrS&in$e4 z;06Ea>8Q6hXLv8n<+B2;l%wfpO;eLH)t1$}d<)6*%g?eD!K#wkrjdnL-(_Ji#R(Nd)ak zD!PKH*`CjqS#<*)hq@`q^A#E$eIsO(=MYI{H{Ng*bvI3l=wSQIhDt90V=LKGj4@%Z8B z at syMl@>8cmzzUrf6olW4CI5FAF(ZThXo6}F8>iEkM(i4|GYa+UoE*7%-)JywXV~QiTi_=Y|Q=M)mBqdW`)FEiw$N1$)GmPBM35deZQ~mth-eY8C^ke&Dw*b7<7IYKuHFy4eG$}PV;!1uS;mJdZ7juh?h+1 zO|b6aZ{LPM%1q77+!@;0-j+RZeij^T&+vnUm{@g%4qMT*$=G*X8GAQ})B4MD&%#D8 ze(VieP4W8Ym1AYQ;(t=lfYW at lp2-qqFYL;z6Swd|4LAQTZ&hmQ^$LYJk!QzC^KSap zmuOw6s;*AzGP59ZdniC}{)C2{I^Uexi2uSrUHvzVBm_*ZQ{*|imDFIl44G~lKJ3eD zZVIC0vmO at q*xTp>kEVjc`mRjMF>~C$eY;(VJj1>14K!`|qy6m=yGe7}JUWA+AhtmT zA696My?W-{InICPJS|R7Pfsg<(jqdT^zFYst3~9(1v16XuCQmax~eJ;zxc+D8&`-Q z!DrLK-WePoYJZlT%!*E`Ub;j{M at RSR>(`f0f|!_?z&L!Ms-rfvEG#zbN>+-~1_{25 zrH%b}7QhJXFB;N?W#seRcKG_{G9fUCN`<)tVQXt^1Gp7*zJ*mlfGU{!LPH~vSvr9X z&B>FueXI&o)6!Vj*l_>q9K_#Fi_d`3IjX=*dqu|6eW38o$Rf1sbKu~#Dp$_#EjeLd zsZGPkm{XUHi1V9mlfl2WKjr+sJE~us#+;Sfb+I{&HRw9TuZtDrD6 zQEHY`33j4+me(f8x}pYw6g*fsO;7tc4Dbo8ZSo|_ZzFh;zf-Md6x`w zMOQy4-uqNhv6H>yd5oC&#;se&q_15gr>ECSt8jE=S@@`Rw8v7A4KxO5W91bsIebo1 zqhK!!*YL0Z3|@3ooIE-GO?}Z#-Y0`v at ziRquh--iI^DDa%?KC4sXK{qfk zQ10>PJ=Kp_2i|hIY9e=8e_s5G)flz6aa7ALX~oOR!?CeTSZK;#D<~83^76XuHKJ!T z5&6->>ukGE^A#I&^GuaR?Ksjez9SPlnsE&OiavA<5&n&ex|T@^M<=f6JJtFt^*>JP z96CR*;kDr6;$AsU`_HNR?3|n?kO>8r1N>F{+Y6|ydfwQ=fFuwX#Y=X6%`?p+lW|~+ z=>vkNPv!xI^% z?q?6jo^ycOE806cqVe-SU+Xu*qGHZ7?I&erWmE;!EDy;mLZ3gs4je1+39|GEU|7M> z;wOk;e?pqn8+ZkmE?r8iC*q7c(bd&ujoXio;Ogk;L}qPU2*#pA48{ZRA0X at LODg_! zpvd&saG>?!IqvGsk;V>*gXh2}C^sNoU-pcB*ftQLx10aQfrb%b>%s3gu1>ZnX=oh7 zAa}l0K1qU(dIbdqq4_j$FpA5I$qScgiTa3L)yvuqcvS`xGW0b1=CKnmRNtXFoQq8u z8$R2i%S2X()w%_$oSKmui)(%{%?7r7pS1B}n#XMUIz`yokHyEwE1!P_@=hUA0 at nd4XHyaI!AZfiY at xHkD+gu@m at 7$nrU0HT!;p~Q&Uq5FCIZ20KjRJE4H(B6%k{IWTI zQPN9{_1MKH*ogx=ixPVD(*ZU%{AE%iwlPn9ebdA-|NCJUkgR=uWvw-bg}YiWtqG%f^gq7jxQEVUaO+jkUNZTrc*qRODJRScX?XOSC>2C~&@B4= zw at hFM!>HHxa|g!D?TyKz9eY0B1DkI(W9aa&-&ay3dzpBe)0#H7G*nWG;dk%H`fF<7(*_s at y#BK{ zzUy zlRm~0S)jpP6?>UbwA6BwpM~vGx$1djcwZ%hqfbq(n%|CXu@*UxUPzb zxE|YCoBp!vWomj}--RA@?bOu$uL%;~(#J$tSPazyet&FfSw-jl(3zOKcZpHncx+_r zH}7qZD%=SFba!rfxkX}sl@!fS3)}x7Ld7)Gcvt+PQmbDs54=NUe7CpVqH_xi$w4@; ztgcEudc at Cv>C&H4hdJf>ixR%i!@~51zV2zVEPrkwfW5p}zsPW$si~>yG(UeguJ;l2 zRCYqb!m|`l1Zjy11}tgm=_^cV-&{y`BE2^H$sNm19T58T>Ep68AutesE at +$W-FVX5 zzD!3>ZpU!ocbw&3n{`KzCMiBS zExUj(KT at bc1YZ%i at Pe)%Nm4u(biKj1&vI0bbLvVYF)8UJ2vcFtZ2=%kB~8uGxTTQl zKpd5GlD|v(Ed{H#CW5yHI=liPzbDX%ffr}*Sa+RgiA2Y2b{5N4Cz%gu(Gfq?Do##L z=2?%2Y$PTo&dADY_+USysH*D!@uLh}sj0cy5FirGaW{@XmQTO-;KA97%1Tsl0X>GK zJh%pp50B0^8LeK}PVEV!xD*9EVC|2&={zeDCCc$!Nr{c(*`}?{wcj34_U4`Fzhq3DSmCDi45glYo>a|$i z{narUbUuD%YrAZ~!o!2nrfL=6Yb`QCk~Lxsg6*b>$-DHx8^>5KeJ>_5$5lVS at BFjh zCt-g*eRgiHUa0oZ-QBGgi2z0R_4UplajUC#=#UCJA=ouE6bxub58i+HdCf67_*^o6 z%8-x{srxY`k^77~m*w!AHoGZ+-BV{(sXfhXZ4h9_upUBqV;}o5?ut$BHC8l1HhJ4-QkfAbrTiQ|ZH- zh5nzoRm6mah3CX)LREwY9}ISMkOFH^Hgx9rzI3PE{)Lx~9StUvjwvkUV~T&^qexxj zL}DOfvlQJkz&I2VZ?9;WIoD!cra+(LG~(v&e%`~=((>!Hr-z3J8MwHZ7}|a3#j56y zALX|<2G%P--{+UgQ~_}$nWPA)E19X1oSseqTn+$jQ8IW|XLe?WAW89QdATrCO=xg% zY=Hu6#MfsRD=z7}yNi(CPq^xM0%97{8y%*9c9JVRAt4Mh0#bT1;OYf$rrzFObOKjN zS2yH*yvq at Zoq-NA1_pQSnm!#TU?OxzZMWkOfMU5-Z7rmeT2)nbc4;Xf zI+{lL-o1#32p@>X7`b!BrKQO=HB7MLb8~YVl};BxUl+P<%wOsCR7>w!#Rw7FJ3Bfi z6%|pwefzf4qGb1jl9CeT3D$d0-K`M at S(h!rk^?3vl#h;%N{kB=Y}GdKw~sG&oN&5% z^JY)s@|phKLMvBEKCcZQx&6o|Nfb_4~JFp9%CCe2MgY=yCThqqvmU|JHVtlSRBPU9=JE2Wua^?tU1>y%32QMvm(=FA zx3}l=t4(M%P+}KHOiavWceQ0TGGQ0b&dx5Li5}8L`cn>{px_UR0+YAbHCh(p&ZMi1~XbxKS5omWM>CE6HuJ+j|?b? zjEtOnvU^}!sG+E?-a^BKPEi8);$whtm^Vk+LP^dwDbr>?isG4CwrLxg8sm+bj#3Pr2lk{0)qFULMKi%a=3q@?;zxFKcOQ84gnj zSzCK_T#ZutQ?B6Et5*$~?xu9fX^q*sxb#n#fUv1 at Vc|0Qv{dvYU}?u%{aA#DS9_8h zb-{Owl8a$)MRY1VijEx6NE?k?P&YY?Gdp-OGBAi_&xim=7i%p#`u0I4`TF|qUIro{ z3vLeoe4i}nI{c1iyHq&+ue17SaR0C$xbOB}D_7WiUlehTl9Eqrt1Rr&--C9aIy!C| z8nS?J=bJtiq8T_d6YowUlR`ea`$K5;XHiG{V0-Jmxo?^GT;|fEQGb%^K#yeSW&1P9hZ*6}I4XFp^GAf{;K;Iy%o$+6oK1w6=)p z#8ntqFLF%H{vP-9` z^81KA?%&5VDf;sUTQ}ep`Txjz4{)yAH*8!YdnPNJBxEIIW+X{bd1UV-J9|a4vnv%6 z(niQ0$)?9nLiQ-h9)*bib?N#2-{W}S<8ai0`hGv(`@XL8I?wyQ#s|si>jlT8diS%k z-cCGd7;;6f%tq=+d zTbm{Zo!44jETye?&NZjYHobcqsk0cfu_+iMInV^AtbE#H4ZiWHKK;W9M(_IS$6NqE z)1N&fZfk3^b#nR^G&?`<#8J-})$rGSuGReE!+Rn6<~KPx4SjvgPck#}{Wn}Oh43$} zP}S0+3^dW!roqZxtit~K^(*tigR08Pdo<6U&9|uAXKig=Juom3oEAOkjh9|G`LRFRGgIKfb93m6?KyeGH&Z{6{ZotqXyHmtr82;)47J1 znny;`M7^mfDPik~7UbK?xz82Hg}&~uy}Wh5>?s<>h;iI14hfjz| zNVwL at vsNsyH8YYD>C!s0N+fBC47H~Qf6ksL^DA+y7 at A+^7#ka_y!p&c+Mcn{X3D8O zD7}n9<5-ebB}LZ7S885f(lizYZ~A0YW4#jAmY2svl?_G>c at j)i-tuguV23wla(b8I(c(z%O8(bh4ivdIyuc39-+M^$+`r*mk!$(?Ay0* z at q1?6IDoSVER%vlAxnc(36`Wbhz4$))z#I-^J=BvU2%CnD>h&lZZd3GH$mf2Fe3zH zP9=jDv{!Ik9F4Bv0dOcbzlLVeq;BRULL1wl_DwNm4~ZLSkYXsE{oH|B`QEm>p1V at 5ns&LSCL25i#*nyqAlM z7-*}Fnf at EQekE~?UD2NzM7KOSv1OOiW3G{V1n3KD^B>CSKL;#Z(AMPBIT#~8lcaSnJ zb=lJt$2qkV23Em!_XFEWzWIsB at J2_O_=fbyPU^e)^RD?j1V;EhlG3 at WAZV zqr$?ib-erqHO?NJ7rtL#&~0pKnJM4nv=(|rSYvc%W+p at X z9FSqo84gJa3Bur-YPoZ9(b49xog>mf0x8brGQHdu&KA-Ej&W{?hMe5h!23r)8m)>A z#pl$^uXLA1-pQu6f4v`?pK2oH-A2wv9;#xxF2==Gt(q2FS}-uTU)FfWUG~9>;M!u5 zpMDc~Z5rnZFFCJy at 1qRlCgrIeU#X)Rqlh6gX+CpRp)~^H2duB8_1$xNr@=C$$>|A#FCQj*drZuB6HP(DU;0V&k+gMfMkTbwj||pFVx+?fWwAnu7zS z^ZP$i0U$9mV12-*y=U{Au-R7I#zyzd^!$9I+4eG1<)2>zIsy!Rh1d26tk-d3y4Yim zFH|h-?5801V-vgX7uqXMfJ;^JEngvG-PFrE1sno`)_mp4`Fr>8Th4tcaw~876~oNJ zqU`5a(c<~_-vfmuC7A^UcbOO|sHq=IR$`etA3y)c>&#_GK<3Ej&B;&A%XcXDf2$fi zE+f--#dPSMM%<>nzk`FY$-?7(Vd3H85dFC)9mSZv#U6!j^jAJ|YaaOX6a4ez==)=9 zyN>i<;y`>nEe9nH#4B4{fxy{qk6#Z1N_Odl|_N=Na6ywtQZ&=ynSskc(s+r7auD!j7gRA8h^Jo ztO`p)Pfzax3Ml>QQ=+WPxsSV<%$(J3+)w}>8IEbWiHV88E7su^qe(dZXI}#&V+cKR?|{2O=yfDVhH26&VIDJ3AE! zy8vY7B?G8FAsXU2w1NWrkt0X8s at zUrb#-*OUp0zyKN|&wo at v4 zIs#rb!*0qcVtz_`wlci^`gYuqQ at DEOSZeN2aG!~ZNvEykR);*qZ;2#I_;M{{TUuH| zdI-*EEE2B8pXsz*@ThLA8kW^%vYC$UgewvDS*bCm!qa(`|E^&MkP+CfMA%&m7VddU>8jM_fY9v<@8Tq z0zAKYdhv+p at 7c%ir$6So1t8==i`G=Gg73Y@~_w3n|W}uPiIQvEEczF_w ze)E8}M2)$a7O78)7C$8oO+`qhL4rEty3NYn*`*?HKR at 2T$ISAd6#6F?7hM!-rneq? zY*T~W0-tL;N{`+)nEv^*UggHO6f6bl92tqbbBEqxpqR?2*db}9A(v#|K5TD;-5UY< zPXoK+xUqU+SF-1s2fYYCP0C&HC7Sx0oV}A1_GpMu at cWhi{P{lMqS$~CTLJ6qM-&zo zo*21e^rCxg^21$hmx)>is2vt>gGZwF`J8RAmy?rIo8~Tpz$8V1VNU0?dnGT85z&u; ztR6cPMly9ZHRF<&qLW;s>^{%CiQj_PVLP~(n3%2NjEp_3G5e}}&JSO^cFq6nxpSus z4Otn5^-qSzINj;7LsX3AREkApsgK-`2R1qaY9I&xOB0Najy^JHhsPV(OmlU*`{k=w z%2rmqjZIB-OiWFC2a=MKz{D}v(Bn#~<;27Uwj*X`W$o?HU)8aMaKF zY`?G6pgG9VFo*HRjT<2GL>6kwTl1^;&PX)UVjXkq(f$`#^I=P<@&G$Vt zE=#EM^b1t(w at -1-kJ-!aluD(v?Mu`QZErvC?&%49yzZ-Bj$WNz5g~!UzdsjaR6=~b zOB+|QzVPD}d%kZ^fJ3AEosQ$c2A>%{c9;eY3<|xYp`k&)&^F;kxR255+S&v*(RgE0 zfsF at 4LPF9D3mL$D8o*uY4j!DD=02a)*r<-hpqMS<^5fGH(9L5&80+`a$lq*Bk?#0W zq?|qQE9uzP^{u?~FM`3tKy8^9^vB=37lQpn(Onbn-~Tqv-Qf+MDv~h=4_9 at BfxK_d z)7!^Kng8yEn-Z*xv#Ajvcrnp5;w;*jv&o!nI0=B?N-0NBp%?!BXS%zk6Fp}3hXbH+&yIP$Qeyd z@;i6#yd>OWvX*4+8Xk_lf1gqE`0=H*F8GHanbv>*#-2tePMpBIB5=g`#KcokanB=q z>>4&!rY?XJVyXnZvCb>q#kIB5PEI0NHE`<|A at Bw+9-i9nxX6cnl;CCsdu85tYje;Yo-FDx9bWTR|f?xID?UDCV0E^|-w5KWk;r|0rw zkxIr%FRzlb*VCJuH6VfyM_%T?yOb_4pp=qZciu}XC5Fk at l}2A~xAdVurG-PB! zAe)rcI()B!f`YvJ99!f3_wP4VGDf*)L36gINYg|{Mrvqj zO)6PIaD*@V$PmH>qUxTX;@DDPtSRf z+^x*k8HCM$M>D}BG*5tOnw^f0jI2pMZol~Hl6J<+m(6v&sW%!M8#@7sTONp3gp at c- z+PxpQ{a-FXb?T!>T1|=dR?+IbC=0|TQ-XV^-9?%{IZzKPd}8O|XjdI at 2pHOsNmj6F z8g!fU`f2=)xUcoiZ(#3~S3)!3PRyq*k1KDIiC*EKLO_%B8M;>C+x4_aRQ zQ#!5=7?=s`iH&Q)EkHNL{r>IyP>Ua%-YY3_3SE9v|N5V-m#WN&AkPosaoxsm!xE4+ zhy2 at KU#JMr8FIdA7-|mC0!aC(w>J}_B)*!e>RwRV1mspDP7pLnXgKP at 0k~r=wSa`G_f;h&TLib3Wn+m zrmkdECM3|o#{=Gqnj$kZ*emQinB at 5vdtH9s@^W>RfZYRy87>;Q(bC-7s%B{TC3F3Xc=@Z10x_3-d8`~&=?$Ruvvx at BjJb(Jx`LTdHmW7`*)@b65&5)X5^-?mjj9RX;tAV^qz_8vMUl% zGIOwZc0PUiG84 at L5fKp=H+N8YUTO&}U4$ABO(4C5N&lSZr%#{oVg%cu>0G+B*UZce z4_)j(XV+kN;Rg>+PmP~fKYbe8CSwf!`t>W2|FAZjx~+|se20DsVWVP7KtO=quEK?4 z$C81r{{F})CeiW1GB-RH{^y?q09!mfJV<%4R)C4g3SipZ)RQVWI2dz16xT=ha at cvv zupvbH`pV9~TQfzm2qA+ZxiSXqzy)Puaj~g%_0b0{33u+?`t(T`)XmZ+Q0`UdM_NMg zN_boF at Dcm_f(_3pt6#&zrhw$zhN7aP9G^WOJ73*=6gNOjSd)E3QpG6-uWkA`SXh!) z#VZ!JAA0P&8YL!}aUe<29er zl80VWywKU(8x9Z$Ms8{_P*!UT0{doG)Rg}BM~*BkEUPW|k;-yd2;BX^MIZU5e~+J32)mq|+~JUXBSS+mMMY{*3c`kkq<=G{)NE|{L-U>Y z-u(~)I&r)+Lls*Tfr+6ef!Kxph%wjk&Y`n54JtHQJ-3<6{OLq17YXl+nA{HBUP*%| zmA92QSoHSxe)wKdQu5x8 at xRs*q at gOpSxSJ>X(CNnZK z8wuITy{Gx??876%!jyzT1Yk#=WQ7Hrp!e2?*Q&zTMglp(k-V2b9A27p4)psXx|}kD zl+k<>xWvn>tU3sC7cX7fZBq@}3S{^0?W7J_(nXA7spY9m56^v0=y+!Zk*W2mGOeCV zxCad#d1ypz?61xjeAaeUR8;3k^|Tp_5x-sLw%G9ILgC%JcXTW)CoHU at zV#iYxO5u4 z_e)PQtx9vNq?lNBE~jakYr`S7AA_mVVqz{Ibp!?km}QIHW!d4&*VorOK{HXaA-I<{ zCQPZxKtqF+kDe?lP}%03XBS_s7x}K4Cw;Ic#F9~NMa6*H4(6HIqlubV z8~k~bSm6O=ycfB!B_ErtJE`wGY~7AW=+Dok0Bh4CmUOD^?4-rvAYg2|moH<6rj7su z!JBrAr!QXc9z9C+>eVZ}r&i-pR#73Ww_LiH{T|P5fZ%VrwPuX8(=luV5HYe=4Wl{a{_2;#?KUF6WtET>SZK>9w!_CgL~>&^7l(&CWMy zJ&$m^S at G;y=!lfGbY^ZY4FN41TQr~y*`E1(_ZT3K_^j`zb-h6y9vohjD(6~o1EQIT z+mgc#fr*6$n%%p6!oTNF)|M1ZLcVft>*3JV z&7t=;V$5Qal9LxhstsIR^3L8I at QM9$VzBL3MPN*5_4LDV^a<%YYl$Tluc`Lby8eQz zT|GU-qd$y0YeOj#9zSlMx~1P`n6K|YFy+EdO(Mxk1?vH1Nmf=CV-WH_DXC?*(U1@# z&`wM7KIhLrFhCKbzPF&ibwudzSspgay>s_2 z;9c$Bukih))fcCbCX}^X=Hq?uvy10SJr?}uqW|%EEDZeX-^WyA_Fn3lbeKL9O2OpK zTv1iU0V0Forq9Z^C9z-Q4N*Ae1$N;zpZ`G_HfrNaZ at T|+F`xzy+ia|DY%Kr${)rcT zz27tmc+4HHx`v`oY4FjnuC8*)$sN>I+~70y`I=~NN;*G1U9%9l z{^3u_X4_7?I?Q-Ic`9O7b z_0vrt5#FP|E-tj56*p=(H#Rr5N&f*nFgfNhR~1M2IfPbXlZA5M1D%&E$Ip2xbdQYe zlylF5&ZP;w-S2E`8{t&}4?orfYW2&NG}GKEZZUJqP8B|SZWMQ%6`b_+<2Bkc(zcSU zg at uJ$-)H;3eEB!H58~05p+ZGKN5E%m36{3GJED`IuCOWoui5@!oF3BDL_$nVjHk0W zK%%u(OPl7p9?$?h%{MeO?ELhpzAjxb at pgE$|BV|PJ8iSZM+F6MJA*w&L_`p{6!8kC z94j=;sU-Lg7#bEH4$(~vur10p-|YvzE3I9;|OpcP%+-2Qh`4+x>lRkIhlbH__J?@f5{tv*eV8;iwmH|4mw$HLyk z&`AvG=X at qxnaggLlr0vQqVX^A_|$7S%mHghKkoUM*NfVtz+d2j%o0~^1 zW}~Ai$#n;9YzkO_xgp8R^I9;4SA}4lZT6l6<9Ha=L7O?AE8%rPa|0z4WmAt;0|FGc zx2BYMPn_7eAD9Nw`b@>e0`rgGzYFA6Do9C%jg47=B*3f+H~y5iMhYe|z%lz}2%3G`sR&e5Rei3~PSH{rIW#gpBnz!IFd&T> zWQ73ID<4~Cy6*dy=QuYv_x at PXM{y_KFcm)^;e;KynKc#H*4Bo1h9YKFvmc-Jo`_Iz z9+o%lObS)Kd-pE)gO*pBpB%-cr1k-97a3B$GwlfeEKE99*|EV~Gca&aC4-hQ=q3rb zez at G!Vv)YEy1RQJiPG5M(=J=%+w3wsmH8*H*xG&%8K1_argP^)fv7AV<|>G+8>=pJ zn_9#c;uCR7jnB?L!0aEnh06VS6a~8_K>1 at 2eA4d#BSX>p;+Lj~i?Snb=jMu<)z8TH z&an;KtQjPT6z54xNYKOP#cS~4p|lo%uol0ngF|+cqFkA8+bX8;rfsht2Fi&wX%JvK zZ6$3T9Yf)Tu+9CGCp=h)nVx<#;JkvZzdsv0JNw;zr3MyDfX;Zg0F)w%@u-TSA*;l( zV+r@}sWtr5oNXbMvQZXw{>4P-FN64s7cat at 5?w`ZvokOdfygT at EjMEi*k z_rzIaMD{o_4tp+)&J8vU{IPGJDM{Vf*eDAr?*C>i^V8Rif|BylxDwUw4GnQt4>>F| z at Z|Yv*D8J`Wj>sHHkx2YMAbWQ&hzrRowe#ANLgr7Bhxg z+2()0y8b1tssaq{NsC2t;3L0&d2-ZaCcwtZR7RIA0fB278z^a!fl8khdHEk at s+nEp z3&ZdFN}M^V*=30e`lAO2jj^LSo@?ppF)j?t8(k|h#C|0xBRT at N-L^(al+vm9l=Ok7 z!2Xa=pFTC;+5VGepx@$DVzo9uwEiei%xehAEk{)JvJM#LFp`H7!b8BH at Ep5+ zroxzVW at l#HeAwlR-9^aj3905!1%F at ox4=Q_ATqAb&g#H#@D?r`%U1&0t8ewS9~8RD z#TccqMb_+KQ_%0SwSFD at me)U4 at JrXr!igw3MkSUwCrDWM>5i%m_E7-@!dAxXjnY+P z(_`@iPhY<#$MnYSrMN)s<%Yflm{|;hdZt zy_o2&&J*6 z;3MPb&A6D=BhqlmfLoAJz+y_xfjv at J@ara7lE|aI!_wvXmh$_8gP6vzGcq%Kw~4LA z5LHdDQDEHKlwa)x{swH at +nWzoUp`3X%cT&NFKE4GsrrX@=I7_h*z^7oJOFGJXuL2Z zMc_FtEz0*g{0SQC4H8o)J#}^Oktp at 7S{C$oe*Adr#}7N;;y~RQnM91MK{5FI`^P6I z-v*Rf7W4GlZuF|~D%0k(P-NxTk%&T0xHvPy@*FaA%>@G9J8K-zli+?v!2XhI5&XoAnPkMIQ zN+N?8S at l(@w-iU>@^hxw3K%FYDbZsNer~r$2s&o^jj{*d#tsViwJ|;_^ZL$6N+fSv zSI&0+bE**+ZRf3x0b6(Xdn71#-*?nP at by#?7_ccQt2_-fbf at BWWF$80rXtR}>L<<# zSaRufZtn^~v75%d0~-_+6cediI8g-yIzU=vWTg9}C1Mc!!$;rz&8Gx`vXy`FRfu|K z(D at g^D_>oMP7>tnQ at +wA2Za))+X(o;N3SfHsKQ07)0BM;u at XxeRaN(j`d8oPf;frs z0A6_OYFpXfce;W3$eA?^2*B6pM^k908Kne!P_RQrdYsFgBRF3f!tC1SY~FX zNR6W>B}UYxrR#WRedGvPQc{x0P)Ya4kF*R7lhb4SC9bHet9zeDFrII2yKVII*RSPX z%;x96iyutn!Ag+;M2L9JXIqb=;*)NA{MeQsD3bLdAt4F0Y2Ng;a26!cMwC`!_{a|u zCFcK_=?0H}FXOX|k$~C)4CDdtv$80_Bf{zMriMO}mXlNI8VT}a&~@c5{_ND at d%@*i zzIrv5F}pI=QD-a3BPK?R4Zp!S-xd at kBqoLexg{bY@$>C-``FjVj2GHC0cE7>2Evf8 zAl`P)m(PeWFfnzMohpD=6n(!X5wM!cIfg@#W^&)3&low<+0_*^K5iKj5;D3tRwrHb z5_3avdf4tfGE-Ai9JsK!+O4fB@?fNq?(RU_m`nPbH`FB5Y$pv2Sg_4Ltf%$Wt5y?@ zF#<=Ao-#G%@F+QKoy^R_k|bF_H=Jse)l=&rwbGmahP!>J><~Q}u-NsZ1K-yVf at F~w ze%3v`{%!ClUUud7W|owAsdx`}bkOAI=lky`B?1T7x(=SavuPf;myl3?^%@y>Nb7Mx zR6Nhk%BpLKV#0D;JTanf6&Dw;N7-Sow;C6 at UQW7&6+^7CiCVY-yid?OA=sVjBJF&U`UxiR306_?H{CsTyM9bL+BBw4~p!<);^)kIU64ij`5FDd6q4zPP zw8(H{sS$O0LP8KWz~klJ3+}*>%NEsfRxQT_jAffaSA;PlYbOSrqfcbG|gCxSASe z%=|?54GV-fLU-?C5(NCv$G6!n`Ga-si?o2-CKa9*fUjb#F*wAv?I*2hZzEJu at _I`6{A-%^A>45*v z=E&*u=i41w#gjBacVM&PpuHq`jR4YJnTrbDk9w^D%9)hkDTtS&(9VyqFwAM}?2K=* z2NKOqAL*T*9GY{AC!uAcuTLKs)9=7Lv?|So;*yf}s(lXjwzfALER|hc#E>U~`yM)b zpG!f39mr$(v|D|dt&2;%=y0#ChR(?s-OBJMxQ<=Xa&Jgj*mCHmL=q)W0;xn& z>oeO}UBPEA^OTb=GM&T2?NJ=aKX3-Fk=*>Gky~2&!JDP?Zs47cj*hkt4r*?0$9~Mrbai&# zynmlDIxY_QLK;cQv17+J`0suIYN7I+s-T}TRAo<*VGJVUuGDk)@=|?v5tI5~bT3V9 zZf^35h)^#rEmbNj`}@nA at z%AIyb_Q|%3-4>S!kZ~m+SvLGNNSRR&}1(*rX*OXYtq7 zxfd!(6PkZs&`6?)Mu`9w$9#SeQyo#q|Ve#o+01wBh zkXyfd->ZX|y?y)k;%q_3lfuG#jLfYNlaKBWC0!VayM3FSpc*e_E?&F^>JUVTk9SB& zyy)%F2L`1P9^UT5`lqJ>l(%IWB`x>xX6Q6?%7vGKjXKY)133VaA;}tZwlkUdZNK6> zQ<|wG-{RwG0Uz*7jikRQ4Zl at Y}Z%I9 at b6n^N-`J>V)%Y@~5~@%*{(=%x6MI?Md@{CqmBcgoFe z0>}k3CU|y{j35F+$WSpDalZGGrLbY)_u`HPI*CgVkjg?z5_bt`d3pKz##8mov5vKs zl?i31GGpp87!d3)oK9i`F&yGw3!(vjS~$er55XV&*6nZtq?LA zn$zy?l0<}iEG;ehhv09~)`R=^t19_{S$|{rzhT=&j$?j z^bi*rwq at PfM@)R~yYC!FJv9~8&&9*ZCV&u#(yw0&?zZq~f28c^Cs$cni5e+3(}klE)+^L$K_jjz*~-P}Cd@)TWIUQEPk}r-hkmKDx7*RMi`)`#f2M}gU{|e=zpu-8Q$2#PL|D(<&F7@@@Kfs7tIpZFAym``>^TY9f_&wpoSVPk9E zt2xhJ-Vcz%@19m{m;(xazsiMniXnH|)Rwj7wKXrY%Ta1P0ZQ;ZAi5Va(;2u!;*(l! z3Nl{3B732o)`%_-G!v8@!AVzFiTlC^wRL at 9%es>DJKZxsbzbRiJ*@o`;N>N)e){yr z3iy)G)UnURM->%0;Dhhp>JweKaDih>ZE|-jz<)t9V95}6C+33Jl3z+SV^(ehJ&H?7qIC3iZGJtWxuTEdTE{^yNBrl?2{K#*j zNj(;3=FuQW#^EaWX?oFdMdxAp2cw?q>iaHUy7cPO5pL;f%Y3beBe1+cB*$kw_wC!a zzH*9;tTOU{xd1#}A&Z`tmOS6uQb at qA^YX-K0$5d^r-2Wh**kdS#*OcDbEnMA(pnmS zm7S))6&&niE+-}R`B3#-K$^nyA~#IC5&VCQ*hYexQvT*pSJ&7_AYx1paiTLH-=b#a z?2}kyk<%(Fgv6RJPQo4t3JGBiFsw?(^XL1ltgO)QaT4<1q^>bcw4~MJ?hdXouLg0P zXWuJ=$x6V08yXt=iX9JPBOZVQZJO&}PPmK`VBbkRp`B_^Wr$AZ|MKMv*6HKK)oyL2 zKeef;tOpO~-`MoTafBHe8SB9AVh*SI-w7ybRq?D~$0j(#@BH%p)|_C6R0J5t;RPFq zyhdVR&3`bcH?uwOvCHV5_O=~3(Tdd=x`G*9w&wZz>-G?-=@=M at L`6mQbMzjz*;HvX z{yj6ptbAR;P%VB{2zfNd1&hbogXvUAlXCEyj~P3T$oTyd4hs*bqoWJj+VaP%SCBto zFewEY7J4Vcj(T3s>hY(OH==cxO4(Qe{R4z1K0clxQmr;lv at d_(f!B|WKqn(DNKR(` z at 2U)9cK!OgOo$5if58r!Ccrw7+r{PO<<&g0GK+KP&UsJ7NH3PW|3d{V6lu%g at mwb z6tGD`Lc-g&;^oVT*jF|J;dMdn?d at bHC4aAIW2>uAqU*@X4Lm!^aA4ukg6H@ zc6N4=b4;>wa$UW>O=$p?*gOiXhYKh-Jp4nYKt*i&A0L0R$une-AiPeTyAsnsf1lYq zjK`%5`$66#Z6hLt7zm_0&Y)x-zK3rNcTRzEeo2p)f(Yoy at bNJZiaT1B=ZAx9ZpOw^ z7nYQ)%=gX~gQCMisWRYbn1UXW8XsRR`VpR?^XUS^v6MC$ulYx3ukyyIV|IbC!P}7V z{FUY9_&ayTr^oKhFmrG;p60v}k}Bu*q_UC?gcpv}!XyBP{Uqfo95b4&8_XrDcMh9gy-Kkz7|o)5>-mx1eboH!@4Qg}2IBF{ zmjajy&BaA9JUpD&?;w_>1wukCFigi;HmxI|Y-7T0npz)^$%#P4ON-egIzfF~_{)SD>}s-4h=-41ogY=vA

*#>Au+t`zNNxr7>vyjW~zdvWmAR^SJNr`81w6-QnO--$|*h}GDaKig at Bq~4GvPa=1L#l&?kAvbx zZCqSvecuf~6Y_F)7A-V9O5-{6)Au`kxyxdjapf>D&qIZ6DuaYU1n>pSY+?U@*Y<}G zh0sb2{6CEMF{oMK1Z|>E+}+(5b&6L`9dLGb*5IS(DEFMd*;bsM&P5ZLT$WQ(!i13! z$I>rPo~f$V7ED|wF2u>kKz;w1v=IQS$9yF`J6>wytaB={>VAF>hv#B{nZ-pHL{qr=``*2KyW at +aM60}8hI_3g#ufs1t$-2kb z#HCjD(!XC_;j`gkHE7>+!i;9p#y0u-#eIL!aHA><=SJb%WNwL{y7;#C9&lDzsmkw~Cr{oC zSE5UrZ)|G1aOqO8Ra0!nlP83jNxHZwR&Oc3yt0yS*+34w>H^(4iJ4D5J!8G^w~u?y z50V9)y!qm>FL|88FJ_N6e$3kksKC}}yVvgFt$<`4Bb&o(i@=A*u|U;1U?nBx$7_Y8 z- at kovfGbX71i%?}k;(Jla2>uPcldBP`HwaU_7X9^y)rT+r~lt%Cqd zy0Hb^32$lOjf;z=I2y4MlB~K%^8v*EyJ2}#Z7JapE@=YkU5^6409W%MyJ9cNN<>V| zEiKK2iN5)k#CmUx&-|l#HU0j5M&yl at 2gs`Tw4DpmK09g_>ZPHkSW ziRDS#4ASimQmU_X%R|ZxZW{ePugUCMbV`LFj)meaUn^I_H6W-cDA+$VG_>`Qu+(s{ z;l#~3w&wdSUm5uL(xU3dP6BXIv&&A>{|i4u-Tpb-Ah@X4EKP^bsj2BO-{ZId(D01NF=!s?Krno zN=$f>pRccXdd1XEzSoGw1!KpZ>i4U!>si%kp9QD$a9I-NTP)-=MJq(+Z+$%HZ|Se~ zr0a4FtJSBM`BD6yIu~Zsxt=_UxFwKING>HyG5B7|Kk32S>iKf93YWk)Q509MUY$=m z;r6XHsch=$(@_8ojdSNt+1Uw#N(uvL&vYjbOGuy-78W+Qvm%$bV&E_bA?qQ&)!K1TLkzkXds#S`=j{r>&C-LAxr zAJ35_8yFmXGoZ29hj1VgwzGV`PErs$49tsE|`yeEJ-pNi2=A>``R<`O{UysV&1_6ysb5STqYzNDt+G-u8`&ouH+|vEe{L04OrAwA?>l zoDDXGi5&-}9ICS}=i+=}%#(ie=FRb9t>>~>*4v at 9gLfr(+!P%hEoR+Ji&Mo2=dru4g1ys5ovuWt~%W! zz;XqXOfjA6_U+r=_vU<`LR=LW7l+-dF0$`+nfr2lu-t=!n3&kMFOPe0aB%e7w|!n- zUf$l`E^=}ChL}oTUtcc~IdJI1Agq&shzK^enVp>-{r;Ww^y$+wCr;GV)a;?AriS&{ z?{|RNY6s^^ccdwuHZ){?Ebns*Hg_zvQTwrcP4^4!P_>08Pk6 at sVpxygN=izKuXc2D ziiPNFrZFpWX7!Es8ZB7to6-NrVm)|%WuA@;p+0lz(xtvHUuqj0_en`f4 zbzv5 at A!In+TWNABc6)t-1_xyJm3s(n{rS^C9e!lAEkzpJftg!dhk$7Kw0>3qlr^4F zV6S6f@?o%L-ynP_2k%>2($~ggi0Yudm`Q;?gL8(r)|VcNSrE#4&I)*XN547{sdlTv zYhnCzj!|udB)Ck z8L%Py{=?_Fz*v9({*8^D(-d!X=9($V$jC&n^H>#)_vKsRr1&>x?_;qy@<`y0!j~fZ z!=CzDrw%YNWnSxkj=u&3EU93CHKpGpf&uWITID4AzE?m;pCxu^uvP%C|UH zheHSaSK6d4!|R5zHf(TcNZe=H8ZENutrkx$h?hLO9zkF)HDA84;{5B$4|g{n8O6!_ zu9C`m&Q|}L8=&Llq+w#(9nCS(6c-M@*f%Er{30JcnbS~tD4;WEH(;(+8!UjQN8qR&56n z?2a9q=ptpd(hB`LH8s(kz*|c(rlqAN=Fle=&7r`m;JbPg{*`S$EO6qP+VdJ3wA(v? z%o&`Y>g$OdzZBB7D}3Gm7HsQA*XN)fZM6`H5##V}gVN4ca7YMIUtgbjXS(wDff7-F ze}CN{&q$(s3P(rH(a?bWFiYCfZf}n4xVlPZ>48;a|95ykoCX66_~RzviY0=;r&s$i z`H7b|lhw>-`a-d&aVf#MbLZR_hS>pXYes8=yWU=DVT}uV753;6o7$;U+~60zZ%kCx z)QILs-pAo|t>om|p&=8E!FhPGwNMGY7FbWPLLC0VA0+p;MOSBM=gq{#M3<4;h6duf zfs*i|qT}z??u#G4sH4Nh=lo!BMN?B#aD`{$W8~nzefw;O%8x?`3T0w`uywDp`QfG) zvsJd|bu_si=SNRX&dQug5pUEtxto?2S6V8AHncWBwBKppV1<{qHy4D55hnE-5d~JM z!AFxvSADTmW*Lm|M0iF+HE&SN8b;IU?iW_S zX8Ub>UjLKRSW|xhnC|#71rmE+s^h4>|R;nIi~`V;rH4?T%6)>Qh?0dq6^P}J+<(eKG;c3O{F5} z?CMg6$M1Z8?x4PbLCwGalA{%Fk32nd*D&%y{trx61;(>Jl@=KP=n>`Bt5+#G71?;z zqenw@^+X$*nylsr%Sb6HaTfT}M4S at Z#BAQfWc at 1t69IpIUY7U!Majy_iq?md2-Y_? zGCXEJW62=6g`H!+s9BYoxj7fceuQ5kAqpoaCuUi90l-B{CZ-35QK7HGq at 9P+KWl9V z=;-J)G&BeZ`uh7bz}a!A%mWdV(b?HnQDti_BG_{DOQ(^lnD5_hG1`|n4XFTZ0Q8yL z+D3rFegeL57+Sp9c?L>!m;c(FT6<#m5;m5?+4AU_=79A at EF4eI$%#P(?&_ig1csok z3Nd(WbW|rxI at VzBEWFL%wlR23ARoBR1~Kt0};|>M2p$}O#vDqSd9lE zQ2ffj46EO}f=6nN_ksi*9UBV*6=B&F%f6L&nO(yEvoIEq2?~+}vJ_QqMMqOQ43^T( ze9oyWxY{A+zwUyq#8TzF-u%>m=&h-KXt2nhiE!`UjNII~e5)qQ->cJGIucpkmR45R zx3_$-<^XFj?jF|07CDzMU&aYg92^`jUS8obF)@7^Z&_8;a561`$>R4eUcN(z5;y)p zoSppkjgUa{_;Cv`xoCgG0ESNx*;h1(V5!vbz~e^`rmB$w;nPZyqJr0dlhE}%Dk^FT z0Pq!AA2ujIckaM*$KlGdcz2WSlF(4%_w}K#vLU8h0QMkLd6JnqHa>pqLZ-UKWOD*A z2qroCMl}eAQDy-f#GtbgVBlRz%*@R4>n!b_2kvYm+50s=B;+ZO9qR*|(Ba9?msnU>N?pI+UUd8U;~#4qo7(1P zQdo=cgJs8X2=4T>0dKEMw0J*W?m#du^_aOIU&+qSel)t;-o|DhtX^qZSxs9T)%^VY zqwEj*>~|hN7U6SvulI7 zNQI4h`1$v(EsUJj(u!Ubdjgthc5V(>e;k;0?eH)MkW(}q(4LEQBhGewYo5>iSXjYWm3ia0#ABsEQ~v96;^O&Q7uNtnL$5)g65?}u5dHE?xraS73rl1vKP=Jj2vYC}tnhRB z^V8TRf4emb&YINg(V`3imR*=;maBnF at X2(}k8>X$RkA8d{E_35)@{i~k?7FYvB z(q(@;#dg>VQ3)tNW`2|>6zHJwe|F8b5gCvcUdOA8c8)FVm1a40S zin)E$%CEEV=mhrPm187bXxBrotgQTGn3IPGfONN1&vlX%thlT6x)mj}7(p-zjXG$( zvK-?$8LL6af3!O(eWMyS{=^uDHe{}H;_ at MZ*+kZB> zcnWR1R5ZSboEdcLdJ@{leXQ|bbj0H7bT`Nu_*~wLqkDi3$HV{AvUhT#2AKfF7EuZe z#>Lh37I47``)X#Z*D-esL6!te at XZ<4p7{doPd60UG%(C3e`h?TNIUa@{M1fUBql(O z{FliysW%k^pMUo3(BYBK$1s6}h6YP8>Dbt!^YX+PfFJ|zejlp9z66UkB%F&W$7b?i zImd8lY=&w$Hfie0JZ;q&&9WtS^(Yq)(j}0(m-HdpcHD%pgUJ2?z+lw#fwr1-7Ei<{ovh)d>$D>ht?lOzwanY;JC*eeU at 2>6y?A zrDu1(xgE92R+PIwPK4R1&CME=?6M3y8y$hL%Oqvr5|Wc|14kJ#A>rY5y}eA`&(GBX;9EdI1o;#x2+x4o(IlY+tfs|w6p}E!6?V3cN)TQ=Gg}#I5*96wvXFW<`z&zHn3MD(ZIkn zGBblY6nsfD9o#-r`vHAh#Y|5p#A`CnIC>9G`ME>=_TUP*yJQ1 zpOc)dZ0*O7cg!N%n6uV)ncENP^B>Itl?(T(){GTvX1C^tXBq}d2$Dt&~ z#>S%n^FZA*fqr8Oa|3lsq`;9QWQZ0XKNvvx0 z|K$SUm_ at g#^GJkBoJZ7wCx}@!Qbw~Lzs>nA`xJJ$zeP;p0&#eKeLaGmI!hVnuzq}Y z_BzNf(A~G$sc)9h8027wo??eR1X_>fS=_$020;vLPRRfA3*H|);Gl>BKm-aBjofd% zKGOEn%LEV at dNnIk9r?~9ikOy?kdTm7L~uqH=tRt4{D=SQix)32{|tkHzklVT zoT4I!^Kj)J0%lg$|IQ3lcsxI#yjIoL&Mnr9%QrlwhLarweor4Bd>_cE8cw}CeDZv% zEclC+Ad-B5XEgi#D=mybeRXEzA0``ttcBsprv`B%<~pen%`AQ^P0pNPxJu*u at H;|5uvvrOJTJ~gNvU0AG&CO1~5O5LWY zk+LidD_~Rvy#b3vK!B|mWMoja;lK_&6wKxNb!CW;A)J1Gezx}ZQEVsn8~uNLy>(PpTh}*y zVK-7LE!`jpD6MpNNUMO-D5W%tN=kQkhafFs&`6hrpmcZVH`h7$``q96ykmU)amG06 z#kKd^bN*tkxz|)u!a;S8#Kha^EaR at Ot~=b^;y`fk>&$BiwS>@7)9BUAQVZhs^>t(t z&;U_VK6`w=V#gT}KmQE%a^ABBj?`a#@#t!ZB=ze0`rG(;GFIKnGgq%(Efm46a at aTr zxz~W+?GFuo2lfDUWX<;H#r#TQ#{r*;#=N7q3en(;m*b-7E85%JpL#ajLf1yXr2c1j zZQ>3mXWeX93bHa}M{!xjAXPZ1UPSoGDz0zL_c38m&m%K4Go#$NWp}D~liBxM2I~}* z=!I-F3LO3hRn?G8z^npGQ>^X7v-1iF0FYfc|!iH4uTKT2`fn1-mjfm=3QV%A7y&5=$CDgreFz5DP1%>+SC*3yzS zn#=4Q5G%#Y;oraG^p86p34}yNX at b}QEj>`6-w1qy4x4~G1rZkp%=+t7(yu3X8yl|8FMxIALyhb}a(~ggX7Ev-K at nKAr)?}2UQ zWAA^%ju^m$b$ADs5mog-T+1!KV5Dbc{0`xRrVhegCmS^SqgE3TzV$P{j`Vl$;-T#Z z2s4yG2#*q6wh6&={lu0j)VfJT0j|~h?LrOidHHTFN z_y$T;ndEvR>&(N$gT at aOx*j=}fBAymFo8QH;A3u36 at T~;D_t%I)!xU^fH82Q?K9Zv z_=AIk#v|qIrRIZ_{I6Rd$sMfzzzzxyPPS%q%##IWjZWbNcTd7+kIuh9 at 5*N?r64=R zX)&a_cslF-rxMZ%Ef1p%ldyskeC2CWw5ux!uFz~XS#cOfV5A8-G1%p`2EL^)5w5XTaQoP zQpdvi3wjgH7f0fSjO-c<3kzgXB3PYU_g~yLx3H+c7SfljQJ{ym-BDvY2*1u84L0ZX zW;#?L8la$zflxq at 4$K<~<_XA849E)fGv5mchcH(uVTfgKwa at scb{k5 z{>*)L9S1$;60-qHdU`MY&2Oe^j*h&~I^sWq!b?IkkJlQ>LCRr+0n6=|q{an$Qv++0 z^5SkoU3gd+jh~mx$+1gaW8(x+6ROaFZN^|wZ(DIOzWDdjk`e+?lf}jF09cyLzD3Ua z1Ozlf!lNyW!~KN<5am8v)aLB!+Sbn)h`}V4%K#{fdEyfSk#{hlX_~K0J-s>Zkz37NpoXYJIbrzJiN~_XM5TX=O!W z5`)K1m@^j7 at 1P8*0>HzjSM}9trP?!J_e(O^93(pHfV{gF!{=d1Y2)Qee(TFi| zaNeLwAS^2eqphutCOoTT$p16q(RTJLRb8PpsyCv8^boSVeY-l at j29Ul{S+a~a2XqT z{L?^!g+B~S7C}{^*K+NbhR#}8SfFYY;M at lf4-a4%3{u`jVmzz8XM{`C24bxXr#=$xElvjO?Vz(=GTeH!`{ckWyVb$EjhU%dmhDuTxl1#3N2 znqp&P^YU=_C2FvM#P9$U)DbVh4x$DP(ECWlh07?SA)p`=6gtchR8>kS5XG7DziKf;YZ(=P^dB)SSUS(vKY{tSWM9q{0U6 z`JoPIcnm*AMo1u`>H{f6{>;pr*VWZ66fyE+k9zUHs-cmb at 87@o^7E at hEpxcIZ_#N} ze0;=hG50(HoOtc#si~iBv{9?aB_~tH3c3)Xh7fcT at pKz8D(sv-cLk|ZP)RLR*}WKgXT| zuKB|AGR??VDkq1v0}TCVSF_d6Tzh at Ez5N>8)lCPMTLvWzaEpb7h2>5=tp6D^2!ayz zR#66_ldPtvr-kF57#L9X5~{1KH#Ik-Y69q*>$h$-x#0L!djxO40O5}Qm+h~iEPC4? ziC0+mnKKsZR4A+o_w{S{`2C{%6$>y3g0jqZN6 zGJ1>46kpu$*0&`R^}Ntfe4X+a4<$!xK&WC+1s*6!4CLb9rKOKMqfQEwl7t3dhedw^ z4uG)&ZV&;|g6H)P?bQAH^aO4A at yRwsHi6W`Atdxhx)ZnzC<4(QES2X)JX-L>DvM|z zQTl(^jLuapNdmA6OG;iqht2 at QQ3C0+XcfB`6cnufAnlM?&WSLf*>&7jlF#N5}q&?1|5wp z%#mJQEwH$_h=c}63{LZ3ij!QA{TLXjs7|x9vqQk-uiBsub6VQBUg)G?0G|Ks>w;)` zk3PJ#^u*9mbjNKq(h|ym zR%(A|h0Xqc?zNVtrU#(Y*8m5fHU(0k;&&3dx1^*but0cx_-QTe?F|6t at 84OFl9Hls zP?Sq(J6ujqu296#^v_gl1ZqY=j}G8 at 96-CXw^!8j8fhmmE80gJ9*)r`X^8Y`YHj_2 z=AN$pd59eU+}zv?d;5mYBoX#!KW`OV46CE?V7$%4L-^&@N&}Gobz2(Jw86Z z1B=VY4^qVaf7N}|-O^H1{~$&Izb&62_z`tsfUrhhBrtRd9KL;|Y#Z7h_|Mz)zzToC z1hX47JYM`&h^nZ3{Irw+=cs97u*k^E+xr_J&Fa>GehR1ruq at ek9)HE4cWQw-Acu2wQ?oI at B6`QC4*OcTYCZgl{e*_2} zd!g;;-<5X2b_KtluybwI-_(B$W){|a!qes!9;haeOGd$^E5E;Z9_*}4!0H3PV%*V@ zRomO!1mb^63k(0=EW49_%IgA}fc9!dFUoKcV8zhT5Q8D&G{*c-(M(CQskEr*E$9=S zR&5Yi(?6p;(4_L+-QGGzc6BRsRu`%-0CWEtAE&gx at 0S$|a{EV54<4#f0l?OQDp5Lr zjpS=2yFp-ld^=m~<76*?e^ir2r;Pxg75>P=DbTAHe((T;lp2^9R852 at wz{+9SyEE+ z>n}T3aC|)K;k+z`=BsyhcMF4HM^h-#E(^#5YyBCHhR}k{%nJzC!AtmhoX4hwNML1U z{eD*wZS?SoHbh=RD){*Lz-n={nm8&d>K2FL1z64Zz*DP_Y5ds3zWpy{k|P8L_r>4u zXHlOrLLQzTJ^-NKELk&#sZ zu_xWBQjnInm?S8J{y-=wOZ<_Smp_N|5Y&2jcsPoNk`iRFii?Zu^sY9{<3k|t?!Nv{ zso}rW6NEO=(D1yWQVVTb!ePJ>06=BwDU1g#RzcML8`;9wF$UZ0+c1L=&38 zcOj1iR{vYB=I1*u`?d&N7gvFGNa#$!6(6ND*kf`nJ3BiwdzwFtx$KaFO0K&mk`2!g zb=6-b;(Xw7?&=@lKM~Js&0GP&M|4XP(ExGiz`>Ra35C2=G&E=7k=7+X^g#KJLJ7D4 zAuecIRAqvw{@cn{D++mHn%7QEdAQLbgyL1J*cb~W(k)`*ANBQT!Mn2O>r^N|+pknk zh$v_+E-88P?AeDPDhbpzv$#5m!UtK2+0LZXv9YlzBG9Dda>p%3q}ga>!s%HqmSu0e z$(`|frCVCm7sjC*HZr1v&h7v~ft)HDmkxr%0L*=CV%{l31`qH`bh&wX?8cpmj5A6R z{&I=O at hQ?d=5eSo44u?BUhhjOn#TX^*|XiL2z_+oFq&@$vJbLByw>&TItj at YJBQ~w zy8P#IqF2hsTrXd}dezv}^l7~hfpsH=h1T7>chOeg?c1n7)dT$g@@VyeB@=}Nb4g)A zXjW;frks*`uIRhL&(D8AT;S2khdNZumoB}o)hJ-y=};?M;o at SlpU6=@i3K|jPLUk| z7;V|0V}|B>GJ+x_y&xk;svPi811v0mSCNs7sF)b4I3gbbw};Ml19(EkZhhB18gwKC zD&xbapB4}?W#n&`p$!&Lz3)m&xHdO8AH%=DdGqGaIKG=`CV0`$=xlNuYiraC8^`zR z>9 at CMZwWfz2Y&#hdxnmV&X0)el6I*%63fmmF6f9VG%*x at UT-fFqO%%>j(1O^I&4Bh zLN{(P)*pz=58mDvYxbsN7&9iU)9$kmU&waU^Z)p<9>TS&!say=F#$0M25VDj9r>fVp*Yvp1yB6&_TX(FM~ zcdT3O$b~Qte131v84ERLGn&w~DiZjQF1 zK#8kH>UAwRvauBqndxT}0fC`mj at aaaqAyEcnkynwn35^mc(fgTT?-H%Zeh4Gs79;-l^n z(0X;(6gaaT{Z$$&q=E#Wn}q(3&-Moc2Mn=xuu?k#!Gey%AR!@H{edm?_22 at 9RRLJ`zUTv-919zmEo0;Cx=vy88MgXJ2d(kcq_RsnQ?x7 z?M#!Kz#~G3VNAAdZDb#5wz`|-#(4c@gnU_W`lh!8k9kbAXrV{DfhLfu#(2i zJNqj?BT;M10e3PvGm{ZIm5C-b^Mab+n(gwy1b+T}*J89P%G$=J{#u^2-P)~C?y>LR zg`;`x7_n|!SXk`Cb0+XfsWb@*326qc3i{~*ct2J1vxcfM2hvfdUt;_4jp~U=NMyk_ zSr0wV zeXk at YCRTy}G1;8$+Dte(^n}Er=jDxF-`J?XR;!nt9oui~=36u$k(o))!OotwwZE-9 zqq}|tx8M2p>}6vR=Y|#*a?#PzXWVV=?3xD$qs0A5q)>0)(r9%IK!!XI50A9YWi&L_ z3((BCHJqhSzI96aWecOmy7JUU5l5+u+Cf)c-8)JuDx-J}Yio|d;o(*t*3`JMm5~Y& zA|fJuTwKbXO4SlqyCBBgm~1j~wXg;8k3?NyX2(qQd;-fzbu|xmASVpX&1GdGSk>sS z*`;M0eU#)GkYv-Vx=lxyAw9Fvc5keSlU!_mv!z7~z62`~fQd08EiX at wmzOs*D2T#2 zJU*WFP?yk7&7%L6U3D%FLx$cL>#0kG#Kamz0fmL^u!to;U*T8h>jM%+MMt-g8t4W^ zMM=RUrqS2e at 9yoT27=ii_hkbJ(guCr8!1Ye1p2ZE>{)Y9PY7rsNezt%$DL(45asFR z<@XCkoS&=3;c;~8#Acds08@}w$rUayxKBUI=?i7a%zwP4U($d<>bhqu~u;b)9CYr;@N4PaS z!%y{IUS)}zSuo=O4ws9*bo?=!*gmmovj6L&eJ&RLnpp7mk`fZ0w6c*804tUF9z)ROfGnYRJ{)FFN>>@~O`k zDi4n3D65gusE^^15Bp`YZRPIuUH>XIG(SHPAVf<=Mb$<2Ec+)28nh6YPV_o at z3&vydxy&4Q?j&{y9>{jB5~|4T?d{=1v;%b|!E)eXOuLfL z$|VTK`I8A`TW{6<`R4KdS^=n4MI9ZIshJrSpriV_x=t|qSr8OPH7XkO8|w?`43 at 60 zF33p+aw4(lRo5 at Qbnz*dMvpR8&-O z+RiGTdhp at Hhn3Ac{>MT^Pg?qx z7AgbftvOj+nb=1*-C%NYF-zTv!$ga`S8%W^3 at 92(ec5` zv)SDia_Oh#@TWF)32k!Eu3Sk)pKfnYpJ)uYykq?1$B&%20FWF; zMoTNTC$s_r)zW1S&1;J5*4;pZ7ZXtEU=%@tgeq(FJ#*0*XeA1=o-YCUj)$2aDq(v6 z{=Jf=CF|7mw6d`=z1>{*drV+Nghtc~y`!$%>Ys`yoiA?=^83A zn}Y;GU^4>N*cf<{({a=I?|YqV*RE}XhgX!8^lH)dXcZnRd1Y>%`=X9>4p z<;;D`sn{4s6}5!VoGs=G3^LBn{4NJeWrok5i9_m7PQ19k at _%{(CMFPJ;@jziFo?r+ zLbA0U9<_uq3D>#vyoeYw2 at tCq>F;H`Q7xI-p|t-A{aW8oRoW3 zqZgY~YkJU`h6+yu1?@NR3u0ue3!}jwK^M)Fip=o-{~9tD>SJN^o2IyAuHn zJUp6+j!j;(oW1LXEPrLqqZRm%prJ zg4jvT$dG{8RrAGR>)pG>EHtK3pr=%3`TGI}Ng81hkudI+c`%zFfH;gno2(Se8r3CB z_i`<-hZd4aNlB#w{XK7w&FY)-d8j+VT0q;MQnT3+wlmvB)eUR{D&KKq`eU!jyupc% ztE=Ge@|Paqc4bhPj`3tu(B$GG^T_Q!AU-B0CZao!F$i3W?H1H8VPhBA5_9bRiz4-3 zzq(_*l%I#H>*&Z{ym--evI%D&9Gvmt?%L%1ytJdEqp)UaVPR5|h-Wv*ig#NYo}y at C z0<2TAzT1&n`do#Mw?xpE#?*$jLyKiZs9$1sK>=IOCKeXf8TV*T6JZ7h1`f82%{>8- zYDg- at +_O^WT)Z?d{Y8bSa6m53KL8iP0np;{e;EXVPV18wv2Ft#TuNZD)^!6YJ(b9@ z6Raj?Y at E5uVSD=YX}AGg-5O_zgGZ#d0Z^%_-_`7nU$%bn;`;JSpttB!t19Xo&1H^XumY>taYAzej`gZ&HY%TG)X7v!AqAfBPFr<*OMHfa8zU at qBTRZ zJk==a(nhD1Y6&qhvF~FMEZX821MSgs^dJ!Ywur8Wp)T$z$ z9itvMl+7g-w%`?A#U at 7WcA{aFy1;^(le z^uKFqt4=-2#cm8q9`NW3vLo+rJgCq13n?~x9#6Bul<-4oaEwU>M1uXlB7yE7k+8}w zrA-;s=|jNO)YkUV at 8d@)ZS5GXV&jKY;(nmj9zPjsah2}C#uz<&*8MZ;ZWaiT)U-4j zXVFY-?~VL70Gm5={PZl{gp_-c?dw)%i?!KKsQ)%ionj;ELzw=YzqAqqyFj at 5{L~ zoR__oySM at N{5pmMGJbzLt*VOObg+m9E3qd-L8Ed^dh|t^^^|m0LIkrugcevf6IfRe zm`Zbpp;?nl(o;JOI9<&lbQj%!UKH}-bXL!O^blhVkJs8IfZgsoIk`fPdR{UxQAlYi zH+;V`g2=Kl*AS;vl|p!!A26LALMO}vJJR!~y+wrqd6Dew?&<!GPcLbt at 0HTt_;_wF zJvH@$RS@^Rdl^7TO(agj!l$kha-d;jNW7)X$4N&;MOo|rLsSqCa~mB(8vDoLTsOo@ zb?puo`*JiUA&0LLbEneEMRyh%wR+7iT_6Gf4E}@4v^(`8%cD<;-7)9Z^D}a{Qb54N zCqiOkhzSV^gJ3;ClYI0i;Yse~4k}}AQn^>4UmG9IZNaE9_~$79_ at KIKzgtLSbiCg8 zA_iP|ez!@v+wzGdHLj1hcM=c`QoP2CzX}2N7^&q?C4$F=1t$k96M!b2MzHBmca=<1 zo4H*MT3mW+^!qMFa9%z`MJ8M0BrWMpLDw;oIJ{RBq<-b+ef zKf&3>g at Kkf{d-l8wwoFb5s@@xC^$Q+sPq}@v_}=$=EDr13q*<31lcrh_!fm$-T(UY zr)>Hh$R~NstS7l^pczkE#|i}nMQT=7mLjgYT**M_ at BG~S*jC4pR^{|C!U)cJTj$WG zmKI7(@24js?`sNXU#1)LRXQKM>djP|oR~;?F;b+b(5z)2`a469P>t2BrBWP)CWgRY zF1hUBG^V1ml9A-Oon2+ISsvVc7b*wM at ynDRv_`N=fhPcuM9;$VvAerlQbxuX?hExE zhmqa$*<^~apX;Up7fK-~C%3;;Hs&Q+Pdy#UgfLaJ#rSVit7y>~| zrikaYVx!g at peOUfrtokEfBm|SskC2;UM+Jy+Q(Vl9$Y&**uhiWHWCDuron)%(|F># zE*L=9dNeuT0xGPPI3Sswv547ZVA-z3KePEjS#*8JO)b6h_`^^x6#$ah@$qqLPL9m< z^mOr)A0LQh`tuiWIfA{%N=%oFkv at vgHL9V_C!W%D)m>my>0e8o-Fj?iXmNV==xLua z??Y`um%f&kElS5S8waD%IAS`vxsL4U2<8F8DK#|- at 8ADaXXd5268>a*NV670j43Zk zJ&4Ea0=1WOSalfi;lnvnUfWDr^YHNSGw#XB$?s}ev`hU!TmXrt{4_of*71xxGYbn- zZbI>E_cH8TVVse^zP>r;&OndhdbO at BJC4!^Rg-n)PwRay9T>DlbDePqO`kntg-0uU z8xy7%O3q{bPCS52clwDCDJdyX`SqpWUo=d=CloKGOHw6)=sDxAtfIp7^BxV&dkliI z939ua`#L&0l87%#&7}Z_BOJdeQUZ%&0&8n)Wm_N1_WiD~@kBn$+TNZP1EESq#AnZp zelv^Judv>wby~Zy-t(y5tA%~`%$W;4+17D-uA?ByZD-m^lQzC=itx4E*KbNLFOQT@ z5R`)?qyysa?&(PhqLS$DN|7*fye`Zfrh8r8E{T5WuGo&-Ua6>_9@*aRuBz5DxF5Y| zPSU<&;oFhPtwB at 2f*J=uKhLt6X@^9Ug0D?I*jZuXBo}Kcikr+jZ)h?>pQ97TcCTe* zY(?vw(O`a&;*5bxU%L{Qnt$tw8fWwUeX6U_X39J{(aTGXXEQH(=swH-;0($3H4VY^Snk%>EhxNefPP) z&RC8=b0K}@Qugq3k8 at XpwG_Z#sO=sAHBS0#4{a9p2An;67JRdYkuGfqcTfg%lhe_I z&Fwb_*g}Vj(-ss9+Y?9mL;pd*_@$vE5)xSTAZReENcXsHo zv9V1%6W at GNfNai^2q4>Rrc3Aq=QamE*YxfsWeX-Z64kyj}1N%z5g&PDnZb<7s*PQR{W_NUQirA}CN)b0Y9{c=U0dDFVc&@_kjmx*k zxbm|1wuh~zq@%~J)m$pF^RH4`IQ6SmD1)Lj+HlkWyNZ9~hB!z&Dhx*eu0F>l^GAC>k> zr$$RZPtush=^bk^v9kJOI>F*G#l4zaQtB2$C%x<+?To$OPd&8luF=%kdLDT zvChH4fvXoyVO32h=R$85R!?1^mZJ{P_zBkLz)KvQG at R?Z6&(FFF z4I1hB__Pg3!_w*d}IFZY3-Pfh=D%d4M{u-Hg(=xv#&Q<5%M8vq7aqj#{(2 at WkTs1*Vuy6=msFg+R<>G2 zQ&UC)0s}wY zg)Ukyn$sK83Ag#Kc587kL`PRQ3;4unbVNNkSG}f)&AV}EC^}!ST6%A9&vvSXz_cg* zDpq1a&AyKpYTy#ZWP&iUo$aJ>+?ppKz{l@|hx_isECeabhNJqHgQ8;Ki}lu)7Tn`_ z2!m(Oo}F<=Z+l9YvEu}Vgy3Oiu0OF$E{n?_F0;xIblDFCguku?m==$QyD;AHmvIUUah}V at 1&S!)AOwW!>3DxzVs<;ti6o=#*1ejXQ zgRFc6L~>CaV&!kpqRK4o;(PFtd!)@=cHi9Gq-S7|%HKMg&naL;ud~wDsr9idN1>8a zem<+q$kjoP<4_UTx#ha z)+F4i${Ctz at s}a8;}!H8-F{fRGwPI at l=N_>Jub)I(F9+h9p6GCLQrtJBSAr4ULKX4 zK&|R1G^^N~CM)O?{i{|%%oBl$y>km6UldpYZ56$F^F~-#{kO5MTAqrXo#UQap3uo* z2sn=H+{@Bd0dahg4tGdorlRdA&##X?w zGZwlgE3THH##PO40Y2*}O$q5&AiJV%9*vux$x96Esp;w6;8n83d~c+tq=%HX1+_+(`An5O=I^~i>o at fDkTu`_Qf9h{lmBXE)Um3rHuF$ zD9rp?oNpB7_v5r!o^N$BZxs#)JYMjss}qeDbm28IGYhG%7FZhmobhwEcqO^W#n{*w zRaCPAUocxmX>8l26eXO5rPq)s_~>A(OZ0QQGsfBso)F%%)QNg$Y%=*T8W$yeWR at GO zcvI+svW;~HfmGx|u4gG0&Qo_lU?t_zS;4sGrRuJ>)WnajuEL?18x0K&XA+0X)FJ>{#m)M2ar#RpCAySs7*l at Yoq)og`~Lni zf1{FAJloJpM*4n7Xfn*2t%7%&bgpS|CQD}=&BLqomg zFAbvkUa^WvNVIa1t0gwoo2%Wd{})e}hDxX~#jjRIFKwVVp0E8sZmi@#tmas^9g6x3~n9lGG7_1rMq|U9-~V76#`bBNb!$EVhZv6IGnV0 z?H-x|Oj??4- at d(3R#r|ut|>9?YvIZtS(mi*Clf#w%5l1%GaB2xPx^&$96EIMYBl at M z=hoHLP3~8MSMGf|6YihNKrXhFDB!d``Fhllhps5~tY+&ld}WBg|20zn*LNBp(fl3L zt#*4QRh;I=ngc==2LpNGn1Ar{B-XmhX~(}kUO)zowqZAD2#kv(4P({SiuDKSCUNcB zHBN(uD?`;z_kh>fItXSt#kQ>(%8qZCEY$myKL_C7`19>Fl1^LmeKLoS1u&?Jzq2xS zbA^skgibzI63j*#t8S%Yy$?bCfky75AV7S|UFKfOu&B4D{J}xz^NZYiV_lxt*}sEVg79FjVSErT%gntvH(Y at t481Yaz!bxBbFWNt`EiGaaB{OR8~=08SL%d%r at 6nSNHDO&q#_jinaCP1&ur{F$kbw z9`WrTTMGkO`n9g84FNoV3gnxzt!;7V6ZWh4q@;2nXwdQ^GzQ4%A$L1`2y*2u{~5(@ zF1230YG4M11IDMa;+DhseK#UXm)N`+^(Ts2t%lV_hXiM;#skX)X;M+NBqSv4mYH>@ zUfkQ?m;PN75pFD2Pbuc32v?b>QxWV=j%|fUc=y>AEFoGBmy_c-Sy@>yC^H$>4 at cK7 z7j2%M+KLFHY*3LTPN8h1G&LnmT9g_p+IBOdvvBiZ48?&T&eyAtLBY{5F){Ii@~RGnYmE8RNWBF3bj;~4u{i)A-IXepD6j9PIqj%(xZ z>s}WLO+kdMWwg{57P4arog18;oAb7kHu|WcudkoI!qA2eeZpNe=nJtLt5LGHE_%IQ z$f-b!do?l8Xnfieo80(lf6LI!ET^~OVU}{*1^2qJNl{Y4y}h4I8u`~T*@{VT>y`)! z3IAzim^!}FaH(Zuro&{g$Y`97DuP8jyx}9U#7#E6-c0X7O|>lL_n6L!#sDM7gv_78 zy$pdzM9D2imAO~*4)xeX^=h1@;KLa{1Wc$3Ut%#F4sHN{v`{~A(&XoKKYjbcPDc(a zMf6CNU7t-5Trds>yju&`4U>6;byt5*2Z`X+RBEkCTLvI0<-PT(=Eg>e)pe=p4QeZ% zKs<*YHN8370)y6drUG_H_!k9GtF^XlDzpY~*hYQ$u`Mlt%r;!n0{sJh2wx_`_ zHoIKGF?kxaGcvUmW8~&zqgT*MRF-;_24JU9NO1K^xM;WyrHoc!NtMgaGAY)sk^3XP z-B%hbW4gE&3kfn(QaC~sd<*XcrOMs%AEOQV!Ozc|fBf)>jf=|y2sC{B__ll_613ku z&i4S;p*;c7*_z638jL~)GTU@@}{8vV~4zNXxJcM&fWFlKybVQx+dcq$82 z%H;C0{KJP2@$v9BWSkvznW(G7$GG#dySln?3h)`O$|-1oNW0-y>cu%Qox7H$T&X;@ zU~TWya&N50B?Y3?=RC(pav{C!9RenGKO}Kx*@oYGcwF3FOY~eU(i{8u at uSJk@^6mD z+}!=>e^A`<Gur+H>+xQx?k zl%G+%l$r3>txk8W)3XpS!k+bvCRaNo9 z_`G)~XWFfHIZ&^X!!h8n9!}3JeeJsZC1;(Bl~1hL!f4K>-IsrhLCt!i;VRlDM4Jkb zSj}y1ACr at l@$H?vqz99dlF-KIDOcCd!O?1``nk=a60|k`Tw9= zuH&bwBYn;0?y*E^qdLg_CQzrScLOX8hunj9dF6ctS=rRiPDRtoGqI3wY93>tep0u1? zT4*Rf2RC;*gGzcQNVyAv&Yxw5#EWOAUG&_1?qiFJMny%9XSAUch5Pe$nU1e@=UP%a z@=ELF90HqtMEV at 0+~M4uD)1$zap!GbyZK(W)7GCG*p~FwlYN}FGg@(Q;&^O6U=&-! zSvC&+d8Fjzj?%ir)Ibzr9opVVuS77#q at H`dAW$b;m4QgWkrVaDJ$3En1bYOzww}&x zXfm5=9mQ%!b6zmv|MUWgFbNI#!sQRux(V^zzaI!Vp_Zxm=2=^`7?6vyTMc7K342U< zIIT*`{e$hdk*O>`cgyx8i>&y43=Itf0|KzoKS$DZt~<>dn_1;URlCZ=u+t7{Om at +! zEc=3}&ITzFl#-HCxBWn`m&Yo5h)X(X?ooefWbqi109USDX&M;NX!)Er2)H at hnItmK z9aK`nwKP^+`(WYqeC+wpZp$9$u`>;ugU`6{Z!g7|4XcSpg%o>xA>R&iZgOQMpqi`J*o~$reX82ir?jw^s<-yvp6= z37mm-zyJ8r+|dz$3N-*s<7$-dYl*&{_E3>@zvD~Sm8d3_ezez6 z?|ah(=t4H$KvnmUcq5iTgFPqojpA%Pkwx8Cb%19zQmx} zvw(n&=C44#4PP+DkU~zHLutq{I)F~Q*KmaRII*id< zjf4lGl6gqeC;W65pIY ze{4Mh|HZ{b-?NHW9a9NL%C7tN&v`)AGl%=sLJjZm2??dh$jA&03`9WNsTLVZS-*Ul zl$dxsFETPNEc1}-?%fm+b<#&QBQ1T4!>Sds5g8(q{R?PA(`3FkOEq1V5LJXhM0a+j zNc1^=Dxv0ylTn)BTxdH(iOLeKz4Yxh0tJ=tj5%ETu~;aR>Dr%F7gAejHR+M7OwLRe zvv6{Tnwy(vDkceYT8`W=d1S#>muA)tIHLWr%JZJDKnOes#q6zLerQ{etn|As!-SroQiD< zzG28>CFH#O05jX2CZoK>_PzFtTcvGz%Tn$3m zOa$%u^XIMY>~I5dL<0kh-EI}_XX+)KkGN558(MXQ*8%%W=%a3@~CNgOm9rGS9Inv1q(0Y%X$vC1=TouPx}Z0C=y zbC32YWD-6X8)hkF6(MFUG&qm46!do1tCdmPg?^TbfLDLMVx79Jr_|luy&mCW+MD_C zYe3jdtj-`R3D+UkmDl_x`)}8!6 at p8do;oKMvoks}Kedlm6sP~vMq_qzGsyYqYPOE} zne*pE^72^V^1E06{-Eh!vkBIY#+U2n=H$e$rIm at +5dWey=- at BOw;ocp^sP$LsYZ=Q z$b_nk+Cpqw4R6XSU*qBAR_3p_}#g*8t-FJgt&sR6LKlXGJd{ z5ckx;0FhR$>Q~XLt0JD)q+%DtwEyZP37B)a(@pH zr?<#RW~j>HF4!8WY=fBrunhI5503X2*nB>IOaoX&ZVK!6>C>kjj}IN<8%;nJlHPye zo}7}x0JkaVdMH^_Q!~za|K+%-_sW+Se@|iEe*XOVS+~0lhIlt`N(c%H_Ey at Nfp1P5 zulMbN)t~FlQX#shsjd#IMTb#M7QMK>xtTiCkRX40=u1Y&sNWqMsS+;BvZ_%k3qHKALbgefh5e8JCyGY7=mAap@^2-k{&~dTsoSyL#@UAe7MLDs_N{OFulZ zIh}W3Ag9mA$G5*T=4QjsCLBD?->h|QQmrxUp6*nb<@&z4rt8XK`qeInJe7=VAm(Wr z5he;)Wq1D<6X*7~7C;Q#d(SoKc5);EiigI)z<^OL8^5BWVya^3iEE}4S?Fd3Aj7!) z3cEqWBvD3APEK7pGc)sKw&#@L^v@{Noeu;MzHroCm)vCLYB_A=pxbXlFQZvx+Q)e6 zLA?(_3RsaISU?6Yt}tO?;n at zM6KcxAc5h~rhyCl^JUmgD&O(F6RKc6w at j}PH)plM* zh!XG5h>q5}DIf<0no(-&I^A-D3LD6CRJT4pJ|0&q6Wfyb)0oH)s29y`5#5qs$zq{N zBUAkdXGgXuoC|Gv^<*f7L`2;Dc#B<_|EscU0|A5bJD^$_CXM`5Pi%5!aq)BYAmGru z`yP~wOH1{Q;ptbxrbi~#w`a=aa!mdF{EQp at NtIU$uFWwPm_0%}XJByfadCwU3k!+j z{y|a|=A!+H0HImXdz*iMoR?Nm$S5zrk9Gq=n&4snDPJ|>=w{@o<%nm<$0etxQlrfJ z`t`v};+N}_%{(<7gM(2mp$t?n!(yuFb86CO9chA|t~fAdpRg5_TPL^d8NecrcXZ5+ z7VuBDJ^u%CQ(pZ2zUO%VGczD=w?D`JzY4F3TN(Id?r?7-G%`|Bzt%Mk64;ln7>uA! zwx?f{HUi+2!wm%hOQ-k*8VlFJVtv>BFl>_hx5-}AT2D!@2^NJr1sls~wMvg#=K(RD zaYx;H5CH(Fs2|hzXy2}=q=e>Ai|w#0tsSo*<+E*KQ8 zws4bD-6M)nVO|d|>EPiL>&@tWh<0HGogeXgH-G!~hE=bMe&Z$NIDP!PGwwP%IG&ymzjkuTN^y&W^6uS`!=s}t z>!}u0pakcX*xTF7KSswOM at vJaWNb{23O$&>l9H0wG0Y}t8KH^-ztk6Zj1aAf?UMZ}ct8`LfDp)VAs29 at 9;J1}&6#DRq!b?CIsM>wrQN}lO$Y at K3F3`)<^E}pVH#fI5=X(v-8%GDPcpcWMK>!9o^k+hp^=2rL^SU0E ztHx0bjV}xo^cEUOWVPy04xUTDo at EKp=c#Df=zyJF7 zb~?z>hX*yO)$OgyZ0&jGfz$ELUtX=CmK)UXl+_YlwapG1obT1Lmykv at hibGk-dVLR{{aaq>wkIojEceNMR(No^5AvovY1H~Fs^BnlcIlH?&xQP0!DC68yGyI&DE z5!+&$d?I7!>guXm^?EJgT+OuTkhMs+>)w(En$tQrH;04Hf+56d)?1cJUNVsjw`QTH zp3DdvD>@-0CRRZOy~f})-GuOP at u`-uRvT*pmDt%?W7H!=z@`_AiV+QdgwihenGGlj z2?-H)Mf2Kazg89o$TAr!HnA4^$y2t_3oDAEz}D6lz2n944+N=Sw^*xM{*trLC66;e zi=(+88>W!=4Z`NT3cJ2*KspbmFcwaBTY^3jxG?Bu7fqtJN_T8{sW%N|9b<+ zg{&kiUfC<7%*;@Bc8DT-l)Va(>jL;-ypLu@TBDDUN4(|)3mfirXOA1XC!j!0F+ zU&$$egXK+Ov(rVr-DW(34fCqO*LD|bD*Szg_E2qv_r>+J*EWOZnw1c;- zp-V7#^InXcmt;}7i4SQs8F9#1GbJTuE*wQ9#V8QJ$%*?~vHN(D*`j8#@ zz3lS0|JQ5fF*Y{-chuLSR(NRti)WYSb@=)Dm*+=iqe3`4FZyh)df=J-6d~hl*irQR zK8S}R&;=#B=+O~L$=T*`l7TV{S*TrE>m$Y$*2nNTVN_(~c6F%dSJ#uHCRM0z1-Gh{ zLjNG?DdIR5c=O&p4(zw%@dK~%hNO+BbHY!2>VWw^IQ at _b?$#Kf2hYBPKse{h==4cc z--_qp!6;;WfU@@il>R-B3Y+Y)j~_$lvb1z`cqWDfa&#;>lUIFyZHDA|_{|M)Lru+4 z at Gm#FR4P1Xpx&2Ca{2v**e|-KeFd2f$1U!BQUE=`#}`@eJF80cOR=KPXu3ftPiIK* z?CKj4kyj&3$twyT!dd!I*5XKruL<1tblD=Z>{J8Jd*3nxv>Eschk-XGidZM6=t zo6dkbQ_|^V!q)SEr3n0Yc$7$nQT|uhJnypF*Zl_23t#YUXlOVWm|a~ZI5+mS*6PmC z at 42jZUT1$nDKW3RSh&8Gk(P%0p6p5?mgPP?Zss=&-G6>~EO7MbQSEKdqn|%n?S!uV zsLl;Nsa`$es;nGllHI-Ilc8HZ2k`&Q1t`+MgVTqPNJ|6fXrG6mK at fl0-5uxosmaQg z;Yp#0!}${C?u+lv0p?GV&zu+GTv=W&4%yipDAZ-ffPfX}fAQ!Wa2|ZS58p6M5j2We zUS7s%!eRmJi}ApLjqbH1;D6KRxi4kP4qdo-F%Jr;cH{FNAb-CPy^+?->`QrIlX||y z;QU9Whd?~BGBx-U(qQJoYK5m9V>|D;KQ79Dn*fyVa(nhQ!u7|l(+G%jM;u{j$A zl9*T-B+I+s3c41aS|bK at J6*eW?V1ey1?d^9wR(K(NAXr at okJ|EfRvChKT25x#K%KiDlsc&!t6OACLd+$B{;S3k;`S>FH4elHF#^4b;}tQ=jcClmNhYAFfoH at lLxI z7at$_{J9bu3 zFf$|I^vLPA>^SNe7}nMB at Lf>yqH*Tg-?^cReUM7IZEsT(Ih7_yYTPJjXy9LDy#dN` z(x!SWJr0?1cXx09%OrG4ea8EIQMt!R0}dp?2}lo3E80IL?azwPG7YjibMMCx?gZkj z*no{+g*~|vLKprR-fM4Hzb$A)4E!J_F>z?~cejiF zE(uHIQVi&h-WE7<8Xy6E6(AV7`S?iDzzL+~<%rrlU0&(;GC|9=efq at NocOAdUExwY zRQ?M~lN!q)9R!>o(($yy)5kHxM5vj=UqGQufTy2x@~%7%$ha^!AjGB=k|p3QNrWR} zaiBZR)e>$=NlB>K%-Cp at XROJ6_%IHD6J2v~_76PQ&tJYon%B6ZofuR~dUGuetW~BONR2VPCCMqV0|8?0}`o-RO>$t7b<$^{39}!r!db-uLe{=0|EIoO`%R zts7ZV#cWPkTC#z3jh`QVlCPgBgA-L?32>TLT1H0Rg{e-$(TjYw?jIs7o_KOgOP`*p zNP5UDX5BnA#LTOipng~0&#*}EfSkPiBvgZ9t7kGeAVf2f<1?rU9O!|=j#lSKAATkE z&|jU-4h5b^@4qm4^QD6U+&Qg%0IFGM6iL?pm6-X)WWxd~BHRTkmE0UY9l=3WrI7nWy?%*@Rv zL;r4Z4h at -wyNo@RP*hZ0oO^%RvhLz;AWH3!Y(|ZNemJupBKqsH;K8O&p~RvUtD!Or zW}r~5pwoLwj0JEonzY*>+R4I7iuwlVX+MoOUCfnmpy>$uYzXWA#q)o`m;{L#4Z_pv(3llK&bhAw=2dpB+mvnUrI zpSn?Qi8MS6oa(QeDvFOt1x+4s9Qnq^=a)&m5UJ`jj0M5WB0Jt!c6 zp(rXUIyr>!aq}iEtlhV*bv?q_{_DB9+<k_W-9~|_Ra~0y at A;w9kD=SeYB|m5CA+PB6}f7G*U&aB57iU&Vpd?1^Fy&`P6X6Fs=D%=uf9H zr56`(rKiV&#y4n~yaWLFbG$JQa!%F8#-?F%b>aD6QR at rOaE=&<(}@#gIC$x>X*n9b z;2fsBygVEoOtrS$A$GSHg)W#ss!-*R@@xw*wbk#YF;Iz3I&DWT_cG3iOSF788d%p&HI zz!I?UM38ih at 3Eu2-k)(%YqZ<|3Kk>}&arK(D6nq|{R{DM>EC$j>37)@E`5AY>+4%z zzt)@SNxK8w at WsaOX*yckFfwE+* zEsUcv4oEO!be_mQ9&FeUa3QzN;gEXL`M`7M_+-4sarn2YCXVIKIt_^%$+qwR(05ueBnp?A%-cZ*+_yFg93(Z}-X7 z#rZ{eBH&iZ_Oi6IU)W$|V7LmE%^pgNmX=oYO>Q-Dzd4hE(zC`hJ!TwDK^qaV3erQU9M~bOvMP7EeS=%gQ=JK|F-t zyLV630TgcYT?K#d*}j7~s(GlwMiXS#>({S)fVEb;3&+{={@+=E5A*;H>ToG`&d$X> z at 6Mc_8!k$@6CF(sRDW at K*0?EzSzQ05>4 at v%M9VtxK?nkVU%`0FLy#lL|01ql-BbE5 z+vN!qK=ify{P}aSX~n&s&*CC7cCDoATU%3*Jli=&`%|*A;^DSiKYnD#VcF7dO#FIj zM7+GbxoQsPpfdO4(90)YoC%yt37|xA3f0c$idrJaF?^J{YUlJEKw%O?5=3NYb3&>{ zMeSk6p`%J7!Yv^ae-0JB!ollC`6uw;M%u9}GQg-h;_AeUIs$NHtc&cu0xRWzzun13 zdkDn$xr~f8Xb{`J*jAIrHXuV2|gX*A&!0I4` z*I$~y4%g_zahe2@@&#Zia*x+(5+un21}7lAqG;lT%pkZxn at 3_8Ly1w1ZX-CbT#dm= z$ndPknu1wny~f*NnQx}0QGgzn@*E=!4i4fZ`kESP_)fn5B}o9(h4Euc1wRm2z7JIh zf?)pf<3|t3EJPJRkSX~xGJ31$5K6I-t9#gK99-w&rZM*5&o2&7NR%-_TDM#!pMs|) zX!*1f*S6C0qdX3IX0n3L z2#0*6WM}C=MM+7C7Famf9sqx4b{6Mm|Ih58`WyG#0l5(e zHY+b&)GBJzM6IBpfMn_ zO#&ChzeK-2_#+Q+I{1Jsml_W at x9{2IPk^%Hr0hc7RBs?=Sn+L|g7BE6?LR+M1+)tT zIz}9#cIGDNxCnSKk=L$Ku~Rv?iX1z}jtm$sQNm+HD!0u}PaJ(%;In9%m>}8pbdjcM zYik3;iR{T07fHE!GYqgS2XwdG#V;`s4+nzQ7kA-1bATF#`6u?G!7J2M)##3DOLuoP zmm&@g4aGTjMMXt8>7R^-Ee7<(h35(mRJ0r%G_ta?INMi7M#k*XIm(eIUShsx at m0jj zKwtFI#CHi8+-tA2YeT#DkQp8a^J_S7G40Zn_Q>u?{B8AVP+HsD+m#-pGB|zj%$YOD zoU`uUts{vLdjHIS0S9^qT)xbG{5VHKLIMt(1;|9^9#&l~1&n+O3Y+Q;7U_wvUva)0 zgqdse=qM{bh$mj3B59&Zj5tbns}edXdMicZrrFwnu1zV{UE zH`L){G&RZJKRB(He&qbkjEw8(cMF$&Q1^~H)FL}4C!91s0Fs$}(-n88b5Go0!P*8Q zjTb(!qy{~Bp?m~@+W)86q9s&tdNW+rwbay9TtTBMdkuW9j~(C81Mw<^2ZgS?I0#hG zv^)z|^e7T1pqJ=u2cLy53TQUN&g6hoT9uWFP_{x|wY0VpUnSeynxk?R7al69-uy?v zCQnaKoSO@!dkM1WcK}ak_3LmFa*XwY!a_Toem?r7qK8|elZwIs$2HG)>Z(X|5{#b$ z6^4h}+S^M9kcy6?=C$szK(#*q+1WxnDuW(J9R^TH at JIAKf`924u5wt~TyX>5xJ2qv zL<`R(3Lp?qap1H+J3G4|wJ2>7s8}F{Rij&^!jh92(C`dkBv-9h3$XeH+&LWwM?$T~ zsQuhPNwIm&Ato{Fco43X>>SBBpl=V8a5Kaa$eio&^*Edy2dV?^MbRXFHBLEnS{%w9 z2rF_SA)$X*ZQjw|P6||_6Qn|w!wVXm77xNn6PVW0_J$A6n1o^pLNpqLFiv*&^h}^X zsDrawXBN}L4xK5D0rh4FtNKs%l(ZwYer*A at n2LMjZ4VJ}S{{4-cn_C5`+hLm~MMMO& zsf9<16ZwOJf(#!!>PvZzv$4v!E4#aw_ShXMs?CsciD+(CMnfbJ$IjyoM{zvl%a>YE zz~lGdJRZD>@q-puSy@>o_D4Cg=ORb)rQ9Qfc z5yz|={ella3Wpd&NsS2wyi_oGcxEq-D*pJ)KZ+*ll?!Ny_0 at USJHlsqfc{7nNuyQ) zDS*R!k?ZQ*5sv-&@!1so59iwB413 at h=n#y|v(|$Z2j8QAS4YQgxDcSk2hk=AXOI5f z{=LuM-ahh=c_|+5Mc6Q_v<-(mEgI}f6+`D%;j at n-IR`td`#@;l1oRDDnWJQ<>PJV? zs~~xBeg)w4o}I0Q=UBs#lfJ#H0L9!m3<%pKTol5pjOSPk at RQGwE`ZDgqUNqHs-Hi9 zqA4iO75x;lg|~d7?&8-)oOKJ=hkk_edYuEOfOUC)?b?r)%4jHAW?qByZ`(UM0M#5y ziJ*K at qiavPq!TTqCS2a<^`$BNA+YY9x3!{Uv}U-f!xIu{0Hkr~HOO=b2pxU>j?Zn; zI1s)&RV+Vf;}?#ywY0Phpl30zhegM!xqx|UzP?iMnNixw{J9Xi0JBnh&d!2pEe_y} zv$Y8dmxO)&HZE)GU4k0o+?%&s at z^o!afCtP%!jHuT<`a=C)fE#d%HT?QQ=H&pfgyL zUuPXfLq8^YsXK9ti{8Lk9X>S$qZ3NV*1Rm1KA at pjQ&LiTK1&?<@!>H!V3!*F^l1e^ zt6Ko*Q}CA*R8*}ffLK}MuJ1qG+R~!3ZCdX;tC)z}tYgpo_d#GW{zZoq^++HJE&sE$ zuBp+0PrLlyV=ohd5QFgp2;w|ceisn9s22Daxvs7*7E_SB04iqT@)ssa>;1l8gJ+Yc zmBa&oSKSP3ZE5kd8sw!uNY6W$w^l_mq}{HuNV}rZGR|g-ZfE?_f8PLEE4p52YHGq~ z-s6;eXL3?il^Bf&Ak3%6>U;#xloEgdNt}C0i`friN;Udr+6eA^LS?TESh6Q-hCG~@ zoFpb8AqnS&ypLR84*+Sr8|B>N$C76%tZx8S+XIC{j}odp1rcO3gB*P)!9jK6|H`$L zzPdVTLt`Vllfk0zwyO7S0c^vBXgd4y_Rp2?L+H>^>-C9-2c3?vYM7Lq(g3`=rmdsX zy1nrmjTW$VU~O#;VEv%SPD1)jQ_y8xOu#xOC~CX#QqX!9XQu-9mS-!&ap5?N86?Qs z&yQCd8;=zkWG4VGTAEi`8>M%C6{D=Wjs8{@BZJ?ZU*(7$a-yqeyB{{-I_K6`4$n_z5##v}ld_l{j5la!PM zYF65e-NUKQ8!_?mjBt|&5zWi3o}ue3EI!l5udjPXs$EEku!8`R$nUKV*d7rf?)aL3 zPa0%EJ6cDL6B}#8bD^N12m`WZ6*4inAc1jPVxC`M4!Bx)Go+JObyLx^It@=U2& zI9v%8ENy96mf~f4kouA3<)S#99b$-zo!&vO<^LnK^cZj$HvkTHhUErjoNR|sK&cI7g=OZ~*KV8`e%~M)#TGTIOuSdZ;~;H) z{iviQI`k~JZVaSkr|Bocg#k*84z9zaqkGsB2Jz$-eliFQ9M=nv1x1b0hGhpG9jpdA z)1kOn7;n4^;v6*@KFGIk6GGMYuRxi_Y3slhhaa7fgLLdn8+rBeC0;E&PVdzpXGzJ) zc|i^AWtC~?4Bg&EPEKA8xYK;5He~h30aT(;$k7!CimH~bt{Q+;sJX{8m;R!Li$yHD zCr at 5Ol?Fu~!u6Nh at dL@m5J{*Z;D&IH|G#uUAnz*(^%1>t0Wr;S@;d09R-h9-Wfrm! z59X6VDR784whchv0WYzfxoNfwC_ at BDuEBDvxH}>i_M^2P#nz4WkR^BW0prlN at 6Vq< zG1srF0s|C>Y+qjZl+7vJk`@ojLgGI|U7X&CCdTd_9w0|0&Jfu`Y%4hm5uEpmsUq}Hakl2gaG)2PoAa_eiPmi5? zk%;|Q9Quw<64+P*^h`S*EDv8nm70|m4^ZDSKF;AcHvr0WAG%iE%X#x_9;g==k4d at Z zSqS!P;yl!?A`$4h-An at AoV>ieIPhFbit!(+0SUZQQX(|= z at gp|GA|oRyK(Fuq_TjNp@!7{kJ$B+E7j`!Nh_F*gnh at y{2jr~UrJoG68}=p)dIotq zJ3AK_7nK3RCMPG+a2DDjmH$XU8u6kIz`rSQ z{gO=J_y`~tweBrI84SxUkNz{&)=c>F>U^nkK|z5nFdLl3k1gpsA1A#`nH#25=XLz1 zI`=L;V8dzc|Hj3}y4*xx6{}~L))zmM;K at 2)IX$(fb# z>g(%^NT`<~70c;fZ~}t;0t64v(8rnC*y98mnuEqveT92iWZYlw^D^3n?&sI`vZA-p z=E`^Mki?hVe*IbxTQKPMXlu*$bFA)aYt){$|NMv7rxKM6 at 1JDPZM!`Jv^vDxLqS-W zIyN>|@KgZ>PLOwVJ8bQjIcol39ymFUJ0GmDK`Ux1?7lpN>_PT{&0_dW{CPU2|CP36 z&gB*=+`YLl*-oh#w2rN2CR|y#uIRo(U3AQY6=IO*lv8ij0fd1VCwpy-^0gaq!7Kd<+Z&QH?!#dK zo?P7AyHH8NlX&mCC~m9-0>G@^R|wS`06F>{g1DlW7cR29^m8m0o)XW|qnQ3=lzaDT z0DRHX(Y0Jkud{lf9m(0X`lW1c$Y><+N`5KWZ~iAXgc>Ik<+M9XFqyJ z0Zd=LdzT#weiV%{cVB=1(nJdhWaUKX4c4cZfAil{yiClaTI2fO*1>`I-qVkjc5TE~ z2eQ6;z&GM1Eip0i#LSE!iuOVvvUwppo9O)hkGe)n6zV{csH&2{{h at z#m!_W>o3-%G zAeasU;lFy`(XsVEPZjjxL^{QYd!v1Qr(i?e(SgPk;ZsILswccx8Ep4!6JY8Uc9z{ry9SBZX!S0TSq%ygX|RRgi^` zmzU&x*X@>w-Fw6 at OsYc_qz9da4Rf;^*Zt@)fQ}ka?$MT6UW%WWr}eIaKP?b!$SiI- zIRacDI!Ljxu_5py(fy|$P0HvvIrmvuSfH9=7Pah2dlelo!C!{^dtY894*Rsdc>$!6CQ8yAO)0E<3wJw)rOsw#0QDRp2 at 9fv(ehljmG?^AjE`E{V*++?lC2baoJGnx43Hn&U-+(TJDPn$bY+i_qMt3WDf1_uA|dm?r{AGEtl;SUbsh*V z+VjOS2t?9EeMLVo0Prxl$8zgNDmOPb^k)YG69K3Rg#Rd7Gy<%mJv$0yG^HCDV4NSR zX#>U=jy at d!8cHQgJUl!-4VQhe1^*&}eX3U2K`$TOqX54#YyeG`=H4H^Eo#NWF7Jy| z#L-BD{-6#8dr?=w>W}n*zEwQyurPiM=yEPdo&UP{H35^g5Uf3)UGJjR89*T#@xd>Y zhj*YRU4!dKr$9y9Oqa;wV!?P$C5~6GUST(bh>M?}47a=!IgYi at H@?eO$~VeC0kxm@ zh-J|(_2hQk4gwN6 at X&Oh&$rhtfFXuu=F;K$4jJE2e?2-!85A6B=i#x}%gbw^%)%5= zWwhFb0iy%f$sWK{Ttb3Qp0D#969^kvCyXjPJ3C;S@>zH9e*W=8m_xIhko*IZ2YqnI zpNA&G`k+@>(7t$yy{WfvM*_{m5`hZ at pkVaW+YTH2UzPjTdmvnhi155e#nQSVFiZ|b zZ3{|t*~HfgYs9icI at 4(3)}x_@*aC;gT|fA#=o3Eh;IuACMJ6Z^QO}+!;D!>=KKsu2 z19-s3U+^(8b_?Yc6nFs)fImb&e0aDb at D<}BsM7iwQc13#MxS`)UzqCDNr-Vd0yhS7 zWdg!T9EgNG8-LWzo3sG|0TZCRu}=uqlFZ4;iQ`=1H#EOxWQ+v!O7UtC%FO3o?XDb)ph1`kPO$z3337PD3d1V$59QLFkKKr9S} z^78T?y9ZH_{27FJcYB(?=)Yti;((U}6Pvlj+S&zjY`r{BuA%P3cmrC$dzNu+FJ zgU54mC-mW&GHeW?ac5;^C9RxJ(a(shSFw2)7DkkondyLDHd4;CPo~mLFrhRxH9^u@ z2PuV1`0eN5!2M%9GX>mP_1UF)w8#FR4>;(+G#wpnZ9Iij<^JIa5c~u1Os1pRj}+}j zA4`bbma#E5xR-5jfld>~#C_-ECGM~r^qq+SIfFCVhJvZ7sj)eW at MCBaFX54T=gu{? zXc{{Z%joZgdVXo at g~Q at R3(&9_NN*7pb*xi&#B36}Q^ndkJIR3QsYAM~pO7++`SRt9 zqJSV7DJhz{;Q=Y^r~=gNH}usT#qJJm`0_Fd3DqH!VlH22;cOrbc$@+aGO>lUXU|m-D7f;VsPlk4;czxR zP`Wgy9W0-E?}DPx3AFa}{JhA8JQ)K+yZDl;K%_07hp_)gP1FMd5tejD^IoA>Jw$Npf5#3+2M3u>KQN(_x4faIj+K at i(i{^QxELek^uS4VbyBD~ zy-mSaAdJ at TQb2pKD0KP!_{<;wE}re^shOJu2l_>X6^FA+d>#i&jPl9wl}yXE%j=8W z5GuyXbiij%`}#^Djllx}fTqU+mz&p at rtG}D#8&!e4s=(*(`S?O#)EH9{TDdoeP_Zz z&HUP{UgQlwPZ^%q#E&1_-L;XCq`2AA)YSA3=M)tKI?fiE#4(F?LUBpDbBF)JGbqGp zAuB8Ugg7DICGSj`c}rIpRi=ywy*wj-v_bZ9JOzd43yz2WiAP_bUteBrGoIf2YvZ>Q zZmR)ov`6h>UYzOWMO&wl5pzWsSmHYZwA1}1#`p2W;PTIpTdDhNVkv+>w#Cr#_B?hn z*p)ACa*Fi-&H|i^ud)f=UZ+!0QCWvP$0kR6XJ>0)A3c;=jr%9>+B!MCAlcZX<><%{ zvi;D#r@^4dBBP^uj~!!2-BD5N;pP^H63V$ZPc^!f4(=)XeD at tZg_z8*|8EoHEnFs^ zGD72az+Tcthltwzd|o2t%$2pZOya7Y1#1u$il#9*BX|)qgZO%k%OgPWZy9$$z$vR^ z-vS$5;z#a$1Qvt)Hh=$Y?c+Qiq^ze$4YZbyf#E8KedOj3Uq3&iQZoq*7m&VCuvB8{ z4~A?Uwr-Gv*u7 at oalPgBYidVF$GU{V_0S(5{mo3AMwAv8q-YCdXB)2$>?)OV(2S`f zO at 5kjqw_r>IZaw3bNe-arv38!FVeMy{LbOLPU7JdQ5{WrEw8?AYBg5JZ)?$4-gZ;) z^Akt^3MzLK71C^YpMG>&(f at M6DWQza%-+7fi28bYv`o#(ArcZ2(gL1uC>Yt;cuZAQ zwG!~}(OX@`Cy+R3 at CQs2?JXB4{?YO at GTpL{k zp&2M@<%OveQnIoR at T~z;H)OST1XSYf8XFte*Pj2qQPH$X1wh^!PC~AAY?n&Bc){`8 zToe&AaZ)b(lEj{G(`N^-eh3q=WQxN+ at i`|aN!kM{v{M=&ub^&4!;+!DER?T~dcW_s zuC86a&h2tMOmXM}8?>`BBzX968-(=|4zRx)7t4*iFAd9mx^xl25K&tz zgI1j_EiHOc+VQ-*ckc$O{`aE;+>f1;ldK*cox=zCVHbcz-cC)GWT#qUtAF+kOJva2 z{F9emT at lZpD~(@SI7YgAclaAeNp=p27*{v9*=zGJUuxaHecQG*k`kNUpj7E-X^8;Y zyz4BB)Y$#7`a<;`fesD*3%oi`BL*$MgP5QlN zQ+;L46B#b6jC-^Hr(gkD_b9FyrPa25N~o)Fe>6amLsd6ql9lPVJ6LN!c$fESz)yKKZd@#|Q+1vZ*Ob_MLLhD_5>?Gg9>q4w5Bl zk-~rB%314t$EAf0W2{z3iO`L~M)kQq2RqiN$CE8DTa+fqIZ?SlBycf;z4 z8)$YsW at dN({QCt$m07&O at eNX-Y62Dh5rh-}|8acl7QLB;#lAGTFBj|9=N?wz?mrn1 zFNMAE_nDbypySy5#Xj5k`1tVC3%tMqXf!l5irRQP!vL2jqnNGN+hS`ze*B2V1WQgv z1`8hi1?m%3Q?nwjp%X8A}*axdP;~i+|jlP-AS1hr;H#t1zx!VdN&dH6Bdw;jt-=FhK96EqLwj7eZDDc zXhhA5$2#Ho`M~@0TWbrv($f1?b7?_xMBcwI&^THP>#wJ$mqz11fVSP%SLUVhxEBES z7iksR(7tC2P3X6p2P*tj$!+UI`zo`SFJEHCZlj}bfBd1&N8ry&Md;qAlA>`Pszye1 z!XhI7A{^6p|9Ss$fQ*mJ>?kUsur z-2ovbVEV3q8cj48nU$N{;geZ%;h^v6KzX5T!5+G)P4IsBUcG2dTHX^nI#fWEueP?T z;!fhTYp6DWq0=;fEJQ_-nzQFc8-pnhUZ9C;-z7IkPIbo9J>xA zG4k3q+?82eU3HX~vZs!Xk8dwM`*;x`(QfVMM-qTkw{Cf_FWR9wJ$)>xSmQE1#M~Ym8=FVEf+`bBwmrB%5+~M#T at o73 zv%3QRz^CLCu6Ib`yo;%lWJJ240wL#dT*1dUQ_WO5U9-fGkspE1Nx%F at haSYQ4v3U0JYci9hrZX#krj}`PDf* zJ`VgHTl2%iX4N${DxhRRLHhQ~e9$O;4qAGBa}%;)%_7^_)Kt^XjvJ~GA^s*;OjYrt zNA$3~pvctVF+(V!xumS)=(tMqI3VpBFn- zKm$`sb~ZL4$?|w(l)OOBAcUJ7DtzAg^&;v>_mS$@76`#F?@etwfEa$c=R}lKTpSZ0 zAAg at Fj{BaCfx$lPcp%Sv|NeczY+))M)?DA%NGei at 2snWUPUqe~!~)pos~LZJetsTR zFgBUn+tr6hMka5_jyJtxx*-e>kF_~)Z>pG-?V;s>=e2-47`Nx=tWZ8o4;^D z0s)a5|PgCbn zadpKb{b>6O&jCWMd0^nS=P(U5H6KYb(U0LO63*adqv!T+6HA^EaZDnu5YC;-i2Vlv zpJr!gF&aVO!xiQ{cyJwXiU+$_Ou}Knru*&adDVl1kDWSo3ZVAOmvaE3VclO_U%iSQ zcNv9>^7if9iHmi5(XWvh at W3dsdB8H>ppYh(Cyk0F3V| z*|RD$VLUJ7HdtyW{-_GF-VFupp+kojH@{and>;SdnK0?3a at fT zb+jeaBYOvj*)QDDZ7LofA|DE)xhTxxH^jumHz6e_medcDkX#LUao*93**kpP zpVcSN at ib$>(oMoC4-XGKp?Ezm?km$pGhrsIg4oo9ACD|^Jb2Bj?}r-oGq@@)MMcY> z8y}c~rAI~WK62!UR+VT=8uqK;!!E9o{(F%O>Z|v;b~B&9e}eVy%J2XB^{cqF^m%zU zdqR6hhaFr5?pgvjU;LKoWex!ayy)23 at q89wGjU2v z3TkMei6B)c78d?Sw;5AJoH}#ngsg|a!Gqk82LLFBr%pxu_X@$xi-57%9U!7e``tY} zF0B4IYi4FP0r}VgnZgCaXKLx6 at M#K4rDsUG&MB=)SZJ|6yBIt`Wp+$m-p-$Yx7h%6E`=voc~X at wxk>S($doSz(uVa zV(fYjRB+S$crR`%tMw$I9^Pw#*A>Q!ZJt)rl71Q%I12%t)@PffeX?;I8s z#L{Z{jgcaP^1sSTq?NpSQvrlJhA$ujNFe0pgJl+a(HgYv6h_w8G at +rPCZ?up!c14Y zzuH4mtiKEWLjoAUjV+z=#w&Ep%y`}xVfC$rqfV?V!PC@=5)v$M4{m8g_gQ?p<9j&KXyFBbUCw6||h znwgvDL2_A&GUH*An3%AL2;!?r3~ITcj{O(LHO`&m1H{84%;V$M!yi828xTPL7JvU{ z?bp!J*VhNk2{Spg^O4_!DQ-82O-4pWWdj2*0xuthy_Bn~YwP=mrgTh9*MMv&3o_!7 zY5+!6C^ustKepIcrO_l4NT53Z`t at t(Zf)|dTQ}3wDXYw=;gbWOcpT5(=jF9&=cc0ndOy6}mKiI6seApFa{d-q}z69wAzDeVb2Hw`1WD2Ta0YMkVvRfeKyUqx`2 zVTG%2>qWfNQ{Qdo#-pnZDV9tq0N5<$_b+mlyUvdgIruhz&!De|7qPM zf&g4n(!F~LINxk&9>@_SHo!%{f`{>&QCJPGKC=cg%ywn&{Y{<1iKPH%cXvLu z(pZ(1H?p55`VEvcfOA0A0g*56F(Ose#$pUtgJ*%T(xUKc+F7*_JbYM^ok=DB=-s<_ z7k~Z2b{h{buQo4 at geNEzd?PG+A7|(M=#$)vip0|o&wK{P7l!SLKh{vefO`Qx(!=t% zF-oXVm%{=AV&~-NPx&WKEd`(?hEIF(MV?0`-f#c@{rDoZq2TsMOS#N9oxu8V57sU| zfvZ#O6sHb|T2L2uc+jM>Qe5!Rp}gGOT at M~S(2G{5C2hkK6Rms1Kon4$>2*3j&Mhg4 zm3AAP;QYHz{`&Q6!u8vP3hciFN%{MehXyjMu<+nTAD`^Yzl2q&@7%meYx(SwN3*J$ zXzCtdnIK$1Gzr@@u|X;585(lpIu#M{V|liJ`EBR at lXqLM?>|gPh#@>YzO!t+y*laz zyxdWqF~SZH#5{J|2bF+m at 7}%DPoCsIer)`cz!VpSObPo0YDV;WGcz;IP;F>gS>tAA zoFTMwL2N|~wRdrxM$&^2N?{{T_S{FMRp(<@L!p zG2HZr>Qg6e0?`iihg`w`=Q(lJhyWQ7))1`#TqgimMQxg1`)aile1P7$4_EF2TtEql zZIU827UQ|^->U+a?7w{ZGBI4`5U|w2oK6$V)mdFyDh&F1DnIdfE1(fJQV6Ej)(P1m zo0OUfY%2QtdmZ0oV4HfPgV`n$YQk6*$X0ys4N$!K>%}FY19l+hsWnd0hFUp03x at 2h z3^~c)c`=2jHt{44REBGEN1))wnF- at TE{L4$*#O9!bB>O!Z{K>x at bXeb3`Eldn=8=X z62sd8WFsZs1^3o7K2Ae~72MxlR~P5*A?d}VT6k;xC$okZiq2?g>;{~qqN2k6xq*QJ zP*hq}O)(cV`IB>@@&Tuzja7&LW at cx@wfJr+{CqXUx-v4WYiykS$8WMd7QfBJ#N^tl z*djIHkGTI&OW#YE`koE1bl6wr+`k{)*LMntsst_flBwalq1Dw(C`b>bsB+Q1+~G8y zPHX}I_{#M6C>ss@^kzr}D6F7-A(hvzgzPJXWW~oDSmf}*T?RF;-}k}27fjl{XXodm z3=}R2rwBR97n>O35oL+uJ;)abfQGu7HLq z=9HH^UsG*14~FbgT&Yp{c$g>*NW`(%QSzp=>NZcjK6Pdt^#QF;1ZYy!#(g4eZq7CG z+BF^ti3`o2`H3n+|Av&>JP%3Z-EtUMsewzkuwV_ot9oFDC8~Zk$8e!+pr+>f^{z^5 zi$pRjD=RG>9aZ4<_V)I8KmwIhb2HI at JmP9ag%}$9SU<@GJxOXiCCgEeS=L>f1`6cyP3B7pv#08xVHs(rrcJtEmJ;fima0Wng+kp;(d at X|7>R%3)LD0837tGOsm_ua}Db z^kK#H(!|5Fx+&}<^9M7wRw<2YCT}-QpHOZ}*t7nejR2w#QV=vc#F#1>{|$+$dhKU$ z+ch;c#U&*wAj5H+a9}_^bZpHhNo!ys at 8Z+q;$meZBRU`$hQ`KM at 7!U+J(>J`wb0VS z!rhQ)Se}N5380d`bm+uWz|+O=fbf89?in7AfehVrm@^GmR%X6HEe#L2K0q6y3Gfk{ zA$ZTQChY9&1Ofp+2MXI&^VdPYzv*MIRG4YsaL&gej8{Bcwe>jGNr0BZ at a$QkwFVD& zclF at l#<6MQ at G$Fb4w?ZU)6=I at LdxMvA=V6j`Zzc^q-A9MDQs;>7BIk&c;{OVL}FRB zT$zh2Dva0A!GRaZzTxT98q9 at KGBSC<0zyQMqYlAcEw`?F=PQW9N!d^~PUjf|MXb at wRc-@i`|AB6`7akJy7&$pXAER*Rm>OA-fO-&qc z-n at ZN$mfIo#G&D2zKC#UID1Q}>!V3q2+y4?%M<>lpj1_xzqjk~<_=*M^4ILG|oSpeW at 7-vOA z-xiKjQ&a!9huG-!CzL;?SDhzm0*D^A4Usd2D~Aag7jg0Nk%Br4{Mo`tDshW(|9);j zLfqU2^ak|+zj8o+nVOmg1O`UDo4)#Px~-+f&TxE9X?sx(UxNb;`R*_Cdd1e-0(RU& zpL)J>eB3g%1YDkklQUaogyy1o_2mFDV6^!A3=)g3J at E1SjBk3Z(;UrpSV0M5#F`0$>Njt)T) z5e>lXuCA_`te?vIoT1ksx9_^uR95D24h|0DMrlUI2-l8&XD#p~&^>!rS66Yv3 at TX~ z(x<)Yde=am0|DTcluXXrqL`$FHNyRUd`ES1(jHGG;%O0b&LDByc1ZvGh_8DRk( zRFHnuxB=vB1c#E}enF!M^G2mLLyR7*jP$f zgjpY2zdu#g)qHQp+H+=SW)RO at -P}--4xQR}djYZ%DKg^NvuDxLCm-Y*TUoKA4RH6Y zV){bw`}a|x#p7?>cx5i9vaS8!Mvv;*vj^^y6uC09valFhSj2U9o~W;|FW!nQ{%!_Q zQvC3tnvG5N+h8Nk?{jml?d_zFj*jOXD(F04%eVj?$|)!yg#^cC_w$nu{;YNP+?I9Z zuK>k5hT&uZsku&lyaax|oXju4y_97OkJVWif2wF)8^3}tmq2EayU*lYPb`Vhe-My7 zBJltK0}2vxI1z%AyE|LuVt$2oGJo%lB2e_=Qc{s6B|@d8rM50El)Sw8Dw!cC;%$x{ zI|gD(71oPt&mJQqBisgt00NLy^)!_{Bymz&eN7|_GC1G=S;)m{w+Bnb6#_P11}7); zE}5`8JN<+L0;otqN?czF1@~lmPh{YYM`pr!cY}0|aR~`jTkakn_{KQ{L$cGK zC-nhKJh)X}VzL?ihU~w-&r at Kcl7E9HKllN>R94bPMMVVw13Ps1u+iDG+5$^k)_OvR z4t4EcH{>*aXL0`g`Fgn+d0E4E?*#nAbND|kA7rHZ%5r}uepG*MGU=>Gudtzf_V>1hjw!uNB+S=Nk41Ih}2{?9LK=IS9lY>BJJHEV1{yaBF z#mvkscl2oZyXoZ4VLSfhS5Q?Bnl``}L_wgLn40E4dUVQDRdmI&C?k7IM$I^uc`i^B zR^4;jjcA9z#z=oK_*mWW$wqRLo1^I#T(muVmItm#y;nXd5 at ERaOE9y1@w6E*ZP0X< zR#u;8AE<6?@*HoVB*K;*dHi1&jk4f9&nzq~xKnh1 zsOn at NF1C8Mb&}`jsO3p%g;Narqa!1m9}_`T^v3xQ_+^xrivmvD+S(E^FfgR0r2$C} z6OKqE9v>eEqTB{}joW6saPr&e6R*FXeeoUkDk>^=hAd|zRk?rt`gQZ}U45H;!$i$Q zj@}nW3kn7 at T5G|73okVl6=~8ek8ggF?n+)L9Jm4rcNo5- at ok?!LG#mz3v*LVYpG;p zWa6^2(RFoomUiWhjXO^XCIh3RxG#!zjGv!uX=w at AUKE7g$Ljg%X>wE3q5Dib9WP$s zNFpMRPc{TrR(u0>;`c_|hUx5mAT at M!bodrSYilbJ5lS^6`m^CaD=RC=kvu#^?#BF# z%WzffL7&7w$NhH3{2wUQ^w4a7Do+dE>-$E(W2d00Kf}V at eC9jx+;=GF%hFesxd at dp z*8-QV9#34X>*?>m3h;pCt)jx9Lh at 8a+>+euFv8t)M-G69^WI!`$x`sAfuFdzx_Sa+ zDaJl8FK>|=UyNNZ++IV2VnIOxm$dZ$l9CcU(;=^*fIW- at l8!hstf{GKadB~7H|puY z=4;$`w|aKz*xGkp{$$*WgpYmas|ZvOQukJJvYm&=&a)Q@)j{?)eA!ymwVRuPKgJtb zKL!2X8}?$T3BFoTOic6q`GcV0zLfi4R=j5!Qs9*kIkG#_+_Z?Ln4DKhb-r9fI>Pw- z%V3`?1BT9XQ-ExBTiDYHJk~un#s-`Y4-+>w9upQ3dF35pHwfIMxLA;nkB{r%!QBTA z8~}w2%H`tT_fR1&zB at X1Z!S)(sX2jNVP;F<5VJ~IqKvF;{~Hp#n8SzFpz1Y>f{v27 zFsc6Z*|WC(eun=02AaysL at sRXpa1K`;p?n;&;~vnzU8VO|5d0O!otEcH7pRPGjnr& zN5$a+H`%CPE|~W{c_{-53(&BdAs`@tZ+M3O-TIVH6U}vkuf*i~ z%a>Z)>r;ui?Mh_ZylY;VX%9{T!rJN_dg;m)g(F9f;7OjbG0Xp*&;yONoPE@#kFVb7 zhO2phUtivLLU9HeKv|%ZT~6r9Trbh^da^kSN#)=%AlgvoM%B`TlN=ZqnrLV7X<_r^5L-kLo7H7|fzj4e51pYLV^x(nV z!v90ndw})aw*TWwOFK~!8d}m+N_$GQG-zmO(a_#IG_ at pBT1p!YiP8`acapYL+B at x~ zlKju}zMt=L{QmdxJcr|;KA-pdy3X@;zFyaL4(h!tIyw)Y=RweW`qOTwh=%gD+DyVv z(z?GdX`~~4HFbYl=%sy0kn)1aOr7*MYt=%p-glS at qi1hAnD_d|hPj>HrC~Q<$oDsd zEtC^3Kr zg!0 at sG6|q+larGGRthdI!a(6>ejc6cpK2a-EMPmkf6^C1*Y5 at A!M8q7fGB(nIq+w0 zvY^-ml-j-$=h2UWHT-pdO%k-LmVx25-wpb`_EtSlzt&tfb_=Ko-1iCjCEpqud^y!Vt!R(!gHKno&_whDp1y zAB+t1_j7MN%IzyJkxxRUrC9j6_zob~#K1}nXVb^|lBI6Ujo(|cN7 zo$4rC42lq331ZGZX at RUauuCsrqBzIijWjeg0KqMPs?7qoiLqQ#o$I%5)y;{CiTi1O z=}PDrA|hDd27lA8(RcXpVLYLBt#obEol{tN4QyI^jw?&IDp+4nuf26TAmcs!$jrh5 zaY}0#5SYP|mzkNj1#7ny7jJ?p_y7x+o}RwG{h_Zfd8jdo>BQjR;6KgHSnPX0BSTBv znvsE_Q+yd7)phBu9vi0zmFSbaK%Fkb=Y at r||E2eW;>DA-Kuz745m*D)Dofmv#zx|= zU%zJ0I7Z{Yo9zdxw*LFr*`%jMMX`>&W3H9%W7$hZk`Sk*O`khdKR&*H at bA03P{g3g!wMSXPus^+4y$tpVrp%dU}VtGHEHJ z{>!{dpEaNk-+$#w&rLfkf)m`^_6LyLXcB zTwX#*+mq=ImYUme1P23 at JtrxtcU1j>+6%kYG6gL?0Y{S9|djg6|bPHKwdN5s8*A2)vX;`lJD8#j<~quru>{q}7$IqbHy*30uw zPJ(!z^nnBQu9}*XmKKe%v9XZ-H$KcX4h#&;ZVoX|OG~S8o2JHmB-lbLD=T at f1at#O zM at Kv=Oi5wp(n^=M*`Mg^>zh97p>_5({LBbA1QHAQ__`Zqqf=91z%8zC&i9c~QXcD7 z`T{U9^7AJJV4m^&zyQ6TaM}igGV^+pknnK8AZPU=vx5!SZJnILYisv{Tb~95?5=$J zV^^3Ss?3f9HY_YH;~zZ0K1g`=%KONoGy9^myLmoHxq1~ZUkxoT>vDSnTKhX+rS zjEs!d-2_-!_lb##A?pIP1_chvRE9l0;qFB~hG$@6Uwr^%pS^o$B*3CV<(wTR4ov6s z?aX?DTjOQDgG&1OfxOMX{or!!$5 z_;u`IMh4Aod7N2QQBl!8%_1i!_uwZpDeya#>+d)UJzs;G19Mz2ISwr3U3y1d-BYvi zR}eNqork$oIiH(T0?#|j%slIMWjM at z&i>J(N3Pp~@TI;&#g85x22wgYKTip3C2TUR z935~ll4yH-TlM0_%;A%`99OTB6zGv72QV;b?{(>>Ju$Zi;x6Xi&vWsr%F|W(sf->0 zS`MqE(u;pGS52v at sh!>2_Gh$l*T~h|=!x2@<>-LpvWbY$8yXq{Kq{bM|A=zGy!>oH z=gc)8d9I*!{cG1w>j&!?8>1$d_TRdn9+3^6jABSq(qeU at 8F>eeo3CKv&el2gs;n#? z;0f3{cIN;S10Hqg(tCd*Q{){QL-rYnTk4BJOnJ>-+wEPa|h};;*R^`$typ6Q76+2h_-;jvZU^#=2lv*E2YzxV9Gv_|}#;2RHXgVPQHz z4_Y#!%b?~H66jM?Q%6QdJ6Cb~RRl)lbOG`Rt=7HjZpV-5amv`=$U*9b;!u|VQ#u_3O9G2nLz&oG* z4RUpJi_gf2yK{%ox1Fy1*^oR9Sof>+Ey at tZOyg%#kM^PP(AK6)Tkh at XxPABT{+e`U zPC}qENA{R$Ee(OBYo<#_g4kt0eVPJ$w!w=lDk+VQjs2VC&pR0o3?ED+-a|1mT9Rrof2ea1oWCy9wFOaE{&L_xe!m|owRQpGaqr%#1$EDex!4i0J* zGwagW at pyn_1;viOy0}OUOvl^XJGgB+?)mQY8nBp%*x07NvYRfWABjc9#O}0Bcf~vh zA;Tiv`rO3C1Pg|-nd`(eA4U_ITYzF;ENox68`zbuZirlO+4nw8h(a%vk_xg5Mi2)z>hw}Ti_@~}4 zKRTLH`TTh{US4t+7Z=pq@$qz9{;~lXqu{SfO7$OhpHngy9=P-FuR3TBBcr1LlrLVs z90lzZ6cmK%925J(3?O{TsHWIm{{nCW8V{U$mcV+y45&6$jaCs at Fc9GJ_O3y$`$RXK z9Dosk&XS~2p*qKL_DqBGeN_%T^R=?#u&}VWfB*izcvT__3JUOH%oobfCj+`eMn#3` zUrE&4T41nvUewvuHM^z-es6T`nz at ZlL~!uFon1?F^AGM74Q at 8JwwWpXZmzEPA3X{M zhW+~O+qc4zZFX~JByaFo}v(?HTcFo at 2{{1fXhP|QT5db5-D_3TBsXZ1~*Vm7g z{&XSI*3%1tkb%wyihXu=7SyhLTna!D;NEq6dkT0Bc*NKUHV!2Xo4{4fwu zXD_dCEVPifvA0)axETmhkQBvfVqzEsY-}R~Ol{u at acRJTdI+-EP^`2x(`&UGi0!~+ zg8HjJmdNGSHWmk~u<}Bg{Wg$G4BJyvDvX_!-$XY`i(b7t2ulI~{FG4y0&d-;>*(Cv zB}Sz$Ny2yevS|qI$TTc0*s&Q2*ccm#!!ya03=9naYkv=vh0 at E*3zqG{&osG}Js at uY zsX at MdrsDdq1b(C9*el^q4==B-s+P^;N$^7)kCR at Nmc~~-c7sRA$>HqFxp6-yI~)7( zz#f#mdSxMb?2b9O;d)H~>|TJW|N0DF at Fyz1TVg5cgw*@O!on(o{>ow}1mJY&E&yA{ zs7VvJwQ^J9YW}^eRW+^u%lU6PK~#1&#|}U)RirW at gi|6C5_TaW+S=M$%i7ClAubMC zO965>Ha2R>Bz_uK02+&#CO<0O{;p?mGDKk}D>!9krU~$-kri)j`UM6zKR-WlG_=`K zQ>pmPn?%R^c at Osa`1q`k91jQx at b>lX{Iv-GVS22XjfEaC9_;3YIsKr^@bp^n{q7mO z<2|LDd;ho09dAt#x9jqW- at h9`?5E{ppa4^FSM2TTqVFv-!P1NiOP`fSozH=5VunfW z at 5L&GzQM6(e+vji00Cf>-QC at +v}C9w!7edp72?`N&Gq~D{n^Way at 4OgPpj(a=(GhU!vH(Fyt?`k5zw-U5^skO6-nde+lAO; zA2OA~krTm}V9LJQV*zZj-Zd(d0HhI%=BK}=De36o2 at z(kSMTlo94~D4tZ?`8!n=2F z&D6S{=lYr1*&&r~(^&t3?*lYHiF!rQ_WzeKh@}wNv$OEZ^OEZy?g+~)XXfRR?(OZ_ z*x1M`DiZn((l(H-%;sf`{a$(x;jJS-z=<;=ljG4Pb93|C5ZUvn#TTPmZ(wKit?lWI z$yEy(8JUR4$Pd^U_r!_V&z~=WV8&kbn2Yf17t5NIzfO5xM at I+76F~cpzP#bA>Z)r+RBpM zU4M5^&pV&SJ3NBAePh7ajyS4P-UBZ4qrzoBJR{l!@KSS2OQj4yLPA24V`QD*zKPv9 zYM7&Q>V&knd`1wqc?7X~_5)wx)Uc9KPso7K?PD8M- zsi_Hg|3l`3KY?@1Ns~mXaws}F7X2V81n~xNpXnB4YHI54kW^LmJK{*)#b`lbY7sFp z=iS{!0p>4Xxk3y!h^=A34Z$o%*0xK^%1&-X?Z*UeP#R!5_d#g at 0X)ROaHrAYLt|rT zt<%QF250>dlI94TP60kXWr!Km z4=ZRh3k&Z7v9WA^z^NXt6n&ZSw!C9zn%%9#WJLQ;ERolZI{zo=_5sXc8(gcW-5C%} zZX at yor@*WKOP9#^VR8uOL{@tX1Km0j>uR&mS4O(@{_oz>qVk6uWo!w}%Ro`Eplo+A zE54~o1^dF3mX=od{laDd9bH}f&z?P7QB~D)SQ2P?yhkBjKFF2i6n-zz}v at j9-FdH^WR}ogir_OoJ;=>Xf!h8 zq9OtKu?(Y;FXJn97NuAM>JRESXwG9te>DVnvm0wG;jikA4TR2hYycp z72RxCUS?L-9e6y;!FL`I$jl{QPwRjZ!Tecl;iAg%5$m9_<{>dLQ9WF2uBW^EdRw~8 z*2*UqydOn$+nDPiqozLL_A3B_85spdeW~Rq=kX>gcsa|3{_^jyZM*UZ&#u`5)wx<^ z92pTo1{Q?<3uo_>-PsZZ-Fy1<>FQrYep|io_ZqR%Yz&hF1Z%h0zuywe)|nOgbAmej zZYTxgSg%s3k!MQeG?TBpoTq!SkvCq6AA=ICaHCB at HlXW>^Bx6 at t1OV`1y0( zu6KuN0plHUKIiQ0j6EoCd`Tj}k~adnl`r{zt=QNGv;dGz>;~-YEX2TXgjo`~N>&f` zc+UeR3D|3HZN=-|L;;g~u+;TkT?wwC;btug`W}td|z%kd%~k#xC5-$|L>d_E=z>o67~yb{o6` zn*sLpc>I{MCb=umiv1+${+DMAgr4^i{M%vdNDmJSoO|}ArE(v at CL<>YE83)T3oEA+7lhwmMepJlP&c;?g*xVp6P?k$GRrNi=*C%HYO(z;9 zFw3~%(xn=V;)RX%=RRA2*5x?Vl}1MOaK&h6TU%SaG88a*7gAj< z&2;oA;B(_U5(6O&8|)e_*2&2UZ@&8bpS)0ru>erUszZqG znAr=$9%zv~C at 2V>I2fCh;(^CLLhs#F3BOG~OyUGJf}Kfw3a=QH+x6u4mBzC+Wx<+& zD0uer@cp#S?WEQ|yb+4PHyKfJv57GR77SUA=T z{BJnLkv*v|A&P7=LUW%VorY%$!352AF&-W=Fh4MqiF;8|uxMr at 4UzOEuU|KOdwH$Y zqVb-vSK3ftUnIte-Xs^bsOlXZA$^ zFg`mwJ1lp{M17lEw;Hf9%m-4I=;W!FHGaP=K+)nC;#ej?59em-Zc+cn?$o)jL`rw&=08#vItVIGXI=?-EDgC4}Qx(@h9Oa#gVth~CW2D`DovrU<|P~W`*bg9C9_Tb0*dKY+~_07$X z;Q0y)3Wz5_5NFGn6c-j2aHtFz1)fO&tbh}+!?r`b_8tvoR2A6MVtT{1KuU9h6`KVWQpM?sk&6(+PkJDt>M45kyn at Cd?aR+f8AY4&wJi_&t;RIj+^Jz!i&jyN;lD at h zr!RWojm^AGLs+mt&En7!%bus%XTobYJa2eSyP25kpD9b7PEVdrT}vH$TYNtKM|^Vf z#oxb{@&M^|jg60;Iz$ft=?HE*^uI2+yt#TTN9Q7VV5a4#*p7~87iPyAqd4MKdHDFE zch+Z>AoA05$tRArrii<5&KpXC)uc&!+{0FX4=&}*b;Vz)I`*yD%xor_E}u43QQ`02 z?_yu>rk2NoUGOUxGcR9$rV_)$!J%ApGQGs|Mk6JQZ0duAgxU5?C1W+Z5w>E}nyjQG zrv3_-lYiFQ9(9H{DR&jlm=esC#mcpRL>eCXIQzHT!@Lg|sdvYV0`x!M|biK-m#KwIqE zpWYTqw;>=KkDN?MPuIl9!g28{f)q z^fVCdG at H6$g6^`LTEKS4jM$$6^y}#99pX1A3HJB*pBZp^pL2UZ>63sUCv9yyhrxG` zfPSli#1ZwF(|dBpn!K;S-<3b+7Y8S2#|y(Uo331q?xza-aY`%|lTe*S%hsJcg#B+E znE8z>?&DB_d(qL^@CqsG1wOdPzlMV{Fp|NOOd7=o2M3d+OZzB-iRYqm0CTHMJjr_f z%mh3uI8;t{_GOUPCbj;_dt2i)IK9@;&`=u~9XPxuE?Ze#c(QVVUT1Rxw=Fyimfyd6 z`!-M5vZ?)hsbw>88G9cUPC>!Aujw++9d(B)-NJG9EkN;=xX|)jeGgu|cu|qaTgOgJ zOgu+C0J8ZR1iAah0(n4G=K9M|gFe#+4CjACWj)LD=#r%K$Uc6PcMmnwBy-X)v=^Jn z6`Iya_O7bOhJ+CHzqX~vC4;54nQ6~l at 6k8wkXov)FBdI4dHQq|HH)k|uy^<65e471 zDXOzJ?ZIHp%Eu+$pN@=NH~*Ru3GmJ=8L;!(H+6bA=r5Pk>NjZICRuzxdvq7(D;>VJO!$^&iI z1=_r=TzaT}j(q{2^OZFPm`ogIO5oDtN6^sZ;X{P0h`<_1y>=Rw?SJdU0xNH5;!J() z-2IOqKZZ~fFW~PPgM? zZLN&spz8;!9P2}VOWLZcWT7D;mqCk!n8Xn?v2k;U3z$}`;`m~S=+C`+Zhd=k#AE(@ z{5!8Df%&HV+mkJib-{;Zg7#AFPZ3Iwf#ton>*1AjpP`kIlrz*0A*uFPlkXGBeY~OadL*j+W(7@@M|xP%Eyed_NhVq)P{F{X;>B(%Oa;)TU#q?*Uhst zk)RbqZ%0Jr-BcU!J72%p^wGDKgOMQ+J`Xc6JkTlNU}wK}?V78z^HYe}=F%M>OV(GE z0hC=_Twa^klK{s#8Lz4g4w?sGrE6f2)R(4ts|YW`GnK-zMK_u>p*)BgQP&C8xz3!Z zW+wq`)pGQ#-JzV^+)hM_k0A#b^u;h8;gxmE3>ajdT~A at h&c at FIfKKCf0dVDnuJ?egN1Ma at W$>cFDG;hiF4gMGsL|r|cUT=z_Q#>UHZQ-ev(Z&AuHDe0Gn&Sj@%z0?;ExlFi<7_W zvAMbFKK=Fhy|}nMP)fXfeA%80{n`cwOdB&DO5d$CpDKdIfJx?S8P&LqlR)GQC!u7L zN4WJG4tQ)INl3Z8vte&k<;f2)a?iBdTOl(uQ}o6cCNK|ruIC5HmLG!ObyWMTMMS9b zK6+rlmkX3wk_Ds0rv=+uf~| zkGb}f*v{hg)fZ2f-v3Rv`}X1 at v$$h6c&;=R!rIbt1wbusU1d-MH5~t1W`~lU%)? zym3E%7$ac-%ZZ^rE~yNd3wxapj1A)zaK~u??Kp7VOr&Vu%d88-?=xVxJbr%Ds42~S z`I5i)ATiU_@@T!N_2)zH0)C5~S5X0-m=KbXZm8&?vmGb5r;6j^z+>aGva-I|If at MI z`xoZm)ogo4{r~jzJhJy8G+J$w``eB$Jl+)ZFeOC|oHw_)cIQ&W zl}r1>&VeHL-Iyajefo68yLT2pDqOYz>60&+))BR#V;nl9%5kte zUwc0e$^f3&`-;e544`IfG at JzwLn4*$;502*3*(kE5oI^qvS6oaqZ;w#1N(ff&*~Og zA7DzxM(I;ul9x9(!wmRT04|)ty^E}cFPWO2z@~Lru-D)J@$+YLbXlC=0wN<07K3nK zFiwN*du2V-T|f`<6es(FR>OAMPoD&k;XfaI5;hlGn*r^UXODjx z^mh-Z4!~o(Pk?!<4A%y*#jE1T1Jw1Gavs7b>&a7$JriT62AHMSi;P&$hf$01HLAR-W7 at LtgKIoFXbHE+uPe(tXjgq zsPj8>=8TZ4wC3c-wxi;D!fg0ULCkoFl75p2Z*0ocIM at IznUk&`OXcpV1ptZrXqWF#3C6){~V z#5A0`U72Vl43zeNqh3nhylE02_P1R9({?K%KK^!N6k~l`8;x=Z{3Ht)SE#YTw9%7& zZK`T&^{^YXOiX0#IA#^+oi!*igeYd#eS3KgM4rsXvETIEnt(!e@~_IuN`Zn at uYHxG zjZ`M1p%LHqP*AsMXLkryyp3Z^!3$x(?0c!DJm=-XCUtj+zNAJm3R-~E-fyuUMVeJ?!;4Df3?2SH-<=g&Bi1RJ=4WWe^2PD9nBMv32W at J7Jz%}}s; zYzVygBNA{O9U>Am9G+0MJ~?|Ab_DpVhU01Q9P#a;OM?Ij+uS~QShB9{F{LXy>y5BqVxiA5>X%WH2{{QaeS zAGj-KULFDLfISOU<-mTX3x|NAn1c0{SP{+A%FHfse76qq>JAE7;X2H3u3EWy0iSs`Ja43PB^OFbC&5%`B`t$%Yl$4d|x4ZxOq+`^BFJ4R;& z?Irg*$PsjbZYO?z^`#=PxNbKluf-pyzL<+Dz$0L*Jc!9Stm$T3`XqQ3 at R2xdI{e`S z0nT-!mT#8_jKwp2-YbQ59UXKyV+vGQWg_QwcDzi*XSjQJKkWI+!rNi;@ZZT2;OqLu zjM!H(P0Fhxk)uwCJd_Yfj?GYYrEhGZ^HvH$)azgKeRn}^gI}-D_t7&77+=?U92~kb z-zS12)Pe2bc%k{eviO9A`f|RZRbVSP-II^u+4(4jb3i(K9}q>WYid$s^FwiQag01* zkkyOF5?dyjK!N};W6!Ahp_-IlU*hlQ3-l}id4Lnj!oM`d9&h=5z~k at kTAZ(oC;UD3 zNh#tyu>26VjGco+dEqof1b~`vhZq>xB_)k&+)QALA(Sn{6BgwhAe%WqOaB&|ZPL?Q zYBM*i&-G9MyBdMrqQimSV4Z9SBWVvFWT!lq%%)#ttN;R2nVk-(ZPV{_MwyqdgFB%0 z!{14IEk*;+mCwBF%&$>Lh$98@!bVO`4pt{5JbWDV68Ig at d2MYv3k!?xgh=d`{r$T^ zW-2YM&`AVuF5$+N25?Kfn23#y#VlNGkDZdzQqD&Zi at D(sIMqfL7pb|qx%rGFuP*1{{#N5;qZL(qivi_4%HkTcVNSht6o3%e*@z4d$muQQ1ey!7>E$y{FrCP!oqS) z(mlfdqBU;X`fL|lLUa2oYc(7q1#rlPE&V#O&Qttjb;f_KRlGW#?+JKpxqWXIFt_`c za&QKufNAwVuDr=X{gCV;&V!{x4>G+C at DiAP?VV0 zhYVixkq-=%#qKFvzke(7KC)teFuRH zPERtG7px8k{T)s78u|bl|C3bE=YRgektBGIP?SSPMmD1gP6ObCu@?Vg`QH5eEp%3Z za4ci0NIZG at -isG}l*c6xUA=nM;C-M>uj^+}?O49s{qh>)nPf_YJOJhjfV at l&E-oCq z0|6L5oOX(0Iklldx%&5F752H#QG1M^M-CAjKuUo<{`&e%2SZi6IxN_qoedm#QF!$w z-nUYu#E}cB0D2_~niXvpQ!$H?{Z#uB48U67({p%Zu7_TX|CRje+8Pdv0vehHGzW7V zdLNuq_<6pcn3xzxAYn`Nv-VtkItQ;m!a>Jp&KzWq|99cIG{+oH?1GM$S7ws-E-9gi zmp_0Pdw{s-BqUaDoY4VB!G7|jf-=sW_&1Jm)ajbm_=euzPkJX}xtTj-`G$TSj&=a1 z^Y!ajgHnso_I-!ikHEWOd;l647#O(qv-o{Qq8?$6P9*FHCewcV_Kiu#SM(q~JwyoB zI$jzUSsY1t-N7LSr at 7UKQQfGSMWHe}GExUo|73|x2Rrx$z#K)Mu_0}9b2E|zV2yxZ zm5I-$fOF3Ls1%7H1MjFdyag1ozorMmD~4cUOA>N&a?oQg`6R{x{{E~9&C8gS^x-Zk z;3X~Xby{<2?Yg98A8D7Bu|}YAQ$2*n1bf>4Y;wH|lJF)pvRidy3`a#m?QZ`LX9=|V z_9uoHJ`wx9f+ii+yUKvG0D;OWDk^%PJ>viEh2iq*D%O4X>VRRdU;pPm8;&6BJwrs~ zeb-pv6Br>W#As~D2NLq|@_5|j#INoD%LR}Fcr*vPW2~m__VXJ**t=1UM5a)$N-%be zWxCxbk0oiXCIooNVg1)Bkx?gncJ z(5HQF)*$#S4uODm13zRJ61seD_k(hCTigFa((?9n21P(P!&kie1R(j|*T*I)`NUpA z<-+sx{aP8a=PzG|_diy0qxGnH{Da^Qq-C&eql=5NCojZ0^M{LoOD+SJer#+c4%%Ho z1?`Q;)U{h<6cDiZOskW0`A?lXMMh43=lS!~n at huO1q=jT<^UPa9v=Adot>TMdJY~x zP6bfx{_`6ZX!~)!sNmT(&}Jc_p*Y}GSXkJ&g<$y&9qLnjwrT6Am at _BVa2A)Wrf(8hCtnsSL;OVaXr0}86 zo}MZ=_7KDUBpI0vhnyS>Cei_fLog3bFo^+{K>~3e=lQ_en12j805&jYlnAar2#l|^a&a4IJ`F1iuuTSzC>GE at F)Bxe4mi#yunxh8?&7`IA4|a*%5giorCE5 zpi|xWLp?ByQ&3RQ)FIZ~Q95v!DLp+MlRJBL_U!{XhBG^{!`r`th`TT*Ak_0oaNU6H zSnP2rV3uYE#`f&DhHH<*w?Ee}p3)14_k>7pe)DEH=nT+ww{>L*2`5*2&4MyPSA&SA zx9~oJB?LIumXD7T_^5FUUHcvELp(Tm{4NRAtEBC7*q$Ahq5qA;>07eA*G2W1N3Z|_ z$PxbV?e%qc_nCH-q^WUj2bRM at E~ux7Q32pkFbYf<#cCJ0$8XMd<)NVf%XzZU;32 at u zPHP)*zw&%ST3U>GeV76t!#JqUX`|QRKL%g$5j;T^;Scsn91fmr(rOfbbaMw at 1E~`5cf98sY?yuhT9lSf zGxePLWl8_7^f0 at TdXbV3jAs5w@qsoRZA_O)95|dK<=E4Bx zeD&y?9ceVRb#-+WvT0|HP-aIWQ(0M=IU-l2RW-Jpk)}W4+5~arixrl$6x74Z$Rlfj16! zYieo?1ve!;=b5r~3O)qH9 at egwrJg|21wjKhmf^vo5ucE$*5BG#_vUE2*Rk+Lhv>bT7yV?4yjOV{s>B?x&}NN{`|a5ut~$s0gap zP|jSh%lmdz6ou^8$o^xp{z+E`CT)eS9e-31+`M^n=vGz}%I_p0b3!m4RL_SF9dhPx zo)97q(o$2aANg>1bZY8qmW=?DUn0;>O`xA=1eyya3g-;NKz4zE`ulQ9}n at Ck0m`Zl`ZjHFamOhV?XUrsXWWi zCp#|XiNpCMBu3AiJo)U)zMIwwA+3#XPy3plK7D$~>j5|!BT#Tm{Ee{_my%+}cZA)K z at jduTETOOO$@i~`Z7FJ|oj8yWPo(zt_Eaxk6fRrgP6_NoxP(2^Ehuz5$*YggqyPzNun_*2a84#2 z7Bs7Ez-}5dGh`r2F`>u%&(7K2SLZ@$w(B2Q6ymHM;7H$JSyS}te6CYIVrH0 at X-mkZ z>#OHtvm#7U8_n(7SKOwzhwM2OVDkihlJ0h0k9U at 0raTD!LjB at c``)4>J)YLfv%iI6 zDrE*9oz_SC9)I$}(H_V5fq!V}=*;y}rqn^e;ykV0y8!ao_44XM{^}b&-q1&n zu0*;_cX#V;cyN8*SNr&+ZmzllZfaVRk%0k9zzwc2_3P9w*H&Kh?(~-1>jQTWqw{-b zGj>qTF-}5C>Zf}lFa3hB!zmt~V0Er~U at VSeJ|)s$21(W2cDGzM6z{EeiZpnAi23KWz&pc at 1;h&n^@~kWmmrhgUYm}wyGCChPD4tE zBN#c07#SEoe)vG3?8Io@|NT3wpkQ%>6RPBYXPmFUdKtxXW%HU^#PoJurQ0+}%DZ)S z^3}c at _v|GYS{|DPMv_n+Wy;wt%wMF7RK_~rpGPm*3wr=yBS1q0bLI~{{O}SdL at p9w zS9ofm%bzl{QM|cWPey_mWZE+- at u9T@Zn?&>bJ at J z-a4^Zok?yymagqM>ofiJ_&F&l%P&d7T-p^bCP!xO70gajl$Mr)X^{w?5jxDJ at r20B z%S+m4HH7{*APXxy;v44M;b0ulbpWxu5NYsWwg0uP?wHe;#$v8wA*EyB_k|+S=5Nbdw{wrhzH0yA4eZ9J=gmYy`5hvN#ac zkiXk;;0-^Lz9MU;=uxGk{1#<#11+t%VHBx{_@#VI~YnWDh<7k+= zZ{HYzCmEDnCs+~>F6U4`8#h-Eap1a>6HdM;%6XWnaF+x_Au9PT at dF1A5YUYR2eQ`C zx_XrXA_ETK-nV;HYvPqzMuAgbspSP)I-}P%w{`P0v5aMVdmD4C0n$Sg`LCUMn&`82 zwbofUo$2slVu0Wu6)yEQV&$Pu!{Hadtn>lg6WQC_zc8-s~Qla zniwf5sS4xK6r~@zr(R}dv16eru-vVkpT*{umiyTiA>c+coL>8o*IFa2jnb8;{+d#+piB}6O)sfj)U)*&RD62&K{YQ zno_)RgUjyQi%6ffsUxkqxx1cVy&KhpmT%U!FJEA~1SW?fq}1}$(bgNFe~ilQLcv$Y zz)@AouF<84JCV>m>bvz5yquLCbu&m>BD&G}zB10ht{OitTDq)f&o=%aH&p~iJl9(s z2g_X=c&|*}@ZiCNcx(@nley7!)<08^cV(iPp7Pi^TICQ>|3}V;bMfbX$-bD-USh6P zCT+e`wN!gIA%R{{j9u6|!(2(K%l0aGk8%ir#^aeho+U|rY{m`-iSt`QuYETEnkoVO zzv(V14RGu0gfEQB$vr(iF}Jc;$KZlxq=}wh2w=+73vnl^d92Dmbn{y2cC~PeIsPDn zt^QFPkYa!8h3jl5C+u#jw9fRYkU at wm}=hFcLYMB<=|C2JKi5ZexMBg=5T~rARzMeK<@n@ zBt8&8vU77^IjR-tW%>PD%-7AMe(>Od!P}cWxyFGhzPk-m;F6;t^zu!r-drpXJ&gwd z=H}+cEkaDX8>ilo9XNoq)+-VT>Rz`jbk2fb6OoeMjlfBdQH+9T65j~V_B>~l^q8|c zbge5-E4->o%89{df9^HYq{79{q*b>UrZq=_ic6+>axDM7c=6%~2yNKW%E`$IO%6L2 zwH)LLJvFUuf;Dn!X~)6a3swR_VbLBw916j%?Vrz|KTe4I>~0AIZ<(K;UpGCM^D-SM zLOq~4h at I;H^G92IdkXfk(4aX&mKH|KSZ*#@Bpj9eYteczQFeLdw at _%M>;T}xbxX^; z4GqfQ95j-I2ow|)UJR6q0y^FW#^cI=*990GNcpYX9^h;>F5 at b+bjYYSYBbX%Z%32> zX^t}N#sjDBVzUg9-Te3+IQZQ<0Nuf4psHr{ifp-}P at NNMs=#ee`%?yT%Mu?+7~ zBJ!1YP4)D$V>Qxc{Bj>VnC#}i8aBEl-Np!JaQT at E6>!`z`%?@!idz^6`?J2&f at Xjf zwxeIRMp$hK8`FxL1d1@?$7P$*Wg5ysjdVu3_{ynCm}9-qIIA>~C4ZW#9V zt$4Q1DZD%w4QCnqC;lYoy0q`QYRz(%_xNUixxF1TUTvA_^Be-lC7~Pbcy_^h-O9 at 9 z`quKOv#aYJP#R!yhGSm5IJxlHF-ibxcev0(i_NAp`@mFNhS?3a8ZQ3+?#P|~4$iit2^b=K zdl?}fz34vzb^z<}$!8>QL7e3EX(^A$cu{Tb^!zW=<)ts9ijO`Ba`!22QY_HJMCI|O z7|X$LPtMwp%+G&$-l%)n{PUzZ%Q&|~&~uISo;R)8OCl?wRbESoKJRx|fr2uP=4Avb zF7%h59qM}H|AHs3dD0b!n&SnGo15;E^xwIj)9VWZjGLRA*fbkV?(@P(4;M?KKFLgf zV*5tlHj_TAA0?35>b`=(hb;?Zv$ftUgc>Pg#B?}bdQx+~JzGXtm`=ubJ-DVu21xBn zGj|5ajD$x7Ob*^V>!+;F9Qm12J6PeeAHXKnK|udRf2~)*ZuXhKR$1!Z8xrOGZfIrh zvpQ&H|22amqo!85b?b%clCu8$UQW-^2W_&yDO;bMJ>9W%`m(UoQ1u5O^EepJ+uQp( zFvx1 at oCuAOq1NQtBYT;9gJ)*GLZs at B>@?2l+!Az-b7J$vyFt#e5CI7 zi=T!S&TJ86@$vE1N3 at 7uFZGhXb{ao(R`Y<(<6b?>d++%8zU8L(iluj~%O=l|PLNy1 zaDxu`gx3GlllSgRrkGiSS7(B{#e&WZlA0t7Sa#tk(WT)G4=JELDtdZ{o{PD^94`Ok zs2*1!)T|}<2QPiDc23><*<1Y6_`+<|=bL~C`;*m4fL%;XSYfRUd%QeX0{GtJYqhgo zk4;k7rrYXaJ1t6?S`zv9KfUJjQ50}jFoI0n at yGR^ST4X0{B6K at ktanG4GxE8F1Sp6 zO`~A{c!4EAZz{dWbKT*W?f853)_~IPO{V3)jOPFa)2_%AZYIg547R08&~NgNa33zklD2xMZX2(12AyX;NNy at +ZPBJQg$~q02O_ z at wM(0xoVuX5TGS}&Y$Q%_~w+L6V9ns z4gsEJw=3naM#${oXfp5C7cOquL at 8?9*}(HA`ZkE51TKwb_zX9G;(cX*YSdqD6DBR3 zy!>JPXyv`3o7T3r^e0^#8zSjDUKomeo!+E)m<=>_rni_S at 8}6>@8IQS=kEgpm;rH8 zC0brZMJ2Q}Gt*M1vCG0DFr3YH=2`O3(&FA?v--xyMjK{B*YnApWgwSPk< zObDl80lZCG_{sxxn3erUl~c$Nejlx=LY`9W!N8p7WRgCbsRFNG|+WWvMn8 z-%}glw;@wIorX1cIGE|V*qd8 at gaEEqH*c2NU10od&tWsbdMZFe|NUu`^l2cI0lzmV zcl>~uUJ|7}Yuk~l+1(o}=95SPd~|evJ{n@)s~aadIf=n_tG^%6OmTXl)@NvF)EM;a zfXS+|ZN|+`eX3Tu{%U=J?^Rxcz}5|queW~rqFip*xj_-D0Ne^yL at e*EFP ziE{J$u-h{W91HO$FZ^3Gac;$8FM<&a!y!V2XuDk%bZ3mqCQks_9U(K#z{j0Gym&Rv0o?%D3nwlD4 z5pt6jdNFLM314eio*4<^;TiD%Xss~*KSLVAj$P}=o?PaC^=oBC+sx9gh*EoULDppV zab%@n;`b9R&CLqHO(5heD05Cx&E0w9tG7 at J?>u|Ua(i_$|LV&peG>{A1`lXFXW9us ze&}V8n;81y8K3j~cVVFHfS$}fMIdc{Et+^F5509V+mPX!N_^xM*Sz~FT;d$aHyq*h zc|p+p9vH=ujrD6LTkBm~3d$%tgJ0!YF%EkxO^R!4YqPRPpO7(1ixnP!&}s9oO<6kk zr3%-y;2N8mO$&F1zmCO9?Y=5(Jq%&+Y+-9>V4fat{KJRYu?hDc6BPd5nF#ItyYAp7 zQj-|00bH0g>w$fQdNTOX2Ve%LPMtDVBdGHaD$ca8{XX;m0Mtt5qE71)wt4}Iwnx(w z7JKt+>ZrQXrAy at 1)zzQ3Zmmc#HWs`XYR at yZvLM81MW95=aa?# z_I7<}>7L&Wc=vRU_V;XF#`5Z<8X!3kNAtgXe`bDEmhE^wlDIclTU;!tnR-t9#vNZE zVK_>u(l{0%qS|{!QKnA!-CduV_DoiGKC at cmfR~`5ZN9xY0y5JmYb9$gTVSvh6bQYM`6(eqrNl+L^@U1qY@(sJ#8CDK6KPBXa$mr38cbxU6PpVro% z*(E*MtIx0UW3G>nkB|4t7%@)ZpryNW=T7(g$+95*L+(a~hL6bjw$itR{)-?=mHCBqi;t_lEqIY}J)ABpWaP73-e-dEH&zC7yG?Od%BA3iVU*2K1`>X$nXf~K>)sD#V%8ZkxcmTUyDK#s-wXXyg$5& zy{r1_Kpx|K9ga5FSTFC9YhP(Mp%r z{+lQ`9vSTTXQ%&-OEOblH)k=>*L8rL=s<36Ze%1wo`+Is_>tBt$||Qd(N_AR#IuAR-_lB_Lf&NVjxLhjcd_y21ZkKF|Ap-#7ku z+%fLG-gD00d#yQtvG!VXPTY9ZcX|2T6ls1vD(rDdUA>Pm+4lJfEl$#|Y}#K-h3=B& z^|@?@@rsO!iL%$eYZZLtB}1<_eSEf#4RsbUI^|?!hRkxP-7xipamf?C{mpq#h+nc= zwYAbR-_g~Xxu;FIz;24oC{#9KdN_LHw% zi1J50{^}f&8P?Dhe-4qWF&r>AvODGiMV60B{89&SE~>JRds=<4R= z3qI$MkB=8LuA_laG&Y0ViJ}Muaks zE15X0lg<5WXbfZJnJ|q at irxeaVy$mocn+F z0`RJ~D^WoZeW zU0mWokfKc&mt_YLjClGCdrhE?hQQZP9_mqSnn|l%4O5-LgoMoclidLu18Ty!)5q8M z1VKnYr^5e*SraALazn>Ju%I$uqn-3_T9s#)8=JnEi)3Kl*lX(St^Fs*>Vmb=3sAXe zq42y-sgF0!>`G!H?DkpEb6=*=!P+j>DE=L={c8LIWx3nnsxLaKs$~3ZobO7$FM)8I zWj;p0{$0-|ZmffA`L5`-{e2nZ<3@?_6#wIiioT>4GqBG-=e6=JnRd$N*X147Ty>f) zM6!$;2>~Q*Sv at gR)6 at EYej2ZCZoc*OJeBk)Qf)%w^1}x{sk_9=0{r~1d>jVuQ3?p$ z0FF|xAnFmt$H)KC)qXB%!V47OYag^_D}GLim50Dr;_M&8h)zdS$^V6#WD&HP32g}M zkfI0)QyH6>K=0F2Jdz&?>uKv5uYE|ec(;AZ4tH?U7s89(wv$pl>smY~`NNYf9|zT^ zT;&!g-sD>iPWZ8xKV>~aJGf*+n7=1xiiPg$gktOJ!Y&3E78Yg|H;nZEcVXf|wyKbg zsgcpIy1KJq_uy|3(+e`%2yH&oz&N~CVU~=4;NN69k!MUGVs>EqoBTp#;yrWO8 z0a~+M%Wb8z?xH{2^!xW&rAEnt{ecb!wyquz+^7QsndR)xmmt0UISuRP%JzISmO0() zZhpKe at c2Oi(=2GKH~2}3iIu~{hzn6=f_Uriw_{^mJxhiW+{tz6zG z=OT#1tpCX=tARWVZ`;W_TZ2e5IkOefZ?4~s<|5en_Rp(uls%9ibe%pLY2W6IL at PQu z{r*Y24O5N5Xh)qqFWQSuNqtk;CAVg4!u{6&5mNsPDfqX9a0tw_15DXIGs$@D&oh9J zwU3pgNtTa#Q&4~jr{v_}$(0aS8obCTC|J@`wmeuMPD^MueBNz$mC8rDEbOQ~R-BR= zm58bjpg7m at HwH)VhbHe80zx7p-UX&IvY<43-4M>DrKNkD`P?ehK`G~=-!JC<{Mor6 zkXKZJ!RlTi05=gB9_~9dq`hUG0>+Pd=g6+IJ5?E()(qn;F=uDz at Q*5n$~h*Ft`Jy{ zmjCD)O?CU8m6bK*%DlTYT6{Lrtxb?C{6kN$rp15O`?J54i_CQerZ94(w_urB+Eg#? zyvOqYpa#t&L3hsN|8jFIK-1oudFI()j>9+&opa{B`9?KHf942nwi1Qxxfz=+PNct^ zp|F~iVMB)&`m+t6Jgfky+})iFc=9j?SFY%AM(eP~=tZnWWaHI^&?fXh9&Tk9_Y18kgP+Q~Jc3RY#CROv5D^i{%FC-&*K_v*3vSEmr~Xf{FlTFArtX|q8H8j) z?TNwCvaQ{Aiwe~F3hHg^!iid9?9WuGSjE+!R>?o;bp{IMJ}7f3QHZ$hUeVXrHy>oq zO`Y{yOurr7;n3Rt@?M6%tB~#P>iFD19*f1i#ijp8i8u_ko%!$Dw8cETMivg-)c*?( zXdn8e>$jiMS+SvYMAW-5zMpxS7 at a)a*Ier=<^3l at 0425-`}zt~6#CZR;-XLSn03BY zB})X!s0T_X#O|Q+a;5eJpH=i^?+-0X7gtUHSf@?jW``|3@!4_>P1noL2e%Z^$>(2x z_hw^q(xiHB5&6~L9Yj3T#EQ>bxr3I5Rx1f;9-udSZ84&K|AB at v^A(AC?E2xJ{g&A? z)ijk#AjO`Yu>TPwdNX5yNoup=52U0*pN##wqrzaUSL|>B4D>mr#z(yXj!DOS04e7P zLY_<+wG<}sM67TV1em1PykyS%Mz zyyNJ5Zp$i?#PiIh>siI`?%lgn_mSN1+c!#i-nqN#nQ<=Sqb~lwd*u5`v6&_!AKd8} z7(9JYG5Czctao9usG4+RV`G8}^TW}Jw}F9ITn=|RqeR>zX4+yDz~IrDnwp~XqW+}3 zva_3z=}p>^!mfNLzFi=qZ-+-`x^pKKNAdps*L6H#gv4RmKY#ryht7`DNPP?}D-#Ap z1;2FV&!0aXVD#sXj}FiW%FD}DN}Q~s@(2k|P*PKSiQT{G_2#oI9aD(xmLqjsRh8I} zXyr)slDWIk)=>80L$vR^qu6nA4j6fAaq%HS^u-(f=p>a&nTsRZHBeF_n3$Mon>K#5 zGs-|6SLV92u(k6zHMes#EZAd10NXHa$IQq`39Q*`38;e(DqGBxc>*V1#H!C=>R!#? z&E_ANdXnM1w@!yn_vUS;tENB5)Gae$b=PU1AzRdPT97q=2aC^lbTcz2Cq^GkG z1j*EoxG@*_DQD^m-+g(=j}E88AepU>RVFAh@^@67W~NBh%wuMmPv18L5nLy`@0Q5* zcPdV9%2mhW`}U<^S~|L*f`U7JT>Q!3zP*B^klI at 4iT5R9cntS2SLt~!_gM$^zk__Y zGOrjgAJ?j+tr!2?mQ_=`ZuRG1n^VoYk|2|5dY1_Zre|k~ItDS7p=%O4?TP-)Z)3QXD0)+sz0o;I z35nU?QC-_g&A+@~S{qhxJI8r at pR?Hy>aCBqf^MuIsEt&6TI-=NmQmVX%44DNc1Q z6N~t#KBMO44e42XVmP_7BG0Z at K2_sX@$!i0xPX3>Rf?AI!f=tjcU*k at jgA%`cHL5r zV$mpm+c$4KJsX>vno^wn4By*onW-2)VAaYu>cLYcxw})_IbbLufrp2O4#*;tL-4O= zgsN-Wc34?i*>8{Z#hW{o z0&Z>2w30<|8lIix+YHCXKRlkSzWEit^q4thYG$S`0+*egy{Wl*((RUwq{CeMYagGF z6rvd`>=!RwP#RS~+VQ$XJO at lecl_YY=meqRg8qNK3EKU!zP|o8n}cS2VnWQW=&QvI zBU4kUADVfO>(e%7+tf$9=-9J5W=t$tPiQ41CIy0t}eO1#f4`w2s~ zj!jtCAr>rt_J9=IH?mu|Zk2uvV0qKX$wWVcnTvc^9@o}DY#GUau|Mzf1LBK}* zV77Zk1ZSx~`;>$YW?^G}{Um|@-ukrWpF7uO6A5`OdQo%AqAyh!(LuGy$VmAT7cT!M zVjXSm=Q}GpKmKM=Q?R%Pv>pF=q`9ADWr;^2f_kXs=JNDfexMM&wLFk#$m;ISh$Xv= z1hedt#6=v_-(Ne-`cgV~SXkO%f-h57eIyrbN{AiE`w7vkIjE{sEN|5LQDpBGE_yG^ zUcUP#Z!>^g at GSiB=g(JrElI at YZN6LldGh2#f(?3;#gg;GFY{-R2TyHq-Aqrq`W3%g ze%p!D7JTwich6*NXqymQi5NCb-x-}?pl`mUGbop&SfNq_Y(jP24>d*4NaPShOH?;* zr1seGh6EJ58R}##K2FXW{(>XNkdcNCnf;Dxme!Pe+BBU{UvlBXg at 3OT_2=4S6`4b> zEnGf-K1GpOeR}w-fW2aYwN7uRI^$Jz>IH0iZ3M1+$4nVFxgOUOdUHQEWBIP6NB1SJ zMKi|!uZXz!{5c0YQj~Z3H!9k`zqP28uEyG6#Pxw(@VbnQjCM1Zz}v9*gJEg+Jc;kkz^>FcxS~atZhTp&60r`MJe^_H4;r4K}d z=sD^)0GdAM0kfoh59&TVYj9ehH2aOIb{TmNezGK at dJYCP8cmBp)9xBbYy zw}F%Qj$gBz>J7AIHP@`G at C((d2Wq}Y=zDs8e*UYO^K2C6o=QGeC#I-HuGO!1x;WO1 zLm+gpAN3DLl1Bd09R{BAd5{qwZVGhvYw~nv=m?_nd`wIiBqb%$-oh`ppPZzA^W(>l zeN5Wfsg>apPzQ?iQSWc2yKF5`QoHR8V^CWKd3ijLOFiVR5yvZ`dM{59#EN_4QBvR5 zDOPb;NW at m-i=HOXuYQL+y|A{kQx7TtEy^Mt_3!lvTKK?O1;O{mXKZoNZ}jB>u0piX zeXw$Xun0)LYZc%DbC;{NrM9|{S>ZgXcz`x0}51?;-zZoVMLZ;HOWnP=Y9$->c* zFBI_p{Yiq#)>d3}lonX}=g*&6?gvh2vz>&BL0lMp$w0TSTz|**AOFf~Z+t+Nwb>Yt zpC<8HS*TjE at m-I?dy)Tt#3Ehj8>kDeGpT%YCgn*E{bH~>+=XA8$iW9yGC6~V={qt1 zgI8{udd!AX4MFGe at Mbj68h#+%V;%w(U)tcjA{d`l5v~4~q zs>;PwmG`0SdZdrq;FI?yRyQ}NTAiuCmA(9r8Cv+%7r|rp2DP?cTU$fH3u!cEConNF z(M=S1*J19PyjG-(ZY0L=+5f^BCj-Mdv^TEMmd1iF_w~Pb>V!|7hN4qN=9(OAE8pRg zDdw50PEAi|n at ob!36!Sw@$)nM53Ds~3=QSHm{fv$`gL(^>3{=y)qtf+_s3O!!_q{r&m2_JB=7rrOh;Mqnd%_{y8_Ba+o zdp60)$Q%#V+_uF)=OFYDpwy$EaaX^n(B1f{&fT)d_4P$`j(eM#brM8v zdxA{xd7Kw6E_Q8>vn7-PA{d)l!Uj}g%FNun6O!NsQ$@kGYuADl8C9GQ=JZ|cOGD?@ z?8>K}+;L@*$|&eHS=%df3H$KjGT}uphxtyra&i6R4YD;dE-2|Z?))0l zqexf#F`-*vQ2YL%d?piXFG|K|S%__8(=J?nEsB}%Omx-NQdImUj5$8sbB$(UV|zJ< z<#pX&stB<#F>z}1Enu%WTo)Jh&NzwFP-uaL{ zC?!(EXGg|K;;!tC>BH4(d?QpDf at roRE`2$AMO%=f=fT1}CXU4_%sXT_s(8O}t2jL-ow}#HI}ntt z*!Av&n zSv15P*A4Ie9WF{1TEWCJ?3^{WQV$6U3HtKodTnj(R?~qnn`(u%Xvz at aTxUlI70%}V zVy5Wo7L1RPiOF_>-Q7FOW0NkJg>I(K-rg?bUGCuEp!n<=cB;}+JgqEC25V;hXHjJUWbPCG-4o0!CKMf7Gq5rcf- zyLV?*)zzDSe5e2hpdlwGUznQaD#W>XZav8HIBOpA#vz2?i z?Xdh%Z!SQ1Y4#j0sl;qs47T%NllE5JUu?kga7n>ng@=TMM8S$j(=mBzOu6DYimeg+ zb$&-F9cNe9u4eX%@9kU^+nChv-)Z53gy&Cx{_>@F*}@4kJ8_lMDb{Dr(N{tYG)f}5 zpi at KH_r<=<;Mmx4;R`xVb&ZX_2Zz;FRmaV)m2KSGH-1jPG&Hz$bTry zdkGjMsqVg3OU(0p>|UM>hw2$eN5_Qtc=$!~#~R at QXu>vdyB3LaC7Gz z99zyF^!D|2<+O=BeE04hB()S36_v{4$Lj at s^))r^;1$h)q7qV5Wng$vr72YLI!f3z z|6zrxdNkHyrsc-Tdq)R56=KJPH%ACMg%ZnPou9uX12To at cYaqujhZ)5XNa;r0$} z{#K;-Nxi`5cbsWOeoEm zTbLcu+M3dD+nI{k&5}|#EFQIAUS8h(V;W;ykqJMwcW|H#aymdHc%}DXC0A#+y`$rP zWo0FgLCyKRyYnmhw1gLyN6I2Ct*m5d2|FNUTt{uqW;!$?W%mYD0EiRP(;w!Tw7N`L z?6pTN9t~$!#QY8p*;yX!o3^94bBj9eNsdVnT!@#4hq2X-TB&bQ8na(m#;YLHorl$Q za^m};R}l?<(QNG3>x9_Y`zXk#rX(^ej_-zqhW=gNnQwA0XIKtj(bg+oKVbP3H%p|Y zso4P0jq0Wsg>G at Io5ycCX|3yT#g`Z=mI<6~#gvr|M(|po&j4jk>Gpz^Z5SHT&S1Ff zxN|(xQxPdNqv9g2r9~DV9-cZ(LPMYg)}8gqPl=UjhphwWioUHnjqP&xgF?y4$U8Rl zaVu>^n at N&>*E_xn*iUKJ>7l6-5)zu)K3-_IE;-nlb35-P#>K%wNlZ-aGVVoutEj^S z(>!lhk;$-CkZhs1FXnMuKpvk1wnCvEZ1WNE|e|6&29D0??vZ3S=3ZWZ`(+)b&C*`1TX{Duht9K^= z%gY~?+{tWyTlDtr4-j#T)YM{B(~f;CnWa at ntXTB|7G~zc*}e>&gI_yJDkTZ&ZKA&; zCRS>83ymEo4qQ8~AxsRcBWjD9ds^%}r3k&9?Tt?^1Haj1>d5cMu362cT+uD`;Oex=S z&H39+W|8yR%(Ao#9;C6qKg`_Rd>7Aa&ku+PFmKXPehFX~)pYCZ=}A0s>TElAn~*p- z33?nJ=hZP+-N8uv$HPZ>jg!JI_N)bQ-90@>(9Q47#?12KNji@)4EOdvn5^@USBihY zOQ36DiR!_6pJy90t)c%y~8yTZexskL=l at TS+?k`Ln|PC(ix(<8Mn*pG7b at ng%^C!FWNrhSU*`LetOMsMRe~%Anubf9^ z6lVm#>b{<0UHG590K{Zu4{_$|{K?$B?eHasl}X9SiewlZJBEhj4C_9i3M|=9m#Tvr zjd)Cd-y{`u`Yv{T9fRt*nmv2gju~}N&f>a}bcdIZ57pdk`1R|X!UqA=9aBN{sD{oo zQzQEgn_hVfMx({>sa4v67Es6h!op)iLz>apl&HG82Oz!1uPPM0N+fRt?@mpC?7t}#l;oZ-md8NxsUh|p|X*YL~Cm+AlxGOYH4wC z at nwY)Mfw*nUeFTyGBPko#ZIH0Mhdqhl&`tvHs3N`zO)$7*ro?zp9fy^XF&mWI^{Ed zx~IoZp`%n3YMq1@>o6p8_^PU^L9wy1RR`>752|mi!9ayag;=w(%LCQ=+$mIKkPcuR zTYkU>l2fL at W|Ur8S$QDa at Z4xDhcIn$V?$qG=oeP4It-&70R at GEL3Y^4i&=6o3M8Kz zjdHDr?*hkw1gOFAx12(Y{z*wDp?rkupHbl?oWP0-QTQDU8g|Tzw%zmRiU*!9zPCOl zJd$u&>}9ocbWDsD_kL_(5Eo});H;*pDVvm(bo!ndeV_G6sf==xTVxu1`^aPvZLF=j>f!y|Ng4(alX}{ zH070lDGMG($$9T`m$HC>fRT-j0zEzb=a7(E48L7Zf4?-y7dl)Dk*_rdoWci7IsaT+ zwSmhC3*}+GCr at senVEI0443TTNiL)Fmq36k0v+M43~3CiH_uJEimZ84yrhg(ltynj ztC~$zVqlC=t^wy|WpQ|YIqHz22!eclXtaRpof{b&OUAgZUl@&TXnk|$QbSu?fWH*e zD+y}Q$P}8z4yjemx9&PHT8)=jSn9_yo4(Ao<4cPsz2*B_W6D;s3+v4l#}Jx$S-!Cvgpg8qoJWm%gC4!yjmpS zyh;gy?y}yTU`-vLl>==I-^A3s#o z)ur_H^^KlBy@!J&$Hf5eqKfTpC(Tl4PFxa>SD at _o@T912{kfEynVGz|F&ivR8;2IF zqS_N??g}R3pt727-r^DxOiWCu4D8SBY^8(RPdhZhjdP$wXkOpD2QFw*@MZ{o+y0N+ zqfz_&E)ha6xNeY=J{A)@$g{8{*XGz2Q(rhJ9_{&a4HF0KTbO&Z6k(pN3lkDdSyH0F54$j$Azn?M&@-^ zO$iV_Zr;_a2|s at LU%#z^1Z;9)A^*q(zuPVc?hUTDmX?-tAlFK3D>-zif*Vn(YLqtM zjZ_wEeNiL0ac_#wu}V)F1%&`z*Ja+cl$0^S+eyUPd_A5>q at lNc<<*LUh=bswf0zH} z0G!5~Prhp6irMW_5Dh|J6Nu=0Or66MNd6yy$}ok?19=a}rwej(QSsoi{e|?4>{`R6 zhwIHqd&8`{c~jp3;zkvFv(6k}%+jwu%f-i+1a62Wcc`~l4SW--K60F%ANn#)`d7goF@ zBgtSqnRu?x90Sjp?QhN_#~>pk$c%$=(hjmv5+_ca zP(TO73n90I?LiylZLhOwOM}~oU;8xmq#|UzB>2-O zvHbjeBWvr7t9|n*;=vdr?6n>)24rGBfrHZHrVEZ$9N)CXs4LA6|*nwmOQMOF~A zk^m|u(97$L`_WdPWwKsU+cRtHB#^cdEv>CcKa}sykbRO(bn~XefQQ%wpR_?`@7l_D0B_$={-wZAtO*0A!eT5q$7k#Ncj{1-j;xzsKt>*lv<%sF7Oe8js z{lnp9 at Gxj2N_q8DlEn7#=e70pGIBcr_sD*R&}XkTg|L9{egqD5vscT8sh0n=J-h0S zu1}%BOI?vRd?1P0Z}+&t~7JHO&vw}P(( zwacXcozHf^(onN+mw7t0phiSQv=28l|2s<1&AXPvPDxKsK`Y;q%Fxgd4Fj)5?`G2`l$Z^Qm7~I9UmWm4hpKoNZSPgIc?0gG2r6jqHX{{wo!R&H{L1n z^TjkUzWLe4pj-I-wUDr|%)^I%XrdP8=EekRA2j9`6m$XIOM_-Z74wPNwBe7Dg1dh`gDR7Sd1^mx}30?}rR>F(VK97P=+a`tKN z%QO_m#>S`bG4UN3kr<;sumGT&Dl`7g$szfEX*^dbnvuNkv0K z;xr}NZ#ivi;Iu< ztg5bV{PQOqMgYl=WiAW_YD&M9(?*B`iji&bbp?KKqY>)I5ajQV3+Ug4+yX{=M-_BL zoAA!f3#U(_O&P`}ChO#c at wA;?U1kHhX2`)%{N45LT+xLYz7M!Zd);q7F;q7WK(bui z;IlRexgWY^5ojH%t1B2R!7ko>5bFED1yWq zr5aYZx92`u)cZ?)>eMMjks!`~lH1(SO-M at WjFa?(k?RQKH2iFCpEW`qBt3^p(63F_ z6XB5FvA%f(++n$fTg_2wu;KfmG^NumPJ%iXQ3YB|>gWifrlHxx^Hn+tG7qq?VPqs4 z+~E91o2Yu4(;#?Sv-WTIKY#jEg<)i-VPf*#+}c9-?B*T!92f#2$wge;X~C;`Yw#5$ z{&30oR+Kz!pU>AC#wnhP_dx%+gy0i| z$Dcoclx%G|N5_ZymIn*wkuHN^LKV>60r at O*#2$P zdC%j z)iFl`_$)3dTFl&BZ)R5pWoT1Zw at Cm=n`=KS|KFv~OT=tOzV$k&AC{e+9ZZ!3%)@Q1 zSEKb``EB0}4Gl>%UhM?4q(W`RgDxi|gv>{14U<^Ygk2*-SowyNs7PVY~@g|DxlzWZHD;#{4bDyvQAD;r(eO< zkd#JtuUyN@%r*mQLwhQ_IVt%1F!doR)|waK6MM|kS}aHO&F37CvOVaAw4M# z)B~2FO`b!I2G9 at nCP0WxMn(p%X+ki8Ej1$}33bN#;O{SU`+65V5|W?*(Oh+8?V()1oG z0_!s2%YeoNvWSVE{v9 at uBpU7fMiT>nia3_5;~sTTdzIHE8FlqgJPMH(48JTpHT4^a z_GfDowXz|WnbKr;tZ8AcL5D2o#N4+Dl%_>5XM!Q;z;msUHVa})#SQB}PV=69r|&Wf zajo$B^=mipN)Fk17!4yUtAD*4R#%^wSI=*N(wmDQ3S8W-1>C0n|I=4NHNqo1OOYQ09rV6T1X at XqqTW}tq9J{WIyo at bvBTf z#~amm^aK5fDyn_``W1yV%nOrj!|R#qrJgF#RN zn1H3rmZc5nUI%N&2swZpPDDh6k(rqcupSMlku~xG!-q05v8w56K_F$2vxeB3ucz=> z3TC*%orap3mj69=g?qma{}X)%^?``m-w*{qX5?qHABbWQ=nfITukQs!vddq7RDsxj z^j`sirpRhAKh;Pp9t;@T#0Dan1_!;T0G*cH)FcZz)7=0MJlCVHM~5T_C{jtk>qTfZJ%RR`>&fc5~RzOl7_m)uD$jbvOW+tX_L7sQ|VcsaF6;iw03^X-KA-4%|*4Nfz z!Mf#O?@_eF%pUAu8NkY)yvJ?Q@@;d|p}g9K5yUrY at C-8sV!>~~%BhOx=TT6}s(l?d|QMrD}5i=dZBOpUrnBUL_=~!jN39-u$T)|DXe; zNCW&=d at c%Pv`7k>0atx`Azvu4(dg=}Tk>jZYRr)wqwVeYM%^}02h^Z at l$1c!`)wY_ z)p!q{x&X at Ix*seTOwG(S`T16E!>zIAfaE^&}?sHY7GJ6*Rm$>J9q^rKW; zGAOLUS)zgSINpe{Y}q~mgKP%(hhEWCd*dhLi|pQGx)W>k`618(YX4$nV)6ijA=mN` z7uq!`c>BR`M4^klKTy8a)zu*l>=dBPJwHF6Yt%?c=qrJe=&sWe$tV?*SG|1aN_R)c z{qgZ})PI8Tq8E=*!xa~BF;A)(?E?T6+KKhtDp?!ya&qEXwel4m9eGD%Q*$9)(4&fP zZ*QM|MgJ&50-Yp_pV!TgyW-wU`0k+8ylN}Vn>#1*RHLR-|DK at 0o94tdUbVm zX|G;1`}OJsdhw*>?930}-5`&C{9-l=gPLD}BtDZ4KwU86;`cutRKO7g)77K}g5uFE zuqH?1SX^N4A#-k)}?Pk7`<&)5*}QkOyx^&&taBZ+&!} z(SK>l($2vl;pNeO2Ru;MEKy&H7=3UDI0dxE at ii(c#KdzO^?>1S6RQ4~32~6pC-41p z>Qx-F>;P7EAIZ^DjM@*)M5oD?CT8?Upw_`Te__>1T8}9Kh?}er=mKr|=xBe7266x) z4(k0v*Tps53)zDDN1=6ph;W#yuAj!ujKEo2CEah~yJ#U+?$IOE0Bo~EiXz*>>y37? z!#{BiDRg*rggT93169P#%$WYgzP0slE7_kGeQaM&{dx{#^{g*LXDEq!W at ZL?Ud0zL z1V&>yFRJP2JOYErKtn^*+1r~0AeoJutrDifoQUxF at bK_FPEm33V-1b4rr9%F^d|@i zT%G`DRaI53HnUfJ%gVX}QGOl|uMUD4RTPg^>>rfmlKoyXs7_5uF0oL5=2tlHbI~(~OQYNw~TSPE1Uo&Xf%e(kg0dFeE3z?43V<{#Sxb at KY0$ zI!xFHQWkc0Dby7lK1oLAx4v!{79Os at z4Qkaq`G$x2Q=8IK64`K%m73oB`JAkG?qgM zH7_YYSP?`ANK-O0>OS1yUTxuSLp at rq_)55W*AFGe$DdMCQbN`Hz$|kBER}8#SfFxq zkeRCP z7B>U9b9BdOxKq;8t3g1ZN=T^dI0XA7nh*#xRkHTG at 7-aYB+=}U!X zE-rV`#zz=~NszCVIc*|?rkQPA;g>}ZS5Kib=_EI9Pzed$%*e<9aiKCWIEV}@>JkIC zYZqv942x~UXvl(WxMfqeg|;=Wg3O(O*;9|mUIuxN#^B$Y?N!idjp#rVn7pe1g6`WQ zzd44ff*Z3;+is!Fro6nosN?I1^CWJ6fB!0ZgeDcWgrwv+K&+Iu_RW*0PN74>AIJs2 zVB1|(!Eu2jLfeSZF?)E()t#MsKy^-#U>Z-JtdUd4zkToLhZ?ScL`02aCEniPHhBsI zjf$~>%-qEb3i6zpF+p{V(aA;B7};f>u5@?8kAsVALXam9by@*=G5+iIDRdeuIp*bi zkP_?UUz+w7we#;$q80>iJUuyGw=FM&biiJ at aV8DIX1v-PHRA!WJBfo<%~AdZ(?2qB z?-0o-D!Ku1(+>;+laFddCO!-`sC;v#y{~UpP-un#ZGVLEcTMX&MBRcm6b*9QOaC*;!;x11DGHfK#eT4w6w~r4OhgegD9Dp zE*xwxR|EYi>gfeE1~Z_}^Qg%y0Lw0(QU)k7f3y!5J|u2nKz$h(m(ylMNNB5Dd39?` zHA2R0<`^OdReeUEI)``jo{kPVd`d}6>k zOAx?y1?xw*N&T3d0^E?Y?J at tK(~=~GDFDOp&GWMhj)-KcRkv7;1_x2T7=o0}VlNefBdsCL?q zAGi=XfZtLu%AoEce%C;EppqB2^|cy73r+w`^cUDrpcWadUv*2JIZaw4&H!zfi at RB3 zR)#9U(yndw=~biM%$Sw8-rna%^_jZ=t?9^gBs%8Q537OTw zZ_cY at l>pwT3^&2mt2Hy;`+*S=q-an7-{YN%j{bfhKshle~XcA&-3mz%?#vO at H?+k0>gRQ z&;G6iP6U|cK at FQNEG){al{M`YI?D$EY=8?Yb>5yidE&nvtY5vtadvgh5gwAS{hz%6 zzPGQ at 1~&!<2Yc at BI-_*rkD$aV7`FilKOMjMX%D?CKeQ42CM zGE`6(m2#}8 at Q8V``r|`ddb;iAoDzDx`T&ahC_&8F%F0K&hhLro?z!`&=KTCgSn>B* zu&TN`=*yS0V2b3zxxC1pKkn%|+x-ai66)Rtxc~zYo0 at t76=2PMMiA|`F7wRHjEa*p zNN|luRFpz2_6i#toA$}%n3x#N==1&E-ETqAP5^eJbOSqrG62Slnu7!FsBiD>oe`9q z5xJ`{?dR)T=}W{A7#Vp9F3!g33dH}tY#$ww0As2h$w5bVrnR*ds1wDykgzc7l7f$q z9~Kd at 4p{OQlT-3lGvvPA`Vc}bP`Xz9ND>Y6S42F+?-*vvx0==PFK(r2b2EH{+=mpnxR?W8%R4L07IS3U1TczdX5ZIxE z0C at _%A^_6>Cm&K$7LTv4DU8N_BK#1Mqg0{5rdVfkBql zARjvA4(wlBalFMY7s=<3&01*&CZurRB+zvpKZ778iP+(zaB^Z|WoxTH_BqJ|x<(_e zn|B?~qy2-AA3sK2L#CTUbN0SkQ&Q8?(dmPl=YKwi12Q)Ygw}VRlKeJ$@+KoLG}x%Q zJj5Co7Z>G~^J7a(m-Y1YcK7$I>g$u+GAL9rs;a7JG9EuBM19lYS^piS=K#fG?P=h& z at NBeqXy`+GyCSNkQ|P=#jf%1w)Ly);TS{{393IH)kf6{|Z=Dh+eaJW at P+<(Sot8D7 z_LFbs<>ym_$N>OBkP0D*XJ}}MhO09{2BeYcIlQzK5R*WSA67s{ zdGh2_ef>&IN{gODwDXN?*J^e69UUEuBxE3I;1|S|m4mU0J!)1~te=8|i)wm++2@G9_wA5$mSMTB}r>dx`+FsgFh!*l47|`Uud)Lmfx4!;dwKx8(V2emBSRr)ojaW&C6*KN$Up7Q5um;XR zZFxX9nir5HCMKQ%S2UCw4$c9*9IE!3^eD29ujMJpyAyyR#sPvcjKPhHii)p3f4-WK zkT3?>jUwFVe13jD!jHEXh&c4CPUl%N;QW2m|Iyd?zJmh~0RQvl0bUIaji(?=hHhTa zYDpsT^72BbTOf(xFGWqwi`?AY*49Q%pP9 at f!`wvyX8Uu^hyYDJVH^yAoKbs?fn2 at B z$4sDT-q at 7yon=snu7L}poeYVOQdFHJYNAve7rL3i at 11QE-qDWHPOx4 z?5e1$>ILLtfZ$!&mG3qy>mHs$YJ(9MDqIDM0O?&BnG2w)yn&s!ee<7Udiwe%A+v|h z=FiX1Lxgw&@d^nEp at PnZ#l=-Hq3_2QSfR*%HZUZ_>&utxB&4Kv zcSe6p;DO}Gm)5(_%)(-OeE4E&dYTF{1xP|1h7|jq` zWJx+Z9|MKyAMUQ98pvlc{cNFa3TaKJovA at joG3E6$p65n^7hsxQ|n> zLh_ at t!I%orAFqpw1P%`mIbI$*qxOF-VVvzfJ=22PA-$ju0pU^p0A2tPA)yYs4&(zW z|AhM1KyC}`I at oW_Jj&1~rl1G_4G8e*?CktJIdOk?_nwqga;S&k%bxD;F|Y?9;Krw>ru9*$xk1tJ^Ydrj_tr(11Nbm9HJyYBK{O3iO9lK3aARX*gF3k=C&}pt z-?!@oVFX~BkeEmXRs{kTT;x573<+uiOH0eKsVUUj3ALTAt2 at ib$MFP zpEbfTFBmZ%9RnWU<^y11Sm zg@}j<@GI(4HTdk(F8~j8QWQxU6B85X+9>Io`MJ^f!oHxm0@!j zM4QE(ocPf87Hg$F0Ft%ME(ODZTr+cSwjr2&xEX|1KJVYZfq1-rTjNzvx_WhOtrvFn zRw`H|02dVAV88e9y!FHC!3&^XJg8JXfG7RvIL8OgAlXFzmxuhvyOa3HwD6V`6a*R1qMm_+bNlw~v4w?;ps06ucTtlj z06Zb7)|3dqgTuYqnB9#wQQN~^TOMBCN|*wmBBU?1wY9rVh{{#~eozyvj})S4eeUy@ zFIAnLgov)Po}K`B+rR zKO>jFR%Q4&f_AxLLFh%MJ!T8xF!+Iz*0lTCERTULVDykEJUP!m< z>r?rhuYQMHV#*)_5=KK)bGz~5kS7dwY)XocmeE(ZV;DRhK0f47S5{Vr*x10pKlKeQ zONfs at 1!Oife+Ct|&B%ZlI}2uid}0Evv!F3>`u~`E?|82J?|=MNNg6~$MF?ecMK)R4D|>|O2$7Y&GqTFc{GG$~{`|iFxNg_grPu5Ed_2y%pZonh z&N&ZD%ik4Kd*b8c`=vPQ8X82wgkQaS1z3k!R- at -dxw(9>K*KqV4PMlI6pmcFxYtUDwb!h4~^(a!hvfUbxT?VgSzod63kX2y z1R)0mZj?4VJDWhrP&@$f4teW3pN at _Wd_cxx%ubv^&cwvz?&DKk;c*yTP?`c_4lEu7 zfRT%Vx_WvqiiP-O?B$3o&j#9q=ffM#>B-9!K>HQ)LgT-7o4A;M^<$^ z_`+Ub`Y%F4s0$ar-U8a!9yfl)H$Od{9AqOf;Pmu#6n&yTwDo6ozq#4=Uj4!o+>>7+mPRU3fKnZf>cgxV$S}N{b?x8Zxr)PgORK70 z0`euo?%*I9%9-oo1YGDNNCz{SeDxzHi<* zLA-yWrw8hC4}cg68JRZo>!m1ALL8cfe=%wX-6n>x)X~vlGNc|Y=2L~2V1mI*{hWFX zfX%|nI#5*uE{33WSaosvqXO(JS^74I7s!T~ z9p^V^IeGFVkV(W4W#vp;<%2y%)`!70x(6h}+@@7Z9B4`%+s6wE3(fS)kS_rMghy+y z6bM*NpW^1GKq at FMj#Cs)AKHtT%i`V3g at u@p^!D~XTRr6n5nEbLu3{uRHZG2lkMCGY zN=o<3io?uO!z+mfqz*IEou{W(QS1SvZELUUp5y!iK{q1F0pFanvw4$KQZyew-VG56 zU=EYCRaI5?Ie)qfEcUmjsfz)X;ZQGrjf4oIZ~&)zl|Q?}_qH}-0^WX$GOxeCbbS*3 zL9DkLkIyY25JLU^Yi;eIWo1)ya~bS%zH2?~%FC3LbD*hGE%&SPXXoZdfBJMtAys97 zSRQufo}wZ|>3!rBD@!v60Vj>(ZosPoNniM>e~_-Wt}b6>sNp6rFld64!}`jX?JFJd z=IP(Re`{!I?E-0(ost`tn3#yu-Bk+AabidJ&|sw}7|zbwKGll?#|$mnwBYx89~Rpz zut|ZI#_Okj7KZ2#76Rch zqh~?RMUHJ`qd3f`!P4OAQ*I??p(SX6WpT>Z`0M+AbQp&^GyZ&lT3c#Msinc2nJ zNFtyPACi(hL7Hf_B|orpE01UK5P1O{+(_p1_NWR7r{914a0Xxmdj&~z5Qs4tEP}2f z(>njVyPlq&`H%eIuc+ji?0`20)ox;HI{3C- at XD2b*!$GySKf-B21m=+c`kycwtv60 zy86NXk`Rx;kP!Hs7q*!B13JOQ%j at dl;URJE`4G*VwMUmjTCk>S)SEI3)QN6{Uz$AC!YQKOY(yT zmp)@4l<}*8KZ1qQQ&UR|Z#};xAi&;g_wfGxqq!&M#(%`&P+{0oAmK=@K-%L7!knB@ zffh9dRaLLouMZd*od&uMydG8*v!|n?(;MyG-6tVfk&_VFxp5v7aY;;M^Yi!b;kjxG zGU~|Dqmd~oq~KJbBbE$K4-XEOQLZdF$+6u88b)^VlGD|Vzfu9baeOhWN1~B6$@(y3XQBROm^?U(_4h+dn|aTFp$w&Lp42P6FKilwDJmki9A`qq!u*179WLIWTtwD;=FZKvp at b>%pM5vKzfg5Yqv;?#alME$nWHxatFL z2e$Rxed|WSgPlR&jkL6)bIe}UUW2t-&^2lK<`bNXZO0^I8IHy zwUiKcUXoH*oy$3S;R!i0DV59fU3(t(mp(s!;mI2r&Qd?|)A1=OHw_KXVNb at oGT3}= zt%ORUC24!As_^5-XW&`#8ZHt7gM&9xu0Mp=rlzJoe&WR3vNCt#hy45?>WcA`-|@P0 z at bdi1%fL%5{!mjIePRc z?8h}--Elz=ac5%Ef?x9~4V6{$^700QYI|GT+D!K at gzY!xKg{3N)wN^I@$=O04;DwOgtAAoHU9((6 zTl*zoj_HEc_Yc>*OKRV9wO9)lTTD{o`l*Jbsh4;|>@p%0K}55#u;^|iJ#n50!jsD2 z3IsnyA#9Yju=O6l%uo;~gl-A$6Q7(c0*j4g?*9GV339dTufbDZzkZz;V+bT0uS$YQ zj7KrgpYJ$u;J}BpG{;8a)=8|uo|v4Rv2JN?jRX-31RK?7JMqNr+SG)E{J72UK_pin zR*+yFNJqM+%4);6Z?6TKR31$Gd~*g;%>cv at g4C$M5mB$KtSpmToZufSgQgJa`Q1Wq z-?_7Sallwl?-gz$a6@>AqPuTU`Xnaq<1^~M1{^3=wXkYow*Xz(*SMX}3E;@x9G|T= zlO~qDiiZ(AItJ9~Mn*gT7`p90OOuP-y!Pd##2&AncUD!0dtI(VH~2zt(N0BtwIGtckU)bi&QT&4?h7>k9&x zbxTe at iC3J;WGos?+B(OLA zQ&V2Y)Sm6os`fUV%_KQQ!E0e~*Y5lj4lk={j+Mk3bG%%Q&S9DU2rOHJuDX zdAF>rEM}d+yzz#5o5kNhH5#TyrDA1JYh#JkvuBqW7#V>P?GhFi1_larj&`i~vp|9Q z_ at 9;;!Ts7u~;zxPp7M^%i`;*c;E7WrwKoQf&>@{q7;yv z(rPZ90`x~ll&bdm6_9c{Ik{`<>U2g%MpQKGwhI%jAIuo*{pC6oGiwxUoqLMym8bXb zCm42SU7u`A8QFMi*p+j$&~i!xurSy0{$_s05&D3D01S?oFJI2?EQ8oKyr`K#lWaqG zMO9U`qpM5baL>EWB5TfDqz at z{UR7B&y6!(VY}Q>>^|9 at mck`0Z=g;dr>nZ`gKCG;) zAWgrI9kT^ZZ2hK^L$#nL^M_>L_4*9Kf`re`QtOHSaw;XO~a9~}YyNJ1;VYkC{cRu5iR*~&eYWLz9LHxu;PDx40yn=#S-s`ZkCRSEDh9NtHMSK^X zfxUgb<^1j2HzgXfZ5|LQJ;n$;rfCfHm*x(Lmk^Zdc2fh0{Bhq0aWk8P__dc8FNa(b z1iX9qK4jd{)lKLoQInH%`SZtg_}4E`86w5k&$6=%j_)EA#xB$|oedC_^|7rp7+7qCBEF2u7IBDiot$=g*tW^a^_Y8Uv~n<_qBv zrxgARg$Q-C?)|xuzxTz&;@av%z~59Vm|Wr)*53sL?4c(DaRUcnV0*nDvCiuJ5m_k;*f*v}7X1APKM61cdIp9Y;g1DCy1{qX=l1thQ4G&LU=)5j*UQto8rv2i_j~~-NeQI7lvHX{(Pz_$Y(YwGB2X*mK;@4u&ki;vF at Tnx5pq)|gvwMS{`Me9|o*v4BP^Ospi9^(GP zdq+hv{43^SX5M2p-JP&-!T`t+fKZCpbeMpZd{KO8I7hY;Dh5nOHG~P&eETLMDoXt6 z)2HFb5NN7C+b-P$Oq%=$M9-A at C>Z3{HXLc at b%F1OpnhTcM7CF_l39>BjY-L48CsD7 zA*9v73xHekntC-yGttn{fE26S)g^v&yj5UC+`9VX#oFIP5_j%Yh6~!JZGCqjSJUZCz#kKH^OhQg9KF_kz!*UXKTl0fy+SSH;2_kZm)$*j&z@@<6APCwpZNLn zCt;)L>SSkjQn$4N8-s5AF3C4cum}3>saW|4(1TguhgwwCb#?DN?ewN|&3-jd z?Y86K;4rbUc)U4;mtE`xREHQ6&-)~;g#*t%ZI`BXL#YoXM8srg^8!=U06STV;|5EP zjEt;0f6bPT%CIhR>0MLK?Fs64 at GZHXx9a}<`EyTRo+;N^chXJEzyK4m`Q)lp$r(1b zfqu+97cL~X&jiIs-nx4?fVuE3 at gRg#(Uj}o at TL#PO+_UoJ-a=(W}c3JUXSm)+Dya< z%Ok9$6kJsr3CnpcPAcpIG1qu=EZ)0DW!D=-+XrG}woTa>EfSkEfcY&droDUj&KbxG zo&@vo$Zmuu_4d9C3sapG5LHl6;OKlmu at j=gh=Zi4sOXcXDA(?*N1Q^<5;v%LbxZ?H z1K&>A2geA}0!?LsxF9V}ff*xsr}n+#H-GX at 5*;8V1(q$Fq*~qEOAGGpx_tr3%b1wc z$*P5v at 0jk`B=<2(%ieqNzytm)Km7q!h4_l2wzhVPe$V at J%29VhE}9y;wm3?!*uCl9 zl-cCGRiD!vykICptq4(QZ6l!ba|{(YY(E&~(7~6MB zFp)_;MpXe+K2@}5%UXU+OiWvVI$$W!$YHV*o&2}tIi=pV{4P~ozj0qL at BOQ2cxOz# zX=!QY-%A8Ppkkbt-26oEOH}}|FkchkLRQPgcDye3!v_MqylLG21m~A6ifjo%?gE`j3tx**x2Y*-#*2 zw{PG6o~C{ktIAHDA`}-F*B1nS{Pam?T7$vjqObNhf8xwH{1q0=J2%np(IhcebCOTvBWH^zI}+y%vM{H$t4T zBduw1=gyu*2V$0webJI**j3in_MUWFP6y(1wrF1ft&CI#EIX{1=`LlEMi>YmJYZEf z)-Nn>%emdfE4+YsUCXYdq*PW~N&utyUc)ONG4)~P0Lh(v^6^`*JUqS>+pi)*W7Z5- z_xMGF<9NS;FQb_0DOQFkrq@#**^eJR>i6UtFlCk{GYR z^;^GQv`n*;rnlvr{bJ&I#;prVAz%;DZ3&4RN=j51*A~+Ng}yoy at 24LwBPC!Ieg6FU zl|UB{507r9GuiiV?OM9!yvQ4HJ#t6?_ekO6+z*KWf%Ggmn4+qNz6J4+kdRz6FgUBC zqT)y9=1wcUa3Uo(Dk|8u6*!0K^!15S^>v}-(b5MXD!>2u at vf^X=7uplJ3H{dDAhvC zSM~jONF&S})jzK%t!&oS*1m~|;9QFHWul8OKJdNQO|mLJBDK( zH*0wC>TJHJy;9=T$ zUp%poR~39N at T#K_R^(wxI6!}6jJlSlW at L8`pjS#$8)IuK(6O%;=OC1t(lI5J$kgwc z%g$bHNj6N_*m$$T^FUFtw4)=ht-l1ohH8qxxxBNpKx!8yP(3Z6hj?8po;VwH=lR|Y z6({SO+EZyny{KHdwW>5qopu-; zp=XHF<^+i}*`8JnI#pUp2`{@JT3_mO1CHDrDfkJHzM`Vy6!23#Fur~Jz}vTP32T>? z%^Jf6o01eb^PhE9R=VE&0FQclZL!N3kAxEw6F(#+C7E9mnGby7>^v|q at Eiy2(d?tZ zAL(E+Qh67%m0|y=E+= z9~p7w)o)|L2atAF0516E4LL5!o`bX^Mn)V37LzY=NxlVgk9FmyReke;<(rQi9DImf zLzqtlZ^1n8S(Uu>rpA8ImCVPCBz-`IVJi>tP+D5rv8L$%RoULXd)MoM7Ca%9^Eu at l zjjGot{=9x7(z5)!r|_8U$J<}LsQC~zW6Od3!_Qj?GXvPjf(4(iX$7f3d=J~#$;zv! zyteAK-t)h%mb1z^x3w(Q8En!)@#N*_(}M-{*)N4P58T^T&0q+hq-TlQ5g<5NcL7W7 z?&0xVBK8KTc=F at NSsHVCGPJ~Eh0icB*gSlBkj~q3s#6nqwS?OZ*u7I&6TP{y-tpO% z&ztTlnZLwt(EIo}*h#=|F%e_#j#ecKgg(cp=hfr7_w at AiJBF%#ss}4Q(G+5ZZ6AUH zqM)Ys#1uJRZV2cAV%epr=nCPQ8yQXizySR*6WeFcJ^;u2-rlZo$rPS?>Y_o1laJ5} ziPcQ+ef}d1HhADaJ3n7B{Po>jd&$QAaTE8gpGV{4<9H2bRCIKPUMm}xgN?rl_6J4x zaP(Uc<}h-M`x-XpL at cs^_!O;97v?#v^MgG0Cv&5^M5zmYB%O)dA0N+RoioLw)9|6F z=;Y$!BGt_In1tyW-gFrGAH!Gfsw zNe}rHB~}`6Kaz5^jqvKp`-rl97>NI=7gu-~K at + z0gvj9dJ3>0d3t)<#LUdOuuy>C@;8Z~pkS;pGx(+vd=?f4g46ktxw_M0^W*FK5I#mB zAzIAH;?yblAtGF|nPgRA8LGi^=e;5W#!k(E->7()+7brw{Le6PWK4Z21>|?D-$FRL1QrL}+hHMLeC|t-D z^G(le{0S(*0!>_AP>%j&Ne|f1ovREnF7{A&TtA8<*R&f#6U at a$NZUZ&%r%M8p}pf( z6Z|Ljh!kV)AV`q2`R_Z%p>plfqtjS_3$8ajJY4#J&3)IdT_db4PSuf_frn|-2lMXd zGwLpFiWc*S-EbK`q!>wW&)V1?S)h!J7G zc!G?Ep9la3posi1)9okkcRi$}q{QPxtiAAOS1=i=Jz7y+jdxOk0*AOO3 at l^hL9VKb z%J&pyKCH!TZk8FJ^>B~(2;wuQ#m+2v>{wy?D6tRnNfgJAyGw=f54h1e+yia2^>gIt z(E%^M-qKXn!gTOEbSyyTO)-+^*h%vB?U`qjDY at xjNXG$XXegzf-v$J@!RHR8%ob1I!t61Mz4Tb-dze}36axcd6kb~JKSsKHHwso at q+CFk`BmVn367My)m2sS zZ#RGsy1R!L`1u{#D%M||D$mfW1|GMUj*brNg%GB;sEDt-d;HJBXLAfVN at HzhGHt9Q zgApsn+SP2mc4-ba2IZ!ym!wMP+2ir at XPcuC@d-|gQ(br#4><7WTNlt1*RZg#vZlin z(ptRsE9O{uc>1&mJi{0TUY{))z;V;g4sXvz>VrSQAQDcase^dLUey;bUiguDWZ|XU zOn1W%7#ka}EtPD#!cQ|j1 at CbjZ<1s16R$or`Ti)8XA7Mu1b6Tm_kvxyT=`*n!B at Wa1;?ZRVRyo(n0wfyJL6Jh)oH$X9BzkryS*x2MGyWJ76 at q1o$S7|{? zFOSDXAYx76ga at oK#Iar2^z=W-s_zcs(c!Y0e*(N$t36fK#KZ)k$;Qd^7$?@9g%CW)>GTj(5KigU*Kgc8o*p>qi6#35sk6_vqD8PRl2{r|14}qnFHPLT#q at u_( z3{g7TbAy%G1OWjlW{V)*#rzH)=;E5ae2GPI2#S9juli_~zoh5lBCo2dl5o2PziQzZ z9`4{q$5 z5f6^v6#?n9x>{aWw_m?48KX*~^V_$0*=8h!yp95kq~-zgEo#J*tKZIng-_1Jq3ZWEXrBNNlhU35PfHGMC_XEgC(?dCh? zyVwzj`F`MRa%@Kn%*S6~FTu9xqY<#&wxcMZi?0CdToL|To&Xb}=jW$FUL-C~^6=q9 zjYp4McI+b70AxnR!*4acjo)&20jtVa7AEmFW2{0$d{~_+Impb+94oA!^(ixxcVBMO zrXA=UaaH2ZEZu17ys6<~7l5x1!fs at +-A=x~zGY$%Htx#8{$ngzn=*Ek-C7;K-elK9i|+Wy~20Md9|e{6%cv9SRp^1;Gb*qtj_OabdNKivbw!2JjXCFM(a z_*17(6Tw#^Y(LsIni!|DvKt~dZ+^`*jJRw at 4E~>G#tRI zchtQX9~=GtF8I|ah4si&8?wMz3xKpYHp z8JAQ7KKp_R?LEwd{bLY9-I^nLSsy=soUPp}B`%H?p1=fLKYcoHtt=oQkpD#4<`O at D z?bWW14wh%9VZGJykO~4CH-HghwHsb&{PAvt_t at Bz`H5D^|2R_oiB;ep7_6|vFHpcL zwRzyX78@(;m%OJ#CM)y0*d^54+Y75Ue>)GFEKS$SC3u$;k(F z8bYzbjQqq25BQEFl$2O_3Sn(_Zti(R1RYlQ96o$Fwj6v*bPQE*Ft7gB@{$HCrC at P$ zy5pt;U{U|tzrVos#56QcVKv!6xf?|8`&(Evc0wjW4xs`YhOjIV7P`F5bw9Tk%)Q_9H542zkXeyZBc1jTB2=guAd`r>m(pXM6((E at Yhx_D83_6dBF1D;WVv0~4z zR<$>5HiL~*VBm2)3md8R59HN<|9?)l7*Mjdn_WH`FVDtie&9bgAdahD_8!J50KdC( zeWtitlI{N1^95cuW8zRNA_0p)mgK(8l1Sfxv;x9{t&_kd;ZHD~2(UNSQ^;p$XGcy> zPEc1@&obsxmVOwuxmF~sm8~@$u;gN$W}>L z{rs#D%pNTpg5`snJ7D&}1L12J+1U082?-$-FZl)qc@*2PG61(z&NaIKU&z<18f}d5 zS(t3s0_zXF^fU^r=&;O#2Og}lA2s at l?UCIPlTyV1JG!fCX?amaRh9d3?Gd}V%46`U zUPnB$JW&34K(Gdo9mMWo>p!Ql_zW20B>{oD-CYpn$J$b;0j&MV0^oN8!3;GA%3N#e z>JS4M^hRCCLBD+2SYLfQ_MJWy{FI-cfA;U+@)}=8Zs69>o;_pPS|DDHrluxr!iNyP z=h(4hcFO~9SV4V&l+>ThOOO0UKg297%~w*Ql983wUk?orS4(=M%+`^rRy6g0olWHc zk6)tUi*11Q_5&k at hre{`5)05qU1 at pwm;L=3glme5$M at uH%eKn14?YpGE2LJyr(+2( z*avemL{A*U?yx$=2ZZig at v$>{iM at 7z@8Di5q^dl}@DF}gx$S47i61@?q@|^?yA~N_ zgwR%BrLuB)b at d)tAYhQ#NDTC7YmJWN*sofC3+DAiD~xf^%DMnyYY at PMgp~9q_)2Gv zp(h2S=oVOCctU>m+&O=;ceoEhpj4Ap@(*?88oQ;WoI^2*g94VnOC>o!$CzK5_rp at LRt5D+2=U~H4($Yj z15w_F4I3?he})NI4OSjg$0%R14cILr at p5b`Mb9W8D2TvH!4AC%4=3BTYZqWWHG

    !hH~5iuXyN}$G|J`RAU#c~Ycmj|vlh6_eUM at O5- z;};OceEf)gSIfW`Bje)S9%O0{^jP)XoNP-0BMK*{C&H5c(9qW{EpnLpzI^!vuG at C8 z_H^%Oc4K2eed(;J{{Bq zh?Gg_A0PMl;=D&|w*SlQ(vlkhN^|5P*u=x{- at S8o++6Kb%r&Cfw{IVpVdn+A*)Mxc zEG^~vFW3-ENJ@?{7$umjg6jD58i9y~oqZQ!YJxtoP at XktyoE(N0*M%I!l=_+MruN}CPH$d at l)mRI{Wy-U_cIN at y@#c$r+UFAdP z1ER&n&8 at t=To__~c;HcX`F^_16^h6lj%KpPwWsOV at -W8*YH=7slQuD~j=Ic|AUeG^k>MB6ef z{)B9TT%O6mPV7?uZ{EhEN2TAtlf!~wfG;Vzy0*5ap|780o(JsXARtF|%&m8&7YHCy zQZaZ5SRO#PLIULHM`UJZ27LQ#=HtS`!tJo-dplNUAVA&W%d4a8c+Gr1UO80N0k57NyFr<`N*m{<+zinLm^&dSWGyb^QFv`V7Q`Pij=uPEawZrf?u3 z<`W$kXZn2RhN_3uH&>2<28#c`Rly`0&aBYXq-Xe}J=E~OS^!4I%TAAZ{39ZKoSX=R zYa${Ha5nB1hJK3%ax?D>x7L>f}^$=mX%OiS0~9)3k2mQ&f)hx zgxW5Ue5cz%x+)MvNK7H<;v6Gv_%gS!@F(-3 at +d1SQ*!NUZp6Mjbf2szO*`=v5xzPp zDQPVetmE6aZ%2goF+_DXL|i?{$;kS%ioMAg at K%uj(yVtv8=BzYV at f;~QKYu|H?)xA&O6!|YdJ241vig~FU!op7n zEB0bZglwD?n(cpuj8Tv4-f!!x`3~%N8L11v9v%)x}=$oO;XA*i7P!TF6 z4yq2Q*4xw6t5cM7Kgc8y2xMerre7>UJ~U{jN9!2nyea!0+5~H{fHAUp9 at wbSjQt!YuOc-2)KlwA4bV!iI+* z0prks=fnv at p!$Q!w;`s9F(Em*!gc>KoNM6jP6E1uo`>hKsHo`JkGO-dF8!b==;-J= zIy<)%#X24SJ>QF<^ee!UvGdEt)fM}d+uGWWb7?qTQNF0tgz)!qtTS65b5%J8KX+o2 zX%NpIoJx|Mf-xyL at YSn*r}u5+$jQi%UM6vm zPp}=24ImIo*8X^w`I6%u*w~wki at dy4vRS`#%kCLuz>$t3x98*IGx+)G=D11xj?0$6 zkK;_nqK%bF{M{6AItwP3QV2iTUB!9jL|1T#)uUDVtC zfv$yx0$b)D;2!_rAqv(V*p`DseSozOEYA%AQF9IoIsxlOe)8n&U*Wc7_AB!PPpZ+$ z4bWNj+fyq65AgsPW#Zq_`k`yQ^S6gbNA->vDnxh`f_5QjXlX at 5FrJh!=FKRw)lE$H7EA!*g;4u~dc}#d7pd{Wn3Ly-oEQDEU z6_wYN=afFEmpI^30AB$CT^*zgzf6HP!FzXnd>m&n01%)MIeq5LmyV9(NCe5qs+ at N0 z!X(*oDk?S_HZ#*35vS zxR7Hp2CV*-D_8or+vdzC+eiVSB;4+bc-4&mh#Q!i@&YS2ANzhC at FX||-*>RW1LuFV zrK;Bb&p_lH9E3tcLowfXJNybjPyBPG5(hz?ss?fL;r9>MAxh8VxI4_ at 7g_&JHy at XQ zAjR~bc$#lXkYlT;s;YJE0-_==F7AY_ld7r%Lqm*>dG$YYJlw;*r at C^rYW-N_6BA`n z4A{KF5jR8CKG=|rJ?bD$G at m?icUWK90(}z`uA6|z?fH|zq*jnlC_ zbL{QxG<0x-w`DV>&vy+> zPVSeLmEAh_VbC)NFvhc!lb212jEsyhmx9?zMn*;;Dv4*9nfE{}0Hc4mKX+(w5QobU z(layRzhZ-OdwO~_wY5K*42#5^26VtQmE`-g()>rFw8!!IN{r6l1;?cAKgJ>V|D=w; zeEqr?p_u7TFod3#(vjg|Y=U)ga9IBLD*~9Vc+wfk_gm-Y7@$T+Fyv;5t$}ZHF8x0w+ zX0ieOR$k`1x;h=eA=aB_Jg%dJ=#P`D3aw`Fs#_c at jlm!6Ha#b&YIdM}uF-MhFg-ne zO=F`1{~D8|m>5nv^x}W{(&PZTi4QwObsT%Mht7 at wYyv_QA{;sxj at tu;g1zdP2m%tv z^{|43^2t_~S`)rvd1G}3ClcViiji9XvG0+)MjOI#F=ESYAITKown00DwR#3&$7Ey8 zI<`EC$FLgcx0jIpibh-VgG)~bT^@4to(2rUH4s|v--VYh<0vAWxC3v48S~04-5(gw z;9>f++tr-o;+U}>6MPhB?redv?UE6mlIvwFjhE}T1=rdxUEf$~FDa|878~c%%ePrz z1C2p!Y-~I>F(K5px`sox!0>J6$98~c9H*v^mJHy)sdX|kGFd{^5Ud{{v!xT~XU%T5Iw5Pw_iM-s8+0^Hys;9X5^ zt<6}mH3#om(s31==Ks#1ZwGQ+Z`wHp!3FP at PEpEkuHn?t(HR0c?ds)qCuOaqdB*pp zO at ok!%K8s}oy+eqDNMmj)ojouEiHY?e&qrv1mD0w5>-`Itp3Jev at M z7x^}qbipf<{^TxcVVpN|R<+ zPO7?i at uGyA#8$*UN=b<;hQl)MkiEIW7Y zikK*l5J^qt_}2$=R||v|sKdmijftp?UkvPQY4n7X*n`IJ-t=E zZZWwv5~i at Qyth|v{CkM&$3MNFBmDf104>yjCkVWt+brAFIP`t7(6Obj(s{$-{rmSA zY;BgNQ!?x@&&92^)sD9~)pfObgO-Vt^C0lcjv{Lw*xp$Y^$_=IPGD(x&V;#IFv!xb zE=)9UA5Oy)fzYjjI6p8l5}T2l at k@yAzHq~Ryh{_zZNt7D)Z|QL+S~_~`|xd`0q}vn zMh at 2AKYHv~c~1|`^XJbyK0THUTdX&`WlcJ#Vk6^1j$Ma1stZ8%!%a`h?1v8Wzc5Si ziblY)<<-ncfO=Kq)lIv;U#ttXx;4Lk|7d7!{}noNdn?qoJQ2?{Yy at 0W&NaGu;U+>(>W~f_z`zk4EQqADbRQ)z_Kvh;0RD0&;`UF69rY?H2<*%*9VG<8ofo< z*xiDsUcljS)B%>cg2_k2$t0653& z^-z_U{sqUwu_vgg${>#5*gm24d08y3*t>Tx_IU%B!2wfP<$3NLDbB=tNk+48yK$Eh z%oC>)Lc}luQ8GL{%wm7(j=Ze(OdC#q2F%jhsz;IW1e4y_SSlHUm+ at JrvR_@Sgm{D9 z+}IVLo=#rvLnqc(Y>#z5+2uodPlqr{fP`shwoAd&F8DIOnm#^h*`uI-fv800L$U-=jz7s%aj;nK68E5v|X?VuP#ieF5u~jd>egbx$;Pn0bJy7Jlfg97L3!%HiZ>MhP zhGmA{+KR!!e%Mo1V7_-dQ&|Ow|HjYnbYspGC}BX=hMXSv3%SyN-NWDGc`yWHaVaUp ziYRSK;Gw{?1_lQH82kis%XRUhD6AR9cDEKz87nKhfhlr48FqR3@?mS at eS9MvFy^>n zhqE?8M at hKJ*RB6Cw3_a|-j!phsi*g9aBxtkU?y%(iJO}n at ENaE#5w9X1$CiU$6iZM z4+~*BIuxx(r}FYH;n9M%wKaiYYikQ44(W%W&AgMF+nxf;soLj6bh5Djda{a&?m32C zXn_Fm3MmWW;o&&x1uxA6Qaq4QaZlmyiWQFg16#tdk(HIzwjv!ZN`Ln30a$u29v)kU z)w8Uu&q1j}ICJK&e<>`o0LUp+v@($doWBy_UK44Y_?FBN$;K8$OH0e;#V%w06n&}u zMz at i~35GLgc4M{_9>!$6IhL8S>+KoI*{{qI8qG1c at VKz50BhJ_EpYtldk)nV#Wct9 z<}>qSO*n*sg_SiSdqXonu_;UhI;8%74XpO7sUZmyu&R2od%s?Nu-f0_6E?q(9zBYu0yc}k_d=Kt3=WRcX$(g> z&Bo52JuMt7GZ5_~bOYiHC zIlwSBnBZjEXU{I-KtozuZ}eGVH?qfD-Yw0Dm)_vn*I=dRSXVA5&LU-IX2wgFytG9% z4RZ2Q9n!WI|qvI6NUQp*% zRbJFM+qb)01*e#Oj}*q4jY>HN9(b;V1K?(7o$~W9+bm4DBAr7gw3%l)&%@&{&))o}xLY*V=c?((dJ30z!n(9VQX z{j|^W`e9kgLOc(wCR+9qrUkx_mYd$6Y)cU~H9Zgf^Y3^2IhBH|;gUX_5GJsY4H&>| zukB2$l6rt$?)+v{6hqPaVpn=b#`EH0hm?g*Idugy0pLw3t4YOcniFr`()6!d8wQ)D z7We*2KDCv}*Jfj5tAGGh*_o|h$(~lkz;8ZwihyJbDJ#x-p6jvd^8lF^DWBv|wv>Pa zSBacfg--Kss at wmJmyXsGH2&2P7Iim5DEqx}cb at d5{%cyng0>u>$%7CLm%Og0$a^Y) zkSNXoHF9V8*RQ^%e- at V)KYcn6xcaQm5Qx|*cnZBKRM1MfefefOf}0Nkhd_se{YIJ} z7ZJIm^z>;4u%E!?>Z=>{c-{!oj)~w7$`S{lc=GDEx&QsuxZhtQc3V-;`^Cn^dAjaD zwvU~i9e)?}J|obeSaZc2?w$Y6`%9bwP(~WDn*fiYsNWTg*H$Apw?>Pm%_%OnzG^r) zG_;#A(BFTG)3$s4iWJ9R=|8WFi;H(UDQ4^4PZ?4yv`itKT3MW0EgdqfsjaKKdHeQj zEL5p^t@`Yl8zQc9*4EbgkriIoWt&ko1WkLE z;2tgJqo-P8HKT^0qWSP4LrE%F>~UT_KRk050m2sMU}a%{rM7S2slT6}``FkMiX%r( z6Wouo%RlLv28Y|}6v%I(_j!rcg&bVq=sBe$+Fun$Q0&L=c zbwFd!K`;^#aK?AaBpvhJmGo}8n>SsLpMO-d%j|;0{D_ at mYU1e3R9u9^p=eRBL&U_y ze;jV#xl>u;akvupcV at NFE>s2JFNKKU8x-_l(t_vwd7 at bzI{)fy>%ZDNt{tGgqByxy zFr2vYuhDUzq@<*8SXhdMVB?voXB at 5Su&tmjd}in8k5pDxD&;2B&HQrs^`w5`pv}75 z)Z}&UC${TiU(4IGMX!a71||ixEQLPasvDv3UH<#>Pi~*X;hB=n;@PF80At$Zl$6RE zU&hL-oB!@o96H2cYG&q7w&3x`dnU6{=rBaQsKi8x?n0}(zq at kp9yoDcq*tno^mSIU zabGc3q(#NVh!idL+U|4$5%j&F2&9IRI_7`}0S}^zic~`REhNl;cSsY+_fwwDGd=); zC7W;xI1x^)xv8cW4%k5s&Vb1RFp)fy0s9E`04?<#gCN2wz%N#i?l_;~rlw|ON3ne& z2!=yW+HS16bW&1McrY53oP7Jyqet!mYBskB<&of zv3oxy2^}3>zHi0XueVT$W8v>$tv~yAdRk;38w(4$y4{?siMjcam6er2;N6t)V6qoJ z?y<1s0#3;+75XNZM#wG?ODSsV>JGE{OW-%B6|YD%9F&-NXYAL)Hxq{~l7v&YZ{MEy z98vxCv5-Xg9Kf+ at TpV_%@q!796k1LZ5$3v#O9W at 1QAZ~ypQWIpV#9+aOi|*&pQeGq zSptc at z5T%SGzXJZC<~UkWjJm+_+o`h&8sa3= z&YnGsQ!7No#ogPUH&Mw{rvR9k-e3NMA2;7{u?BZS4 at S@D#Xep;FkxUfBcGzk1t>;AuWv)pUz=zPE1-+ at mXJc+TB!% ziGZkcxw6vI^aK)N;c_?H=!+tvV26G+MjZ3^_m^;6 zt%-S51u_|*qk^x`j?oBETfcBm&snuRlj~~l{rwk8M at C0SmDGm-9;xbLz1 at wBjF!A2 z9XB_2I)NfLPkHvExf#|zRx)5!sDz_>dVHLf;2z8Q zgeWKAKb7#YB8}mK0M>wbmQ$TLDI(nZEU&<&vGH*a_`k0(3;=o1GoS_o6!oGygU~y_ zu)rIx0~Tnk5MdV`gOL8p({uNS2Qlk;tJa|^qtT-$J{J`M{(+dX4!Yo(6A}_~FRAt5 zmEMn0pFYV{R9Aa~AbW6Dtw<3orcAwQozQEqU%x&WX4Olv37Cc}e_>x&NFpfsR?!Kk z<@6J|Nrh&=yfHcyIgU$5{8XKYDyAvX+o16R at ckCVGb3PDbfKf--4mTl4r_Vv?)fvX z6-DMB;l%qgpgllbUcG*O;C~hOiSIxZ?6?M+=GNqj*w=S$ynLu>S-)>{k3r>oJ1u+s>j*4DRf?fv%cn}&hG>*i*e zltw-t9^qTJJcM$42=Tqhi|y>Lz*FI|e!MgbCucPd?fWx)3)4pY{8SK1U44BiR_4cd0qBBE z$H_e at O4%}7CBF=MA~%GE=Kx^`VXaI|P0PCTx4GiiC{q|K=;?~%C+?i z?J8eX?JJ5vfWUM;@GU1$^)^$#o|*>v1nYEX>Kw$e*&Q7n{jj50i3TbhPZY6 at 7Q*|4 zy-)UKK~kq4=)fWl{R-NT&|%cKAr>uM>&Kb zEXTvSv6Jm-Y1v^)DKEprY2oXn at 84hOSYLFVOLHovpGY7&!0c0SahLS z;vm>-zod#4J$S{i+s!QoEyuif at 7}TQe4d7I!Tp?^X-bom!OZr4-F^uPtP+xvfUP8E zW at af}Y}?j-(XjIrJG1+1opa~(VS?kcIPs2q@${UK(v;k@##y?aCY{Z$Ft;#6x#!(ipqja%WFXPcR} ztpB!NHkCrE$`@eLBh at 2y0k9~$1q1}~`j7O)TAE(IL zEpz?(`xh&+e)oQ+E}e6lwfJ8xz>mTQ$#<(oS%dy?H7|B9Zl-HFc0anUYMkfc$7L}g z2TbPR>e7q?rY?wCd3X-vy$k;~e-{4Di=Vh?(0^>TINqJ*#ls4r@=`*o;A78+KEeJ< zALT!4eh at mR{eZ(z{$ozy<5#hP at Ab8F0wojDsA|t25MVGOF=Bb~`x{rul8;&gwa31F zC*M!5{`;mg|1Icpd+CmWMXQL_SK{3gm0 z6Y`kR!rEN$O3VX*9|7gNn at O7+xxnV==!iqz9XD6$KpPQTSy?G6Dcy+s at uj$u583+riSW=Ya+5MOv6bAWgkaPioeQ=OxRt~YjwiHYS$^C{W>>rC+p3!_1^ zla}5gDOpE7t)QgzYN{)DsBosZ9B6K2`{WM7e{5)c+cWfJdws*aDavub)1yu!O^am8o<%f05V!t zuYCm$ZP%bh`}z5mf;&p9DdE(`tnDquvfqUCqDk8l1=I?wilqa{muO(TZzSALRX+McGKrqb6C5e ztgP(QM!}{2jB46)pv~0tp)W7$@-067tj4*1RZ2Fk(Aa>cnBHP@#XLsI{#``o!omJ6 zFNx}sR#IYHoay7l8-VA=qJ6M6X2-R_z(7o|VM#Tf_4&Si%P?>Ax%=tlCF?&igj2AJ zv4(_-5=X;$V`Z(}cB8IreTA?oSezpSm$bAQ(g-@>`ULlumKNgAY1AVtagHTcu`<9& z>O=U{b7w&85D_9}5~$2yzdRWce+IA48H8tlCqPUgo%Er7(p5eQ`)6VUxinv(7 at 6l$ z*3kF>#PJY_ at 59KH1@Hiy!h)cXrgqem^4y{6>1l{;B3D+%qVevFdds4hZAlQFh_E_R zT4xpHfN|0&lMA`1sOV0o_=JRfqs@=0tGd}rn#RVQ1osr>+}^pTMse?dcJN1&- at yA? z2;^b>7KeHYt&R~YenmL;en_6*M3L9gp@=gSFreU~lYq&No3#M1=6+Z~GB+Ax{OV4~ zMGviN?^av29Hyxe{am3f^-)t_pRzPZBVg&y9APJ;sH`kGgTe3sosfz_NBVUfAUD6D zAMxY6vaniG0z__60-l5L*=7&Y6eY-%CLg^AXkP5 at NL02z+v=jQ|7d4N^`+Qm&l zLvs#-sAKsrzTwZDDtY$^p_E?@p4|DD0Q6|_5c3KO=m~ED z6^--eyzCk1R_A+>@2be|4-P>C0Ib#4b)plmCwgaoDNs5Cyg*T++Y7xuCU;mQbr=8q zQK7g3-mlR6`g#p9UArb^7tdRuY6nzQR0hL%FA12G%{>91CG1=I`>Q$nwL2##C&snu zc0H3 at A0h>MXDnLwIusAdSiakte|^afRp%j&D`;wle=fgP3^0GQ?`{c~i{;Hr at O!rs zrW=HtyLFATb$PIm09NTR{#)L~0x%<%KHt21r;Jy)9oK#KuXn-PA`j%!h%@|W`g4$- zPTL;U%{AuBF)ZN#QNRm zn at -@Wro_CwGmt+*kl_=63cNZ=Le9Wvu>bo~@9IiN- at wr#-9mtBK&lN>^B)ekW=|WI z76Jq$*U->tDRwl`)X^cP4S&u0%J()~rh|^ z=BUc@`YC#WKX^gxe_miF5vvXnKi|A&JIZQ*8y-G*Cp2^`;oHy zNuQ*O-XG<9y1F6PCQF6`mmo-7cV(y(QxFPFE1_f-Q^iuo*@r=&#Uh|IRH^SxE<#oo-EpHL_y at vO$I4pf_ zJ`3kyJw~1e`5Wgt&i=YJQfqEuQDfxZb08)rW-Qin9XJyi_<)VREGz3+(w7 at g4oz1( ztEYFHjMby>nJDA2PflMmblZ1YH^tDf0kdLJBYz#PA^m$WknJx1SLrhI`OJ*+UG(&Z z3kIRtYvELfyM|6f>Pk=kResSO%_-x%cJ0C$i|ra3&0T*gUYu_)bDaC#^XjT|d9%-- z(NxDjLVt>D9UL#RtXSP$eKBbgA7-3c at xb{}?B=!4mwf^q?1lAR{XEkfyM at gCT_X4a zG9Yp1;N-*#DJshd&wY2RsjUqGL`XqRO>Jgl!wumUOD&5(UNn#K5-;VLFxVFFVr0Ax zr>>3>4=7Qv66;m at 7^$nzPv(|kq11Es;YqDD4!8ioN76Wh`i9jpi6{fjd(Yw*oL0ZQG at J%uaXSI8`F89UJkbzy zOxv!jNX_t-hvwv3U0FWV4VW$Ff|e3zYcl{|&s3jqXlZFpj)*cFvLbmoM#ImgsSF?*-0sbzwJrJ|lkh;g~+sd$Tn z2M?$x*3!_;=Wwk(64>xN8 at AfGj)xc7>#a^*dWaM_nYaVF=$AtM{TA z&X<>Pu8%2;KDl#e$EFh(MWV)8Z7;?a2o)Y?UvbDWdgYO?BU6~4PqWbDZW2pjUN~#e z^vO7rw*M!2JH{sf9dBxD6?g0vKLbad{q=hE^HbAqHfJ#tgQtPXBsGC$7E;-d8q&%J zXJl}%<#~Wuw+HI(9vv$wbU6Fd;oY!1xH7iTz{!#yJ$e)q*3-7pDEpr4*@47w%mxd& z8n!al-|bpow;!{eT{VtbD}Nh20sK9HJEc5l5B2x=C$wC|8C^0mGXH>pr;fd^TeHEV zo$BoAA-NDvIIl`*>FSc6KYt!*N+SF at cUOVfIXBV82v5g`u(l*=Cryu7x$)hC0+CVc zsvCW$|CupT7=y22Q z-n}hCWh>Ghi(Ej<637 z52Ks_n6q8&LwOb at H#adZ^%xxGL8$=jL2o8szZ#xUfQQ>jOWT at eAb;(xLHnFTvF-)I z-6&!Sp^3nwn{~0)KbsE1OV4D9jWPM`pT*#Sf{IVDB3D?hk% zvy6<4)bGIuk7aybcWVX*Gw})tPy;!OhT18x>#O&pqbVSt;j#}%{tqS9 zGqF1r>x)PVQO;^M2HvXF->GUOtvDyf#ujg-eu}sP at fuh4qVNdd&^+&DPmgQYKCs+g zpQb#wHUlVRL8=}r5#Z2MTF;^+mUY(XC%99gjZZG57`vddv;+7Ono5#8#_^)*$LD;K zioY`yq|~h44TmN9<6d;wzB~4TYpt}LD3+C%m#5w~-;$)XQIXlQb?Yg*;_5eVE`!N` zV9{%(rC?qyskE=Ux%IKIwfYN|+i84|#}1;P at vGU^(@sk492t7&&h4S4rA>yn{8Ai@ z3e{bKrnLJ1fJF1Aql1x)i)+Id?w?guB*igbARN`*-PpACSj?WOyQk-S*r%KV%Lnr^ zwo%kFe+9jl=Q^xpxJ2V$y^>15bRJF}jQ`lNW8iCf#l?|a_B;_PcBwbc^_n^fIXvOylF4hYJ#s-*H7TbfQGrG*oL2M(E^(O|tj7)y zY!?Q{ql2LCRJYzCX(QI&-i~XvAbL#y9*+OOqNJO3w!QtcLAy-EqepKV8!4TK8mhoR zJ9ms49F05eDmrh7t7~6g89+Y%7eK4sM~6*P*5fw+J8(BHNy+mzpQRxj6&UcjNU&hH zOkSQK_T2B^kGp6tySY6&U~!t)PDD%$=ajHr7tZ4z1y@?N^X1Ex*v$)B^#e;l at HSGv zO?&q2!3J5T=WVU6XTa}Rk9#UJMvcP56KzC+84QN0uAvGWBRGLf-Rw7vCSEtNsMw;! zwkF20vb^+xCEMuYD3?8?@owkl<5TTQJ4Jatbswu`noaY#x5MB5)cSVPe1JC z<&~U{u6ib22d`qf^V2DD;SG+|0 at e8oNO@{%>YgO6X0(;yZfv>6R?x3szhdXzW)hO` zVH;qF<5P~&?Zev7_DM^#LW-OeC#vJ_OKfJoMR(8?5|R5}8&2H2h!wQn3N~z0LU8TA zKhZOPy(1_sHZv>BUoq*Vq2XR!R?WtC7jh`!M6Pi;cJhDxcm_{bT>P=x8TpBa;o&qW zyCh^$`wos!BH`C?GpDq)anyYl_4%hu)OK?!R*# zCU{RJNn1z)%o0~{1X42NAk&N>x?0Z(x8^)FCJ^kV8=L9tCx9=ga9 z1p{v-;O>Mohj3)^P=rc}#X2&n?_ZoA0JaBDiQh07CO3h)@|S at Qu7!dCgAzD7)Z`#1 zC&y7?lzt2kb_h}H{3llf*qm6v!pIo(_N_e1l|$;rJ}yA3#8(p&6O;fBN;v5>Ng$0^ zf#2h(edP@>VmbKd3*h;5Vj^ zgGCoUJo+}CTcLR2!T~J6sm4m|!iy&TUQQc7ZXO=&=n7RN3034&>){7?d-7xt{q`DH z?P7a-`|n|DjVE$DEFfMjLVQ6>LH(1zuhm`1+$K74{2RN=ewHt=V!f4)j_$uxvYF^C z^qBu*jbi+Fb_>aZt5Kmt`H at j8f@%Jl8$SrHmV)Dy=5M#bQi~Q7q&6 zb6SJ!q9*?zSy{ZhcaXY_f67*ub5mB{784u$eLYIauWCRh5wq{~2OfwUsHLT)@wEy! zOZnjh5zF1+N!&mN_H*yy!!6mm6xq7il!UMFc5CSzY~6RMEh|K*~@S zpxV<9ifkQYC0y at I9--NUs1y!92w+ELPmj^0xIB?`WX=Wz at El=pQZs=RL$nmy&3mMr5mRo6RM|SU61j>s%z-6>`ZA&zS z7%q3-^@5;oD)=JW%*m6RsP>9inn}CV;oK)SM_M<>1>C-U8_sFhF-WS4VSCBpcVQyj zyHnPF`$i}l2}2jVb?X)!tI~SN#_L1~ELCl7FVqLz&Yanqm6ZjFi-_xT$#6K@%#2+i z?u2}Y(C!`Y-oMWfW|us6Y!49Gyuw1<@g^%Pn;dATp`igkq at HEnR42~>dqXX+k&%ly zm)P7)t(~fleJ$r?_SvN3${H{s^s=u__4PNZOiw`68yB(9T$7=w0;`(*yd?pQ_UEf# zR15;fcQlg}(QN~hl9IXsh9<*zAfv;@jSWwP at w`@653E^EiyBS_E_}Tvuu;f|r2LNM zR_B^hT4|~Ir>t|2k6$Z-bBaKuj`x8(_8i&L>$7?sNb+EqJn at j-`pr`!mpg>9OPy|T zP7r}nP3dt|lEq@>;y^x0X0ba0if!iwGW5R1_2L5QC48Q-5efPa+;Z zRHiGoO?9xgv^~FF09Q!vG1R7#Ls!C#6I&J8(qQd;<>` zKa}X#Ha>@w5QrC1z7jyFpqSVrNe0cNe-X(5SBpaQO^F5oaP{gjIKZ9s^!r8agMi=x zKX;oBH)DQ`l8fTzvQZ{Jc}0gF9+Yz at eb34WiMIT|9l0ll<uqTdl7+z9SPq$%2!c0?cj3PPk$STDEX;2J< zkjgFzC>)|%UQtn?R{BGx=YBVDLQ4L8Z*AC8 z{^%}9q7b{y+n%Wrd=JKXYzCiG(AR(5P#>&J1yQo at E?bad(hUepT3WRC at 87@td*%W- zExswOfg*!!MeycQ*KscQA7ZC+JMiWVPb4Mf0D9o`Kpk+QG_}qO9~o?_LFEGuj?L(x zF)=ZN|1hQ1Sv at _}C&ST9+uZU%_peuj#x+KY;31KV^m;F4cWWw>;wm!83Z at PYVK_qo zW{oGs`Oouolh&6Xw-#W}_F7iQj?{H^CY)#lxE>Z32ASQ48IKBeDFNH#X>CzCeHusV zAwpafVcLJ-0B%$DEJuZyZ98^6WRN2xBSTFMun?!Z_1`9jyVJ%ya+SavwY0Q=*W(p1 z-etwaoR)vr*chOj311Oo^Z@$^1U)^yw04`(H&-C2Uv_nkICfHuYOWswtj>NFtq>gHyRf9^P;2)`K56!@k^PpRwQ*xO7K z8}biW1`-z+f5#GJ5L{(iiFdUq1j(s*r*f=4vxFh0bF0|e>@lQFx4E&ims(LR48qmD z_;_l7PTj5 at Mu36+{QU5;#oaPd*LwcP7nCA{?->k}l at HZ1G`s^|1HtLVix;@d1qg2Y zldx>vOk8S;D#jM;KVNbeGxKehM>jh&g_0gr zk0B6VzI>@Xcc-)Tby-#XdNO>1NFkl}o&NJY~WQg0dqisrnQ|;9uW1y0mhV<_( zI8z2_mWGv;|6l5JI}89H7(K4i!*AOoY*qR0o${nUQ4y!fmF}`)13U*OCjdjfeF6g1 z54hCcRQZwRTh`O6#!DMbD$A<{{(}ZXF*4kz)R+JD0{9r)e9kF1Ds>^Rt*xD!o(|`- z5Aydvk)j1b*I at FUJl_x;wVPyK at j zBz{%CIYwol(yykqk5OqZ?36X_Dzf*0zl;s{sd1j$;r#s&?X(1U?uBoD{CGDKJO~UP z3q9`erf`%EUdh^@LR+wX3P%37Wj|k26dHu$ROa%Zxd)OAg*3Pyu(hpid`EnJQ&TMt zlY9$en_2kdW?3CbiG=}FL!BG%07{1FMcv4 zB;gSeTLDrMi8^qYCjs9=I3 at Ib`?d*SQ;ODMK2`IoeW~r4Sxcfq-t*`1 zb2T4WtdHl>6rKm?NfWj|pVZRVS!j!UFSOF-FJ4R&ah2S+&sVP}$&LN!QO0u5xr`T$ zpFitMRLprE-$6=3=mRWJ3pRLOic?cbYpr(hP@*j#YZK?r;T_K6 at 82g=wAxFKNl3ik z0BUJ1+bJlfCyIL!&H}oV)z{w*=P2mVEspC60P}YSKYxA%%b!9*nl{}{4GsRrxk2US z*JE#WLHwJVpAQ7a+nvw6Uq*)QL!aTz)s0Jh!fpF8($G&R1KLf}>9;e2_Q^W5Cr_wL{40F)xa z1SI1mV7gDA7`P);Y&vx_+ac^+I?H(BI6!v$`^4F^yNVop`PCZ5L`4DU zlsQxY8^jvty?c{*%n&`Xc^GB7mKS=5bzE!~KCcTo$g>y#^TS;*_wMZzbp+6ol##K$ zsHg}zYE)6XjVShDSz7LQw at ZwU62HPB=Fq*Z+}Pyj02db*&a4?6Z}lt*or@{kc92ar^W03ly(c{@CJXPhQ>~ z0}~Jw_Zgo(Vp=W?8XOMr@&qehCQ)k3tmH65xPveH6Y66B84Ze*oG5DWC7= zMjDb|O>7&z_vlgJ;-W|Tl`=Q(AU{76$PDl$DNIn+9H?7Jtw zyh!a1Pbc}%)D&1;>|TBs2P1(5dO$omFTP82{Mq^i!@8K4Y-T%$sOYZY=6EuK%S7j{ zbsxvCL-gqBgPiNGrYPY&v;)PbJ?Cub>FG at ur@o=D%26MGd+M<~aS*sCu&Cr`_MwU- zVCZ;5s;|FKQ1DE~1aMpk#G;WyA5$TbnyV#!FVdBHmmZ}jH5fRbdvOa7)B8I|}znW$0B2Bz;f2cXW6(I{!0f&xF z3W|z2!gVw at c^Q&JKcshuDjgTenP0!AYndHQ0SMqwG1zwP?O$3!Z`bSO9En{zy1I}) zZ~OaixisbmkpV0Si2>i(b}FhsgY3Y}%uGB}+zvHZ- at X;TbhvlHe*zL14BPPgoDmlZyRITi)MH#(Uc!AX zV5BH60ih7x-Yx6dGew*3oa_l;dOA8fl|YIifR+l{Dl2dE^76_l3w{gvJ`Wf<;sVqi znV+v+oF2eyCM@#cnM&*E=?z3XzOKTZIrs0=V&e&FJMP`PcQ8zG-80v`_k59ERcp#= zEI0$k#Rf>6{Nv^29Sl?AOL+E-6bq%`Sd_8qdOxM{yGL4*&WrC6)zQ?XLCS&IqsPQ zL&c@@amTOqfy4} zk^D|d>!+FMbBKp$O=-mIw-ZuK$T;AZb;xLe#<)vyjG4Mfupr38Hc|jchxqve!^3I( zcVf{DFxGmEf2y0=lB&U0!bw+n3gA1A{!Uksu_1i{+YuRRj&Dw%UAGsOcL5ZPd2SO1 zNOgI5C~OLunx&U`<2=U>yG(YUoAe;6@*BSNO-R at W;mGjl9V_rncpDFeSe75`+{}E` z;cd(@XWWzxK$iq}DC}in0Z>5X&f5U>%A_4RMt+J(;Za4ROq9R{%>;M%4G!v9ce-7^ zEGQ)@dDsbsFin49 z{~yMGS*nosz<~qn*Hd)B^I~K9_$L1h7uc(RPdG2Md&gxL7x`1CZWX8{wGc0LbJ;Y8 z+`c`oTANcYz{|UZfPvMfHYfJ}#PBc~_~w%b>pxUeyVPTv10e!x9s<8w{OPlL8~1jA z8Tp5YYnC at Z5xGPQDI0H;v78RCf-fd2xbr^byY$0LeN}$IFDrdFZKI~HLsiG~-qeQ$ z1e741#@0?))P+@<3ZklwHo}=ALXV|D=#vV5oMRHT^2tegpY}=Z=DF5HRW^m_YaCU&MMOP(-h3P!YS;NA at fDvT0 zy#&ba?!3u{tk7fH9ChUgYpb#sdVMMyZl!T%=Hw^>sz5#w7soGZH9RMWA-nr(6 at PYpKSF(;yN8H~~SQXyY2Y913{S?z1571~V#k z2|p}KQ~w>3EDF8PPjm&ztp24-Pfy1R#KnuaN;&Xbb&y<&mgqH>ayUk&P(_>0u)BBb zzANtjL%WV>7)7&xSMJH{R5C3v3qv9(>C|%`C^C0 znEUId(wQV|-GovzpI=_#gwKb&th^R4!fzi@*0p~|ODrMJC|jfb9Gf7EFU>u?zCwH|p>YP1`dWHbzlr=Pzbafe8TU${&<@QTR at AmywsXV`nYp^IPRXX5ijp!wCFLfBgYNKA23$G$d*%WzgHKN0;#qFo=^(h9kf472 zD}WgQEa+Sk?voI+lTrN*FoU4&r>y8n_tDk^2syAQ;_51zn3yQ&Jfx}~wqej*xI7Q1 zU{d3~Wg+)8G&WB6`BC?WE3sAu({L_IUSt+=9n-

    9&XCZ&ksLv?Ys`yCYTf{o6lb%6thIt;JFTyx*qplKCB=F{KKCU zIuOGF>^*I{m|Qynpa-xwyl3`9hm_#}wr{_A_b&BXnCO|r=9S^5SUF>3)*Qo<= zC+)iX@$q*7s8IMU&oP=?SilJ;w2h)lIDiv22yrqizv5R5o)}me8^;mzuFsBW?&soy zBicPQG;}6ir?mis`QqZ@)XgkD<?!f#^)etP z8|zvU^>U3-S02#Vtv2hxq_ORWp2_uHSp4y=5cqtqH-H4A2Nu)87;j`} z^I0`KzLA!8Xr=7Lx&btz;5fuYVet7I$FI%fZ7V7p<(XCQWM!o)G-#9vX at KB?yKH{^ zu#1tt&IAr2er=xD$=Nv^jsU1;Vsi3e*j)=Vvm3z8 at tzKM>~#0^q;wg%_iUAssi5k8 z7aJQJn)~|CcKjH|xplZwaVO72LBb9h&M2}95|q0)2$Av&%l5wBQp~Pnge7v z6&g at RsBA4X2=(NZLOOHv=1qyl!Lw)2wvL}acdn|ZhXo~rqNAC+ySq`?*~8Nl<)`2f5puoodX;_w z0Y?k)}3I at a(+_+4Y80$2-vC+Z;r?8BZ(+^z-RVC4RnS at c-PSNx={t+c at 2 zv4WhGl%%tiUsUvn-**UrhS5aLn>U+3efrdKVR`srxItlC$tGn+idUX<+`wDlsmDcb z>*?qicAQyhmVqP5ZnBMzj>cs#@Z=|o9eVou`)^M6lqCUCp(|A*J}^D~@MaPcA=63%00;uMFTf8N4C~tOzY|5*Ao|iUG6pR!dRzoj zfA(zE*RQO=Zp_Tgwoy>XK?a6X9t`W|NBdxB--KSbu_wKuK^3CbVANc!1^XKG#->Rgp{AMdLBKC ze*4ykDhD9vBf)i<=q&sq!wP_MV|mIBQj$A&?x2J<1Z{&(rOb9z>B0&lmJi?a!IuNv7Dz at FYN6rQcCb@y1qM!W)2uqs?A<+fzL_N#C0#8oj`rt3fKv*s3vH>( zT*q-%6TmcDT24+54)Q{R*#SHRs~UJG>*wcJFyHd^t1*-OY%@j7ryM_CUfwKc9tau^ z4mmp2&CSh0>ZyK!K|L2HDSdo=0Mg23>!QNn#OoX@!QE3`jumU&0q_!0NdjOhs;3Ur z%mq*G{0HwO#ADTifG1%8GZz<@#sCaLf<=R>sF1no=a*97rIJEhR8-U$m7bg|ZzJyz ziikQcE(~ap<%J8U)V{!amXthIy32|U+s$#uQw|dwm#=JDiF32N zaG^ap_Z(!ezkj^|8?(#Em~fj8egA%n;=V0&6*zH9puL8M#^RqpvgtZAn+e%xu3Qnq zRwM+F77(@3F?P!0zAeZYTEaz;zi@@i=;#;o2X*VHEZAWPuSh5aUVTicZ?m$p%FD~E z>@e{OJs9WFAGm}YZzLs|QKWx*pe|*Ub=4tCSV#yJyZ~IcKnPAh_PbZ at eN$6YcIl++ z!5yT2M8m~qt~vO~K)RK?&>>O8>?-bzDl01^zU%>Zj}G6}#SDn at 6~UA&GBPp<8 at NvX z24D|p|AwC*2~KveEH7DnOgK4$dl{mbHrXpC8gj=aP at f)Po#7 zBco6fGOE72GAor&<$`eC5a6ZRv380ZH*RQI6lkTth49Q?SZ*5U1{q}6l2A+e;=X{s${Q>hMZ~>Wjp!+JKh3DuuwId! zEFI_H81Ua7u-V(&8}E)%QogI1f=`9=iicy0Sjpi;}Zrj^9$aO$_A3u45Z+zo^X!~~Aix<;=#QzNI z1MJai`D1q0#nRFei#SL)SK`8qyL^$?v$3%qqTlY};i0U at abTlL61uX~yNYt;ls2s^ z2f>k1+Yki-;Y^W12 at 9v(j%+6qaY7j)U{0HAH+VBn!IUbSoId)a at WO!u2OwY6q-&=p zKU?VRLj8#8eg%S$3E?ArM%4IU*Pr(C@&XJ~ch_Cg<@@_x>Fj0mE8Qi|cv|o;W#!~j zPJMxQW_V&z0Dj`fiy~`{&&AC#$4CfMv$GG^FADbV?H*)Yu)7Vwa0)U#UN=+&>po*_ zOckLLaMF*|3~U?~nAh0qRXIfKPJ`PpVN?fE4*(5L%gB)JeSICrS?}x?!upZB%N at XK zQuCiJfIB;uufG1XxQK at biKG_G1@Q8x`k6M;XGX-MNB!s+xS-ClR~lkaRYSuLNG4cb zztVes0i{;5V!IJ*!ms5Ot*|Yesl4^!UoU_GPzqG#c6C4wDLKXmHh40r1=V?jLqltT zG9vbY1dXDnI1`RL3;X)^xf4l+ckjST2lAauZCsC|qb(pC0JB@`Oq>qxWckP4H2nZt zszenTUGw?&W-GxrC at 5&n9BXP)TIxQI&$Vto4GYuHX_i$TA^TeNiUo4GhEYa5($Y78S|Gs4Dy4dI^YY5u*ktsxKgFy+H{P*{09hc!svq7bBGN{;&E?OR zfiH5ua_~xXX90^dDkW*ddo^z1%?x_%uu!9c(uW=Iz6w70jT zq%U?NPfcCMW_9caUteQ}TY^QRCH{Q>5Q!U5U#uhzO1dSaW^o847;hn at W!xv9a}k2&>hmp z$F??XZRxKeZ+^efS49H20|$O^VT!VHCB%r9*VnJ%x(di5kOTcwQc^x at o>lkuer=vK zgDekX7cK_^yu6iAaH&nP#n;~2n(W}ggP*&)lFk=9*!gboZ!5euLG5P|QBgBHyB+rS z_GoxuDM-E4)u~Ww_s*RiQXW$T8yg$MJOBhd=f<|8S-pIjpq;8N+q?a^pAL5oE+&nN z%D6bCrlzJ~X2wn=5>0`+I66AkkPs#&CIZ5``|H0uSz23n{8?7f&``p*-~<5cmz|xp zMJ`m#yyrpTFmcyk at b(G~)Vfgd;@r7CE-o%;$KdNoF{-Oc;GOJ}aPUJNYwwlc*8Twj z-vFv$VFP=pD=RCl%IYQHyt<0)G45Xe`StjYz2c~Ho7GV;4#vE;Y6+}>JzP1?Ge&to z?p4dm%3_j`Ua#1M?*nQ at PuX?l$`xF3GyW+X(V;?{Tdr}r!Q1M|wad<)QdjJ_aQOO) z9L at qSsyLOx{PBu!`e9B%0X at oy0~Vs8qa&fDq(og)AhnKDWF4pW)8hIkY=awWj1CH* zU|bnL2I&NMjXOyWo68G7dr>{eR;>r at DbSD~cbR<~ z8q&2LD+&z_HFx at nJKQd{CL6Sciin8pdr*%}C>TX+Yik8!vH|=H2?>RCZ0rlbeL5th zJA5IP1Hl$QrEls~vNuw|L=g)@{r$L^6tEn2aTInKS0|kG6TIAaB1I;ACZ1K=YuoYT z<%;Kj#v|B;oDXIKiH4n%Q%>mA7({7;FTBUYT;^R}UDfsV%sU15JN#-y-Aq4U{k``OkG^u zted6(G;47q$lWaUUY5WB_wuDUF6kLcwIwIQC?plNPus(lyEc?Gg9JgT^) zBqtwVc9R7vi9<9*pbO>}V91Yk__Zz&Kq*;%bfQQ8%h*YW`1sDMo=$&KY9xH)q at Erl zm>`gtIxwayi&JL6O^;up%-7!m0RXQ;*}SGsmd7ie)6%;6?wvADWCe$W+*IX~iN6TZ;Y6Z9tq+3 z;d82~1;tN1dO3%T!RY`BR)#Vk-4~(4PJ0UrMOb?S4h>cpVp7piO|e0KqB6$DY_Mxr zLV^jmZK$gH8}bK1OhfT42vxW$Gujc}wZGBG8&KP$wl&mjUlY`G4 z5Z1Xr;^D(XO!>&Bu at KRkavEYMfiN;YUft3{gZqkHT?3+{nIRu1q@*0U?39sv|2}Pv z95vY8y at wC|CEX`aY-Cr;cetydu<-im&=}YV)6OMV21s$RqhjUHxCwK?YWmBUyZ`+8^FCxFVO-p1ijq~* zJ`Cvy#e*Rt;mAbf$l17H*2I-f?lpwqv*#{EUvyY{M^1CYLVVg8%=fqocUV0-O!P zVV>)_?%TI-2?X4ZLr?EtS$V>N!KR;8%ENbgX~6=>HEKO8 at 4pE|9i@;UH!yO$eER<5 z%ZqK~Uy#>S07=BrcO&UH<8V9)Zh;hK7c4`5-`$>9NdQCl#^d>;Q#O zNy}wqXBg}85WpLW;Cb7&ZL6I6R*j3}pK at lzK-dL-M9eU`04xdq!kTy;ydm*p$EvET zHUX3hN=o8X1^cB`R|$`f&b-(O7NocyMW5#8Qfp{vARCU0i<_F7@|dK2dh+B+3>*7s zZ{e&2Iw&bAsldZu5SnmhA77bM|aNs{LUi#MaVMiHyulcJR8$51R;>(xjJX+iG8JT~AjW5toOXSa2(cv^49f zQ>U;%`ToNPbR#SXeE4u$Q}WU~_Wig`9=ul&aFr)@+3%yHqoX+GNV6^!|E=ujYYU?k zR at _CPaGSvl%nYv^82AkQHIo$E$GgFu{Qdn+;Z5$mC0}TBWNd6~5s+U?y7obM)2X@^ z^T9Nnc=aTm at JLzVKVGa8sLF+k(Qsg^bNY~^l~c6Pz5$X1q^BSDtw@#1I$ul`EoLtZ za0UxjeSM~=(T(dIpF7^zGBwh|DX*xA!-^Q$!oz8>lyK?+7qG1#0XgS%bo`Gw51F<- zQ#+%l7o@`- at a!2Ew%Rtt%TyS5V0Bej_Y>WNd|_c>yig|~&nz!5Pux>Bbiu}^v;Sb} z&h=!ujCajQscbv=acBxqHS$NiZbqttGN8dkfB5*=-v^Nm{>n?Q-$xo68&SXlFWevu zsWhX&Ua&mJ+B#s{cCX;uM8-n>Ng-g9F?4^Wp)(#a_em90ZvX59A>^jLN;dC0P#X}$BrGQ zfKVVIzWMxlH_!<|5fT3zH#Xrvx3t_sM at NTBxxSM7)-6qoPgL|lR8UV1oPdA;HJHF< zC#Rdm#l?wM=Jxi>ZwhatG#r37Tm=&)YAa}3>C4z;sH+=@`@SKo1GgpfUi=mmS!#Sb zB0Bo^Rtg4ub9m0W*#=vgnWIx#edd3hLklT(=((xGeZAk=)zwv8LINAtrsn5$MXWkh zKXGxiSN(%)T6 at 5fFT1&UKXpE+7Uds()Uh929E}%64!}^RPu<{aXpBCJ;#Igtee2e( z7zY?lyf!&H{`8C$h)}_95A1AH0u$Izd#hg<_=Aj$46v6D3G9B{vu97CSRODq4tQ*! zKRa*NV27EXpP#uzk)I!lot at oBYy>a&*=Ud*8tN`_UYscI&9iQ1MIks~x)4&ZR|7IX z+0y|{VFXctve&jd0m_CrhbR)kMqpqdWs$n8t0)z#RPfJ=RaP*A#i3}&DfoF}pXcHg zIl?hop<*Jy4WIid+xA;Yt;K{oMwIHUYqg%=;>DfWe)})Xnw}Q`(5tgWrdTaTmiznQJ3( zrlumt!#yy-gi&PxfZmvEBbGW29M}q0b+6tMPYMe=Jb^vL4zMaAw;%fW$!|08dFBKS zY4*_4(cv0p!xE>hzlNLZAO_ir4gg8OTh2Gb at jg`H$~xA5ZlPdT0GE@#t{U$Q7yOS) za&PPy=CC|+E;hbHwKMID$RPkV0EDDGXSWghMn=|Fj`)#L2bPw44)}9L?0-;yd8+Ru zuBE}%YPb$1w_=59`On<7v14_2+3J7AEGXz35wQyx8J1&gZEY2Vi^9Ux{VdrtAkBn_hihhDSpt{^uL4~O*{Fqu zMb^0&&zgRTu5q3zAlCzRY(DZpEaJq6{{H at j?<^rFCoC^7FY?c-&1s;<$8lg3_f6vh zN?KYDm;DSy)(fbalDE z(%r5(qpe-7XL3E at +zYFdr%#VR*@NNEvYyuK_xDJT<+%>juzqS$7xw;T55p<$2y^Uc zo%tZ}ZFm?{EG~h6={!vJ_3ALoUP-qf>DTC2J0KcjJ>|ebuEfay7gTVH6nu{=Uq9>O zTU|Xp6mT`QwWVNYX09QXCFmE~?ZHmPO(di^4H06pd%jFqyDu+4{$paoR at CB!@`h#3Iss=x;V$e)q-K>u zyn at ZkvlCr z)4B;sAKPl#**77uxc)~37-`y~)x!EC{EI!(O;p z4fuX at R=_gKgA029T}+i8yceuP^6$<``NEpfO!aNDv$x+BP1 at MdV57#)FCb8Nb1TJE zn}!T_O+l1ECbb2%@*xUM*B^|>=C{YkuZdYT$Aya8e(D3@9y>{aDq{l)uyKo)M1_%#++OXw0^Hnh z-be7{d;NBL_~?=J+*4lw7RqXBTc=jlKCgPUtJbdca{W+`m8c>TQJ<>N7&0!duPi7m zWS%EGkt`1}yQ(Qxa&_+8lgi%cJAAynD5jvipB5LIASl<6S>EF6I;W`I<%Fk*(F9^4 zbK&~Q1aHwYF!=NK?L<};5=au;5Vc6 at z{NYwe! zRg7(tp-uvP=T4s71SBRrGLo$CCsSl#AgON!+v9_g9{|0^U!^QDZ2Ge!eEYxoBO1{? z_4V~QZ-&b|0w@^GI}0v>hto+gh_b_P!#(GyV*J?RaTaeqlkuL$|x~p^fV}%V+Wz5G=%RVF zwngcs>o|jMmj0%rM~`A>7(AVM{%+b^$;KU;5U?Q;#$Bwnq7y5m2T}^a-afWazi%~l z`>Fl3 at 4h<`e8JNp=(VJrW<-iH)bQjYShPj0gJxn<60U8+jy?$8yCkSKBF82~4RB;} zORuD_|H0ukEFR$Q`FnTbQZh2O0}n$rX(y*6v$M1QVe;~n|I`}flV@;cV9jmD0lbw4 zU)OQ+EK=UT**DdJ%Xa%7oo}p64-EP1US0i#+8#JMw>b3;fEM-<;x%x1IPSE8H$zyw z)@IkJSovs1cq5H#1!6NEF-`N(;lovUae1`YOZ%ggsAxD7oVDPQBY>5U;)oewABu-R zeiL2U0CokGlpK?ilG?dkL#B6t-gB5;eyS3=9ql2#`%J$PrfUZ_dl$ ztpWTfEnQWP2)F*X>`m3No#ATxM?1=bAeT*;aKe`rwH3mq%K5w8Qf9}~_Ofo3H zdC}JPs7KL|A+^IHc$cV4URWqPKiR|MvX{eV- at i_Ij)?ghMibNDGvBeD?53YzK-e#x zj5DT|59V*<>O$0a#OXT9Y%;8CE8G|RNK!6nw|53+V8Ok+rw5hXcd$t4z>6Ug z{)bv^fh^!2BZww`m6a!+4x~vj95XSLm)~;p=FL3kVRcjn{_kZrYQe+fZb!&m0|1Eu z$(DW$CrF*s4zIVwVjjS`og4Ijae5&1xtpBvX#n;BRq@|KQbHwXj6o=t<}|2W9#zUL zq3!%{uQ?7f0GJQ@`_~KW=?B$gPQbf({^G@^zdeKJ&Yn#XUjSI36LVT#JV{<*VGU3+OyJvCQcszN z|N4$d9zKDhl@%B6OZxBSS$zjwfAjWjim#sIe1M}3M#KxHvY*X#nD`*HgxG{TQYUcoK6 zk^DwCRk?7kL(uHamQA#DqI}qaPE6On0+xxlNteb&B7bhVu#Jie?;kuoUVha4?{qRU zxC?CBGrrdKmN=6Q4GqCR^o@^?r+tv_Z7&yr at 9TWA1Gcc^bPGI|=P4UmvJDEK at u9fa zn}Upt3{^2d?u9HMvph at ZIsfApc*qG}h9u|VCTt|Iv&-ri4_BUjtIcKm_1fYTJG$)1 z=x9}CCE at fV-c3^rwTXl-SA*h?6+Z zT4divYGPsnPAv!dT~}8ZQ~4A?TZs5;_WEX@)MC2_k z_pi^kt{jy9`SB?UHh*9E at Q9s{j}nr2a9G%oyg{O8C$NCcPg1rq{_6$Uy!l~g!ZL5; zgyc5`tbLGCv+5W;kl4s}%VootK9c$#zczou#Dp!kd_L+v?eTQ!!ht`Z&iYo^@}Te^ zE}SzmGQzWa{rYwAojW>v+lcdmHkU53A3C(HtJu-;;asqCiTHnYIdx4<8bGx)tgP7A zur2CB|Jz%iffQ)VpBHPX0|byWb+w(iNDi`V4N!P|j!Yg1kUky-W0k?*%`Av(?8 zYK-OL;=;zmrTK~RhBHP+p^$F&@$zOh{Rj&SL**Yx_Ty3&(hJ7t&S9%64%z<52zqTVym(A>a&^iSF6L*jWhj-fnecGl%(X(KjtBL~uyOiLQ&SVA0D+PjkAE)B$#?&0jEYSsmdCg?%5)ot1gcQsBno|7uv at 960Gwyw@%v^C|isI6}HfcNs89*!^J5ZC<&58^#5 zWEaOWbMt&wy_26klm4~vICv}K*DYleDf{Q5Cb^fh>Ca|?tYc~@U2QM$GJ3edu z#J&+!0tX5jt(lA1bxQ&7WDFl)cFg_egM42am zE7MPDU-mj~yL#@V0=k%pAYk(ExUw-!N!1Paip*jb{|(#qek~IF)`tIl8z4jTXP`> zUi}1du6_GZjvr}Svesc7*T$5jr#ArbR&TFfeCd0c=yY4ADI4R;4;^k}<_->2eLsWt zfAVPAc(=*OJqdZ1_4f_5&qG$v_n0;(VDWF-aG`fMMm4;~#Jx%q+cjT=$Hs=j*;QkM z%qKhYUh?MCdU}kH9z6mh*B+DD_}s+HP4O~!S&w^)j780*vvd^aJO{%5_U+pQwP-Pf z$sr*jsM5E%=z)xj5H-oF2+;VQ<&jSY!BLyk|3gW_*XHf^%6J)Tw&94sn-}Guh9kMn*1T z%iGtltdRmHIl?&nuyOTeT<+QaJ{l~Fj*bp@(ZbJc`ZezS6G$Zr1g_<$Ee}xW5Tz$v zUC-L7WS_&PjghY6pLAD&lKFvIw$Gh!xrmpYxE%x%0F4OQ#?+h2aa-bOG`QpZo^q5+%2S_pm0wmC8W8apa8qI`o7z!h#D6aX-C4j z%PA?5b#``oK<+=C{flERV>tUj`OJGBywkurS4jPY#nr=|^YimBT}JP#sAq~DIr0X_ z?MB;DDOdv1&f$d*UIR^ilS@$#A&qKwzxf>3jzXOJoM+~z)$z|EBNs+mI3c02b8~NB zSy at pr`FYi*ZZ11>J-|>RgOdFDN_)6e?JBiJw3z+W2Z0LHH~!?I&sk{>ll at mj_>MY; zd;YJj>yD>-|NloulroA2rz8=jVI`4Bk&$fKqhw2=?2!{HgrW!`vyfRSyOebuSw;3W zN+^_BzONUD-|vs>(I2;aa?bg at ->>m}t>Eb8 zd~e>+)?ocHQZf0Qj!yRUyt85d&;Ix1)>iE|#(4?9<7ulGrHtXRA;#5yrZ)LkI at KW% zLPjmE7FRFNF++WQ4%8$;K~hM_+01?nlTuV-^kTehTJI|GkjdmUXAe&?)+09o<5!lK zQ$5wrWc`0vGdOq?!p*j5uLQ+!`Vlx(e>!x>pSu1yaC;6mrM7md2S8qKVwFopB`hXp zUVY?^Lh(0On=|nHI0uW_YH;CLJOwRy={YZkw3?3nRZ_oAuSmWnM1_KOe6*aIBgr*zG(Y4t6_daa>}{A5!GDqC#rT=RILpg^B&e}jRuE6 zckk#$LHmqwfL~=8Rcu``O{XDdMlhCO{w9BRawrLqzOY3dPLb5q)~bQ4y>!yxWV6j9#;GfS^4w<*KYzfJX1SPzcL7UeQi7u zz?$D6S7Oxr)x71=uC6W|m;npqAF`%r-0V?kC=;ft3}3tSF3e66d at 2K&j_BQt;8Sg# z>s}&HdJq#M{fkpxUY>Bm&8^5R`m|y8rP&wmFLH9I2n(}A at v4v9d&;N?KAD+zp7v^c zHnX$i at T2G46pO?ufFDh{5&mx`K>9yI+kfH^>+Q+!&D&83 at +njvIhE8R)Cy(jIMXaNK#=B6^xZT-GM zLnl3b%XoL`V)}?^i_{%jT3XPa;nvI;xPsw5Ijg8BBs&{t7ICgWAql%PajUg`XA$i~ z<(qwdX7JX86G^t$Gq6mLEYaLyT>ShDO{uDD2{d$c$A3MtYD%FZU>S39VMrP`kAv4j zmwW#{$$6wT;o5kYsuaQ@;heoZJT$$%y;z|a>)V*yL#6))swJIv zoMTX<&VBy;1n4c!y94$RDH!*^Ng0c2ZJ;C#qwiua{6pC8;G)6OmXP?=EFqWgx&-gG z(>R?zKRu2_EFO(-NDL6Q`3#tuU{`+LQn?BRr#Y7fs)EJF5O}5`HKzV z2gv^V$Rvv^rwoxz1oLgJj*AQCE{5Y$q+ps(x z?;0xlesO&E*Q at E88bwA<@gG5{#;v#%JKe+5xdg{S;9fka2{Wy;a|9Za7ovi}Q`H%9o zJ8^4s!=t at t-!HL+%jEL_e_n-H}{nI1I0w|`zA6IPTX$h6g7V6&D;BqG2D3o_G2_nU2> zHkX(fyNrwsR*L|<hHYPy_)P(%M&)QV#K at o(Nq5N+ISq<(dYxtQaF_UVTUVc zzWr`xxMRQO+}wmj~b~got>l+wJPTpAw~24NvW7$9@&*zcWdRDbJFUu=yDq3{EtCsk}bg zE{bS**REZ%^1Bl0%2v^SE_Q_i#N>kTZKO5RCXB;-`$+Yjix)4FTUtnwk?i1x?iWR7 zgw+mU?jC;bhKEtm&c at 4J>pTvPH_N2)JPxz4=2`M z^oxz1{U(TSe^Ti2its&3{vjuuZG%CsgdQ at 2eUg>8a;&JRz(%~jxnCoov71)Yuu=h7 zz at D4>iUn*C3O*;!HApK=Fp0gL_ONGF%+9m3v*X|~7-Z}a0o%Whn0`Lv*|WEBLj(c< zi#4fM2Fc0EOO#XY)ApR=mwjR?DOz|>K~2O1-7Qi10bxV;=y=jBPJSOwG?8g7K=kUV1lCSO*b0)VW4gW<*vQFl0wYy`-1>hk47V8IVM z?N?Xh4x_iQuz;AUtf%}2Hu^j9klwzP1A)acgA>=Ui-Xz8ejs#KuErE_7t($7zYiVi zW)oorGyrjTSzR6DPOq7S)jZlTZJ-=C=K8S)l6dO2e at IhethDzIEN#L?i=Vrhwr#a& zkS=-f;6Ze0``IfyJBb`(wmqAO9N;C92MwjA;rP7ru(%@51feH`@Q~cxj2U)GNlE{Z zmF_Gv at b{*(69a)kK{6g5lAJ{1&ynm>T-iKq`NU5|Lm&XJp%)zva%C^H`WnT&p*w|0=p=T%`r$MYnG!stU!_VDZlUE zzhhS~QqF)*CXpTWp6t`f;yQGQ8R;wG*FdP{HYjM;8;(-Gr`)}$DD$x|uduuB&6_tk zrB>e9s5Y$I?2ldD$Y%<{GLEv6)^Cy3*ViA`T}hE^J33-)>LY5?3XpMYx7WNN+D}o@ z<5%M}m5`A~P|qJ<{P2MuYr&s#%#TXNC=ej<8SXBXZf|ImQDc~n&kz84vr24FfBEK(B!(*`w#tmxZ;D9KQff!1E{``3?xndJB at t6yjh{$I6Kggx^`LPNQGc`3e2)zt% z1dg75{`^^1-dbBgK;V$q!gZ8H_vx{VH^f%>dM2v*;!Or>?jwoRVBJINbWH)LCl&{V zgjjLkWa(!m0Ro_;p1v7ZYht3Xr^f^vctNc;OR%%>Q>_G< zX=l_mG%D}$Yv5!#Qq=(rex8$44)N&?+&e}LrO^bf1bfmu`l9;Y2*MzGVpw!e-lYb!-`_u@ zW7T&kCc*Xhf>pw{&ANxe2~VHybCTnp!$KeKxdE?_{Rg;D3AAcG at oDusCS27bg*g!1 zklLbS%qGFQQuRLrMh^+{0Z*eh2Dpp6^(3_O!lI+sIXF1rMiqcM at 1jL*xY%!#z_ciG zXAFAFP!40;-Q{){Bs2q;{OR*&)0%tyD1g}Vn9#4b8$iO2Lfg-N1vYKm!or)1-KVRX z(>1VV0Dj=KC^uqs!r56A8}PyZVR@%9Ngh*(AnrI52B-;7;$?om2)1p%b{VaJ_rs+# z*q}H+_8QKHSz-rEOVeO|%o5Konam81`bKtkHaRT%PR9!eT#^c(Q-+mTK=m>ZVy#+{ zW_k$ak*E@%7GkM#cv=$&hl5Vfc%SCyZ${_~_kqOf*wEqP;v)N=n|sgyRnCtg*#Nj) zEEFL>SwjO8T*Fogr at d3Zevv}tH>ACMxfMXSJopWy4oFTWA)_6HeK)bx@^eKhsc;sR zmzNHrNl+1*nwlD%Gsjd?clXY*v9YCcJa78Tj~~@hR46UA_FrC7DJdz at tX*sV?}xC< zj*cu6+$_b9L8Q^Pfb?%YY(`VE_}3k`brF>=;;h_Iymu8iGI|gic%-=c(G`2z{5kIu z3oEN1oHV*c;a?>N?H*o<&M^O!-fs;j3#dbS at 6gJtir!pK~CBb zWWIbUE_dn_9t;?_Ln0z?eW<7pS(3SCW at aw06QUyUurlBbE~p&-o7A(heg+5R!Rcch z?BhcKxn4(%2;5FC at tntQlnWOxngZ~`4glO%;I#ifMzX&~+AxhN4^|o at bR;VP&nhcV zX>Q=AsvfukQRHGd#O*i>2so!Fui^NOSFc`?8#T3Yhl1 at VABduHatPaNF2#G&fdNiP zVo}>|wjwG8^f>!?{RbBo4VUTBv9pDG?Z&rdzSOvG5L`9`G2+`f_)1_-5XT=rd>C_s zPEJmngFj;r9GD35VG15oIC1poQKT1kauNacLBbboAjO#xfJbl$ph!%C at 5~aC!oxdD zy>~J&Fa(B%zORnpL(&AW5S0K_u)BYHIsh#2Qh{DkPLb!nB3sNYhwhT#-pT-6eSzct zKZJ3SpW at VdKuB|M#x zk&$J0NikVX|4DRkJ`OB+&WQjNL`KkS*RG+=;V|RGgzQoeq#{M01QZ1BV at 46)`PS)9 zP!UTT8$?+4_VyH#l1Gmo>6%+sU?`7kYD7iZz{%snf&2S(kY|jKk8kR0)7q}CE*xZr z*T{}R3Ix_5Odreu0>)##?&fB;F!OUM|Ahu1)d}D`ZZHAdi)*m*ZQ3?MkU`JD;0vm< zs{ab4>I*^$JRrUc3=SrdNVM4E0bzf6YpeF~_24WpBiL!r0!A`PJ`@Y$2p|0!Hj%Ro zk0tc8g!!x%S_O>&{Dg2G^#kqU;o(8FxCuvume+ z6dkBp4-$dQf;jLDhwAI=i|c+C78atlMvV_dVIMXeG`gIzuHxIE(PkC?44COmNl78I zP?RDqsJgE1Ed)q85WXPgR!a*Td`sNYjCANulF9u0_sfCCR8&^tU?k`VBT-^A^7!!G z-W0ZG#n%V^l?NdY4-d!vM&RDyT>8pytVLq!21dx7Xf4P z%uGRBTU+dOJa+6D04W at 0JOm~|+;fsAOaU7wu!nG&n+X$*Lc+q>?)A!GV~#H4zJ2?! z9qBMjXMKGz)Aa7dA!LUJvtSCI5o2xuzu3cpWAx<5GAdVCHBC(sNBscrzzvWM1n{zx z(_28}SV=6yHlx55kIAU1ql3-FSSg1`L5rAW_}UEK2km^R*B=ucJgeKHZDfR`5O1rh z`r!0Ik9?P>W|83};bg#)jeXC<%~rV**Co24-N2KJj7*uiHY|g8o3_bzfY>GQ`XehUPAyEguD+A9zH~RuL2{iGlT2WSC$%fMKU3H zgAWZ2^7i%z0OQ^Th2S`1kDZ)2z^2SgO>i0}Q{m7|L4npAfUPE`rsV;9Sk-R0&HUJf zL+ at b3`@k|T*N<4<6c?l6zwYXagt+9!Miz+-92$d1M^?+ZDJz`G(#bOLO-bSP at bJJI zrTK at uyAHu8N(fLML-gv`uU{#C0J#xrY80_$X4z8y`0>N1xVTucweF(OHh>GhfCIUf zCmOH^2mEKzwegMK-ri_7K|n~6k&$TDNUOQnVOlA#g{|OzZ=L=(+S{nujSV7cEzZ>J zY^-DC^SsLHB1%Qo~mKKw; z_TXh85c+p$2uFc$P2Rb4$Hd$`$jev>KQfe* znz~j)Lj&0#zI-wGIReTH|G`O=aAN*37MPYCUORx3nqb#hz>1(xmzLg%qs2##;20jB zJjnGs?^2|$2~&7-Pm*cpaU=o62a1gizP;t(Ut{;_$%v}$B*z7oZFKv0mokc(4?nl@ zYggIQp8t0?IOtd+*nMxS;e4*XL*k6S-wZ`2BN?ta#u#PWjZa71ePhn!D!iVZZ|JD} z`}gm^IZ52=*A3~nbZ}5zyLPSH&py)c>1prko0kaq^J9_uR2V>vsfht4B9Zv@$B%P= zyo?_zpRTvi04ro&py#$QlOx%nk<@rDsqwB|$2FJKjt>3V5{GXy02s7Vn;O4@aeE_?*<9kT69ANIwUi{8n_#bQler~MJNjT#MYZEY#LT*Nu8 zg&tZ8Jp at mtsJ5B^7I*OJ-6YdAuhP;f&#(H!Uz`mg2Dh at YQ35qrjFoC=f{RxzmR|aJ zpYCd~jUL at mUKyMX=}$YicW-<{ ztt038$mA=p9%V3Of{eA8f-O at 1v-faJy~vKxsIfP?@!K~B3 at -9U1oerxT8ezM71Lm* zxz+#4KbJ3}`r{wLTJ-`wtzx67k)rl5Uqps at p8fsNd-7G*Yt70vKE^Gxv#;7m3qwLf zTkKyrU%8Ta#G&hf#A~~b3CRY=-n16$4EUq{G}li5otavAeqkYRN1;Y(ann~#cyz|f z=Eu$DCXXj4uWG~_pFgjb)JXpQyD(1DHL1$fk*I_`xMI|MXQ<-C7j1nr=0 at d#wIR_?P^EMBHYSs)~2``yG<@IMyE2&Yz zk#l5X;v?fG6}npoHtN<}WQnf$kS|U zF*$TDyRGTnJ5p7D4j=&KKnW|a;KUCim-p1?20iI{Y3b=HRS at f*WBHJ#bUn#W;QlQJ z-zddBM?;eIGE?AY+NTVkw(M0{v-AK9Pf9yvi)SQb&x-B{Wjhs?(vs6*C1Up at +QNY zviNU$a=<1p7n{gIG+l2F*3r97}UaP048D+;L3&#yrJ9lax+(THc0>^*9)tl_`60>I$_dln#utGe&*C!xhz=| GzyAU5D?ecX diff --git a/docs/source/conf.py b/docs/source/conf.py --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -17,12 +17,13 @@ # 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.append(os.path.abspath('.')) +sys.path.append(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'src'))) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [] +extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/source/depgraph.rst b/docs/source/depgraph.rst new file mode 100644 --- /dev/null +++ b/docs/source/depgraph.rst @@ -0,0 +1,125 @@ +=============================== +Dependency Graph Builder Module +=============================== + +Introduction +------------ + +This module provides the means to analyse the dependencies between various +distributions and furthermore to create a graph representing the relationships +from a list of :class:`distutils2._backport.pkgutil.Distribution` and +:class:`distutils2._backport.pkgutil.EggInfoDistribution` instances. The graph +is represented by the :class:`distutils2.depgraph.DependencyGraph` class that +keeps internally an adjacency list. Several functions are provided that +generate a graph in different manners. First, all of them are documented and +then several use case examples are provided along with +`graphviz `_ illustrations +of the generated graphs. + +API +--- + +.. automodule:: distutils2.depgraph + :members: + +Example Usage +------------- + +Depict all Dependenciess in the System +++++++++++++++++++++++++++++++++++++++ + +First, we shall generate a graph of all the distributions on the system +and then create an image out of it using the tools provided by +`graphviz `_. For obtaining the list of +installed distributions, we will use the functions provided by the module +:mod:`distutils2._backport.pkgutil`:: + + from distutils2._backport.pkgutil import get_distributions + from distutils2.depgraph import generate_graph + + dists = list(pkgutil.get_distributions()) + graph = generate_graph(dists) + +Now, it would be of interest to print out the missing requirements. This +can be done as follows:: + + for dist, reqs in graph.missing.iteritems(): + if len(reqs) > 0: + reqs_s = " ,".join(reqs) + print("Missing dependencies for %s: %s" % (dist.name, reqs_s)) + +Example output on my configuration is: + +.. code-block:: none + + Missing dependencies for TurboCheetah: Cheetah + Missing dependencies for TurboGears: ConfigObj, DecoratorTools, RuleDispatch + Missing dependencies for jockey: PyKDE4.kdecore, PyKDE4.kdeui, PyQt4.QtCore, PyQt4.QtGui + Missing dependencies for TurboKid: kid + Missing dependencies for TurboJson: DecoratorTools, RuleDispatch + +Now, we proceed with generating a graphical representation of the graph. First +we write it to a file, and then we generate a PNG image using the ``dot`` +command line tool:: + + from distutils2.depgraph import graph_to_dot + f = open('output.dot', 'w') + # we only show the interesting distributions, skipping the disconnected ones + graph_to_dot(graph, f, skip_disconnected=True) + +Now, we can create the actual picture using: + +.. code-block:: bash + + $ dot -Tpng output.dot > output.png + +An example output image is: + +.. figure:: images/depgraph_output.png + :alt: An example dot output + +If you want to include ``.egg`` and ``.egg-info`` distributions as well, then +the code requires only one change, namely the line:: + + dists = list(pkgutil.get_distributions()) + +has to be replaced with:: + + dists = list(pkgutil.get_distributions(use_egg_info=True)) + +Then, on most platforms, a richer graph is obtained because at the moment most +distributions are provided in the ``.egg``/``.egg-info`` rather than the +``.dist-info`` format. An example of a more involved graph for illustrative +reasons can be seen `here <_static/depgraph_big.png>`_. + + +List all Dependent Distributions +++++++++++++++++++++++++++++++++ + +We will list all distributions that are dependent on some given distibution. +This time, ``.egg``/``.egg-info`` distributions will be considered as well:: + + from distutils2._backport.pkgutil import get_distributions + from distutils2.depgraph import dependent_dists + import sys + + dists = list(get_distributions(use_egg_info=True)) + dist = None + for d in dists: + if d.name == 'bacon': + dist = d + break + if dist is None: + print('No such distribution in the system') + sys.exit(1) + deps = dependent_dists(dists, dist) + deps_s = ", ".join([x.name for x in deps]) + print("The following distributions depend on %s: %s" % (dist.name, deps_s)) + +And this is example output with the dependency relationships as in the +`previous section <_static/depgraph_big.png>`_: + +.. code-block:: none + + The following distributions depend on bacon: towel-stuff, choxie, grammar + diff --git a/docs/source/images/depgraph_output.png b/docs/source/images/depgraph_output.png new file mode 100644 index 0000000000000000000000000000000000000000..960bb1b5639e7ff615ddbd6cbf8a542be31d5fe7 GIT binary patch literal 24719 zc%00D3x*H at FQBqn;>Fx%f zvCg^v&-J;VcyqtH&%wiiz4ux(#u&fvZ_c?4Qk0j(!=c1Mp-_0zQsT-e6j~DeVaLLR z-&~f}{RZDK4WCMiqy8fQrZ(n8p-?wa(&CR*T@%+P-LzHrFCJ};;GW)HV<`K|kcx%F za>LfzBD>aDIw9D7-Soc4&y0!mXp=+d^#wI&_EX(*9+!0PYHg+VZ3xv$fRUa7-6(|1^pbKZhJFaw zu$TRS0sP35=y=^Igd#L=6zy)OK(c6>$df0R+1S~Yo%*F=3f`WpptLx{sHVk=r`Bs)E zhb1K?K1oSQeMOCpjd$NDv5<(q`IMjkRyj}n$&)7;k{TKmGBPrXs;a)}=?o-fWa*ii zm&7j-6BE0-xutpky}-i76_=92VqjpH+1%{SxQXc#g>RHZL^%6B?jFD662ZXWU_?xe z|Jj+RRF?A5 at i9FM3lY?#zrB3T#7e=fmq^_l16i1BVZKMJG$H3qytLj(@RPSP7gOF9UQpvX&(kA+_!6m zenmt?wBieLaNt%|RT&11TUu69#>K_)zdGZGw!AnydXXsTg1 at k^a9!AwU*bJ&Xn%$z z>me|h z5)RPjv+2L*bgzzPufQli{Cl=5B_%b(cv?TVu)x5 at ML6wsNVBuELqJUYmR!%1P~p)z z&-d@&+wtF5Vn92bO5R?%_WRHKM-nnJIGERHLms?b&|mqPm#%xXxmKZhJ2EP&)P3L5 zdb)|m*4EbbbiI1J^(yD*fdS>Utx=4bHug?|ud{6dT-Kv^LuiF4q1{r_(k(&6%v%Fb zIbXC|yjErFh~+Zcf;;@)neUFhYk)rW>eS}w=t#dKm~{Q1meJG`vc<~md9m&jPtS+H zfB)9EvwL4r!9&e&k18!KH4GTO<>v1GJHNO|R9Balh=`~}zXjdb*H?eK$;&XHa{~h= zW;{qOzXMW)kbuCa+^pBIH(6-3-0T`e2ZOWuwaet>eukKivI( zuf}OLW;CsoZ^lX>r1GuRNRdHj2*uI);q>-uYq$s57Kxc`%2Nl+=&Q zs8hc`{fS{fyXI?{uxt8X!f37wI&;u^tZ^kibR&Y{Clk!H?Uv!p3fiQV`EJ8p_q+F>9)h+>=Z#-MImzxg$lX0X8%AeKJfOrC zgl5Yn`2YTGxHjFaw}*|9WDqb-VOTSglkzHfzWd{!-yQFCR*HU2q9K=`sh)n-1|eX7 zu%>Zzdist=KIhl03#Pa)gpE;qAfbAbXDwW}9=R?a10y4s=aGGZda3A|BFcWiq6adN zkc#Tv(~oyrCMLAj>_`7 at eX!`%Ts}MAMM~M)Sb69`ie_P__r)%qZ$N<9osVKnQS^L# zWP&amJnAmcd3zU^fZEyzCx at HwE3HOWhE(!Y)ys^rMa9JEqq=THbrDcf2Ag!pq@|^y zp$O>ct_6v|LBqfl$sfY$9kQ&veperLlL$N6>p;VxR-A#0U&Z=QXJ}w(C{li)e9tT{ z2Dt4mq<#AINk~oMo&PnuFk!C~{lRQGiRJf|L|mPkCeQ{R{m-b3y)5NC^Y5`-mp;#k zNgle0ioR)amzI_m)yYzp?(grHtD+z$mt>0$hMM}dzdr)i2NUDBSgchYhEjxxUS9Z- z;Lf#VUa1)`;A9zfe(CaMrDxBcT`6u66h7M$16|;j(JOF5R6_=un4wW_N|99z^FfR% zD=keM%6~pss8yZY)RY`XD^wI$HRU+-8BLij+VT9v$sIxq8x>0ZKsmSG zb=weK1Ro#2!lau_nS~@%k;Qy>;fJxEodU{yc_2$%yuzRj6J11DSU6KT4 at ytO`}fz- zQsEYS!TVF0`s>R(wG-8 z`O%oT*G1?&w_Yoq;9z6Zqbgqf=`>wkc0Q5}qqeAM$jz0K(iCWEX}RP>L`=LtK2~8t zjF#Hhr)V&CwEYJgRj6GX*>tkPjxM5-BX7fvne_Fm6uO9og at wD5(~INN)BC4&gh~tOuP}|VuUT4J$Nq?7 at msdUe zl)SpE>RbCojVeWiUjbZ?*aF{dmzT6){lmV zhe4 at Go1C?(6E~>m&{0r&Q((^HtApP$>`8Jb6GKVvN0k(;W$TnX9U*Y9I|!TT<_p#=jS* z>XnwH at EZTDqcX^Q{WWK2=cD}*Jta-e;H4#V5_0l)G|JW4{pMT0-*Fm_m6?n}HiKYw z;0N&{GI`kb<;#`kzZZhCa&ib;WM*dv!v|EAmES;Bpl4;pu6Nm7D9+uOgI*n=w6(Rv zw1q%X#WwKfk4ZJu%{WH_ at R!T;uwa#f(-^hs3*}Nbc8UHXS=>Z=;ker?E z_Dadg`878`nsWVvmu$Y}qsm6wbi6aY{;8tS(%LGzwk41S{WsWO8G?4B+b{l{r?P86b8&Hr zU^KcGjId_;RXVoF48Y#;eAc5rnVC0 at jg3Edbzv(AAKy%N{f*t;-d?dnB~ULAD)AQ7 z34yqy?Jsl at adFt!u3eLjOOy5Yq5RB(7xVBq#eZMe)ImurPoF;hI`06wg6-tg{9H{kV*fTRQcnwf<G{ zT3QBVNf0-nf_srM85xZ3`zx0 at IXVC1I)ywANY*#?t4g< z|F*GodrHQk?qksAPr-3I|51;Cl=Lzc6&0A>8*FSi6IC`=Dh)8h>J6 at 3{`fTGpK9R0 z!PkV3QjaBqxQ5r!Qsol(2vIAa>ixn38yhD)FOC=e_%?0p^X7lBvasB^diCmf&5I1( zF|!n}?2+V+Qp1j5wJ%mn$3HZ{zJN+8D=8^OIk_TDmMC-C)Sb-%`;>Ia54%8PL zAOAZqC)u*%;r;vMn+?18D8#|AaB?Cs0pf8975m6r7;35}shz(54FoSE at STahi+xMV{a~B#X><(Uj zn*zQ7<4di_xOsSZu=aO$cj+Ufz&~CqRAc9O^l~AjZvlwUcV4rQ?BL)aT8Rq!E#97 zB+{&N%quzjnWu8er=g+2*F>{0CR;vf+}Qi^++I#m36o&tCuPTmUUKS;(WxQiyloJ^tlVA3eIrJ+mGj^&Uo= z&g&?|v9xZA|6D>trFdUF2%~<0e&xy)>#?%S0K at PFjNuQCM_E~U3o86o8>rSO)T`6= z`vzkmcZA at f!ovv=Kd4 at mPfSI{&T$Ifv&3!J)YZeo2tpbuEizf65)zmaA=h6Q6mXoK zPXBEx9;pL&f%?px;`t^rlE|Eg@C3yt<2Z&{P^-E0M!S)xeQ2?W7m8hw*2`F6M8)_^hmwXT(Ve-64n;tdy`?sjLIPeY=%5iJvv5dSt6RHBTmVv?H#S41W zhlB+7hk8s=aBVIwdSYVYNSUmg(K6Bu5m+zcB at A2kTBLZ&V z!40OHyh^I7I=@CRayiUNeKqD at o2b6_;K2iy+qeC4a+p0mJ$)Ak|H^Y&@KC~Jf>&)^ zZg)}8HGelcs*bE^zsk(uje{%P8B|{vKqOA5@~*QPIeSS}-ERTdIU!pX_eCXDV8?dnLeq=5nbPfK1Hf)~pJw-EMf z3%J%?JVHT36B8Vy^o&; zbU4p{BSnd*tH;jb*a(**xi#ZG;%NFKerT?eAWK;q)I?5x8)7-@Yq>Yd2`zk&WBh4l>qS6#>((s_jub8*&<-$K+2FCtDk^TWv$Nyi<7a~sE#0D~0enOkX#~$(_PwnwqiZxWDvDKDI2n9Y z*Z1!skZCL|EP)F>NfYL#T3XJ(yf2QK8 at F1o&i(l#1`%RxVQn0+gK*zh at 9dxRxs9*qD zeuo*;^V*MY5%81?;OeYPMHnBZU#r at dRFI@)xKJw%%$WDZ8Ph7V6H$8SE3&%Lbj;Lr zabi^}Mp&R-OJvu$Pv(BOF=BcAR4vwRBp at cvxIgZ8vYhh-T6G0^bEoO| zSbFV2E-tReApP!pOY{z^YX+TQ5Ny|gx`-qM#C?Gx0;vf-Mv-MPs`ARpj+z&LP$&?R zSRkjbgrxyVd825KWPnJm8J|AkKvX_^_AGQoRZWdAUf0sV04+|I>CDE^5FLmcXs)%h z6BFT|?)kg- at 82I^)tAjuPRq{5thSr>|93$Pb-qrHVq;^QSzNq=+S=Z3nQHVfu(2tu z{_*W!i!jlUltjb9!Exhbcj#{VsMw#hvqXHtS#UIfRFdH7z$@7AF38O)fklUKOoL`Y z^Fa02`dncS;B&JxGblkp!M&BCt571B`ZEHy>dVXTK`i1y7K0i%nLjMB>a)r3=;#30 zB&RhoF>!Am`S~-osHjKZaM_%Xj}O_90{^hKwl)JchMAd}|G|S6V1c8F{(eHR5555b zSG2XY^)0`BX_!>{C?_D3MMTc2EAH&fcSonr_w3A5yV0HW`1lyvO!M+;R2+eL94$Ru z8za7R=Z?LzvwvY>VK{CY09lHnbKJh&0i{_{ON+h22rhsa95=U?i3zHysi~#u=0`AU zSKyaYS)OVY`6?_VH4gJQ4liH&*&M(t$U5SS7hFX;^~2^9+`POt6~xURk{i%nD=Vu; z!obi_Jh*{@kr9T7_vz)0MBRH_TrJaoFI+fu8?KX)k+n+wwXHLe$x at cG-QVAD4g3Ti z68H at QgoT}*IC6FpduPK&n=QJvqvNuzt*!mbmv8Iq1+{f_X8-)b(9_e~TOPd92)^vI zi%(G2ATtfPab%;=*!af1d&HnspP|-;r=$R4Uj>alIy%A@*VokWxV_Ko z$%NqY^YeqUD)RX8>u`~)I5_Z;_uvdsV4q3R(a{MA2z)@P8F_dR*0y(YB0VN8 at fZTT z2!{3Rw{M~_V=yF$i-pQCv+yGUwXx)qM;2VrF)+C7XG9t?M2DRm>oQ*+ z at 0es}Wz8OK|AEZEB%dVU7avav&UkNgnhwgzKNEJa{w_831`JJ(7VaP^&gHW5@`$9Q z(5vKkZh#xt(9l4pkcScvaSODxVOv}FzJ7iNM_aS4fuT+fptgVDvq-m0O_{jw-FpG| z%pI~+*VJsCp4J{E{*YBb* zFmXi`6$wN|MG2^>f8D~rTQArj)syw~qy5}hLTX{p>v?&3ENpCVWSPYN-FPJ+BlCyW z?5&O{Y>HX0^;`-BdjMREK|w)*bQiMX5>z^n&Gybt2B0OdGMMHg0Of&koKbp*6^P~L=GOXJ z#NEBl-oL%42Om%- at ZCExD9kX#NAbB|Ln0lKp#YzUxDqLOd4J792}egBCfQgYK-wt+ z0d**!VYEWQ7+3I-y}cU8Wg>`zy9TY7WaD|TfgSxJ6Gjc3DS at r{?PpNnpzOhXY1cVo zJJeDmHv|IhOB3UA-O`^$7h3fM+UrSq^d3h4>|nwU*(VPU4xX8t^No$Y{?F3VJyYDX z^Yae;%8BKYOqJ))1CZAJ=?ooh_I?D^+8IXkPCAOoU~RP2^OL0b$2&T$VKjmzK*?tp zr<*0^h- at HLb_ICxH*F<1~krh|ZBl}sb2i)9|lG8fc^73K! zHzFb<-%#DRMV5eI)LZAeqChEIt~5*ZZAdS|Pe&{H#U)OwYCngEe{X-GlN1$2havhb zIhfS9zdC{}2s*6}Bm4eahlgL_+xEayPNBWS8Dvd=xLEH!va6n>kRtJ(R>-%1+UUK| z at Y37nwOLVdWN*I8ZaQpzqM8x(y{M=tmj49sCA1Je_&?(A1;8XB&T~-V zkzDh|2|F`o_99e09`6fJD0z%|sxojZmi4K|gbK~V(KI2F4Z-D`%*=zSkKanMMGq8c zl+)HR8agkg8bZABK-hreDzz45ThvX+ftjDnmUVX7ZCSbR#t$0-`U=# z2b$KbFel{Y;R&QsE0<4xC=FrFgyMmGI)TZS&gc)MAzMxFNqiuNEIz2HsO;?Qq{F=L zeq9u_=#1xs4!MMLZ at e!h5faT_}22}dNfvl%p- at XMiFfuN~{~88lF2aEFxNK-6 z{c&7U6mVP$AEhr1kb3n>__)rg*V z5($o9?smz>TN(5R>=Vu&F6sq7dkmB)|K z`~m`^8XJYqPgaX1Hkx#HmBW`g1wb+*^VLZU>~);{n8MwVEwE-4B3 z^zvfm;rRfe6*_*{Sl1d>&q_n+;Zc2jw%dEvW@^$|r8`tEPjvt+N7t`kiLZ|5$(0P3 z*y23{$Zl%_IXOFmS{Ze=)#XOBKaZ4U$^c@%p4AK at N%_$A?h^?KWfPMur4ws>=A5hmvqk*%d|t=mk)fR%B4!{Gm--h0VBJAA+)F<3xLfPYVcNrZhqR4z>poca4w7Tie)J9ITBwo*vk| zl~k-V!SQ>e>%M%;!O`(1F)?x4S at ra*e})`bC at wx82^pc2r3_4FiKX#R1qGRL_sk*z zJRdxG&>wm0X<*{Q-EpJ3Cm& zb}H|Qc0V-Bk53^*_%_FgIghcKnIsDExL8S>iqAR~GyzzI5l9RvQP2r3J7e=D?@!3S zH4?EnSFbYGYRiL-om*Lvfzpr$I#uEB#WS)vY4W3TNSV$MLYmRr+Z!@GVl>TB#(h%} z#5688R?*V()AenF{-j<_0In4qSz=~+YrbVrwY)LBv#9_`0T3Pv>(z}W$M>0;H{n); zS{Fc~)otVEPyZPO_v87GtbBY`VyCfOMwoK&NwHJ&cQMXBGFeClCKM2plaH`$1055h z1e{hrlvsB~-hz~tVuiW1#^0l{7Ueq421{x8su&z68gI zLPElS>8!6Vyfp{lE1qTM+9aRH}F)=e!0c<%x-}Gii z85G^j>1og&WrLY2*gM09jC6mS+s#<$1{1B&topLKW7IFwUVA9ISQC?Hv8zd5oI zkDRkG>I|`PN+^^QxIMHf8?;~xX296eQby2agGBIyyyWJj`{{Z$>%)iIA?laTPxcv6 z72ba at B)SjRCq~vVYdsEa->%;R0fJP at dtR&yA01!H{W>r(ps&TOkW3OYS at R-eY~G=^ zVRE7-B{sqRyb~!I=zRv{%H?HcSg0gHmm;z8X1yQlXa8N1wHdDn@)2|~9XbBL*JS&X zq=~`7AVL^_-;P#8QxiwBZ+mZ_e6Qlc<-o3kuX3k at x{r_65rLs{0b1&C9_3I!eGE#J7W5azOZj}W?tXB9w<{Mp1CNTwt zv|ZzV%&38*fV*DQNV%E9hv?|Z`kJSA_p-F;y!HqKv5*b=JGu>lb#(#+gsN=M%55Nx zD=geRs$Mj6x%hL^)W7(H3H=D+e*XM93OO4SP%%yMkKyw;lmHWtcx12BgO>4eN=C`B zb{Hr6sIC&bY2mrKxzvgZlH}y%t?g~XWQD&8T-p8Rsqi&qnKooe_2Z_Mxp__TsG*Tj z1Q at -xfNMse>8=ZLX8LAk0dRFgfP-rXtuGd>vgycx)LIX|Z8+GGjaOQcLmPSO`pm8C z2CeGG?h2e9ZrTkh5fDbi#ZjR3)!$dvny~ENxnh&iXh_!PGVLV^ibUfs*9Un(#;NNMvN>-nT02v2+*r&_MH(z2!?jP3H$%D`4S-E{^_)j at 7*Q1Ey4p zAMay$c;8YjSWg>)%g3n`3bwX)&~C)ZGI9K~DA_gdfS!8TvJ2imz%Vm|s&EJI@&0n7 z7L+Y&8XAlB at k$2YmtPX>nphKEgaJ_VRM`aJ+wtjXwOh{W!h!_Ke74n3RZUHzva+%Q zW>TX3kLF+uSCqU*2Z#R`T8|K2*I7UO^*?%lPspJ#Iep^PUmYuFMoR?_*4R8%`~NyG z<9F@;lu(3E;L?O$^DR_s_4cca<3-W2U{ZE06rxqo*VB!9iC&#@Q#>>mrkDb)>aZf+ zMj23oOjWjJklno>_ow?x4BC88Y{p)tr>C0(1*)p6OIcRNf9vk{bKMYrGWzS}27`fU z+r8e0Vn_{9Wn+EtAg-aYkuEs%p54?#s7>Jzf-;!Xh29stDI_E$H^(`02L_Z^hVnm7 zx&6T=W+Ff#s}`!wwRmcZ988`s^Z$?2u0seZyek at K%k8&euvA13fNvRf zK3U&bVRC9w)CfFQVfjBx$s<30t$vec78Fc?{5;0&lDm6k3;DUTwUs)e>+xfB8w-M5 z{Lh#7%yF_3RaI4cADhpfTrU$46ukdw;+KDpTK+QK-=iyTuCCz_fcwjX0c3ZyrS|vt z|I-6i4Grmwi;KPA?>H&5np0k#1bzShUEs$^#&b2bfo{%bWFw$;xxapz`2TN3n#aw4 zad!4GD%WLmN-hRzLt;J^(9 at E`byi;DmnxP19?KL8ok4{NLLgWbgx?}4kF4aiwL zq;3xn431$b@)ykE4X-^ruaEHWPd9BD*syyserf`e0=;pNP{4vw`%$S7&BQ} zS##(m=A)}^>XMp`?z|u#0{|UM00xEugUgjG#!KXSo{yFDK0xYS&cLJcKHvZ62-m)? z+Sj(>o>?e^B-GThU@#pI)-+1n(&pxj9h{w+9zA*lRqEsDY>Dj+X667Ug=AT{2n5CG z+ at AfgdVcX|o9U(`_k%TQ=tcSz?uVeKg=&L{1}`iukkHV`!BiRs+yrBhZI-{aaA at 6^YA_&`F* zZH$F~Gr4<1Uq^P at lSNeBraqgdG3f`^NAy06F- zXh4AAQ}c at qX3H@?efkuXH0}MPi&!=`Hn6JQ9y1EhCL7$M$T-wR0QPRcJL2z|$p?5d zBso!sN{BU|?Y!H{GJ1RES~l1|=W!Jan+T9oK at Ja&lfQx>QzlA3621 zuL&Rd00c?{Zw3-Fn(wa+xgS0 at J}jOT{-&j(B9^aKnC|0)0{2HkO8NwT8aTDo9)6%x zWB(c%jQ^ZejAD{Y7Yo1-qEQO~;L3cKuhtJm77JBdUq6Uc1dqdw`Q at X@zOiyMi~jT{ zBxGa~;KC91e0xp!uB!+A!OTr at m&wfr8UjM(Sp}!nVS|Y(8*_8>&zaIucfC#=A3}LA zDJ!$=-5A9{*2=1ECxh{*_-+UZ(M(NENl8h4{vLO4c6F8A*;NABCOq5e=0x at bfgf&s z?B0ffHX6v3M$RNboU%Fx79_Do%TNoslhiw}w;rrdL?k3!wX?H(xZO at 1`_PRW(40Pc z#cTDd74v$sH?ml}NukBoc+?W2c*wXCEBtJIFH%>8HG at b29bIlZ+n0=nSa z!{{D`?d>^c=HxKmzKsV6iJXdpzRAfBp8YHHet`pS9cv4ws0E4M$f>24)HIcuYcp at aPu1Y!cwtUON4|w zySpGOe&uGpI6(_g&+hW_c6@$~K}bOn6u~ITaOV!bt*xzL!1o)<1GOXT6V)%E4Vl^5 z47|Lg;KAN#7W$W$-$TZimNu-U?iK#~vo%*w}bxZLD08 zEgD&91b`$YCVmSOf=m1GI+#$yfHTE@%gP$pZH9rtLBFI)$O$Dqy=%~=qvK;KnVo%|ntB8G--UMM2qHQLh5>lb9d`Y~A=aocdM76* zWB~4H*W6h1K^}|h{>XWiDzmWgc0)tM%>4Xi$Yxh}_tv2yQsmTGem;9*Qqt_&+Ar23 z1AtnD2mK%lj&OZsRmPErm68#z0s zp+SK_=Cs|fn%7n%MQJrP*TG{;LN3Y3$oQ^nGemrDYZK$-Iz2gQ?d`n=)|jE0`yjPW zcECbLOblaZXUCUjA2|h?lEQhWqpUozltPK3rly{OJ0geY#>ODyF&%2D;S0E~q_Wa4 zCgtNsHPGGt^k^(cfv%>m&bg1$*l+;C_tm)okacimBoXo$-oKl6ot0K2 z$b)&H-suGevYWLrF)^a(Wo2b=U{(qW3k?hm(2x^n8X7GF14REGM+=w9f;1Vl`mBrl#ie zj~{qo7ww&#`~m{5Kt=YOYV^oJretU5ji28Wj{?nMX{Y1NK}uwYSb_u?`JY*zZo2)@ zG3fC}7GOV1D=Q@(olwyJug2WScBZ5;3S@&SG~U|UTHn$V2Xt0lORKG?2OkDDynDW+ zs>&Zah3s92)F6sN-Jz+e8C+_qP7Y=eFO1sp(dxZBm$OGihNSBu82KDLpL zwCw?OuL1$Mu_50K16oJD>g{LIAu*6M$sx2tnrrsz_dCfkG{d8!25p3sdTDj1z5fb< zb=m&P_&$&ZXLW&pxnky9H2c=!A-N!K=SG5NVW&SH)m@`b++ at dA@h at 8XS6wsP>gqsL zkOV0={Xry1(#gNf0~k$BN=o{0z(8jlYA14b5INT?W)Ko3|1$X9J2VtHT(Sp5og2Nt z9b2o#&A&OlkKc`^-Np2Yk}rp_NAr0U-dR at wueN^shNF@#O9)p+o^MivX+ at p)-o88h z%=7R0q3cHN;*qd%!Qf2hU`TK<4n)g#z_|Ly56Q`4FBf~Z^LU5z;8POtsrL@*ANS7> zrx{sTup%NNU_ at f!X7>P<_?JuWDhZC?DE-}GQ$6o<-fY>pR=uw80yOdhw`We%%8Hm688McZmtE(hpSR=t zaS0_lUibKloFbv8r at zi+=nGxU4nQ7jizqAOKHF-$7Axpd1iv&z&T!B&K%V;n+-A!s zu{pDZP_#?uPyiH%HigBP_Q)2h{b~Mt&T0BR7OhtMjx+ziuglBJV>Txwm-d*i4i|py z?rzI3ZbClH8w8z$SnHAwaH3#WEBK5YHU&0^StyXsHoVdox4+bn0itbUVq)*$KqN at r z8Sl6@%0(^cjO%@IoCA#-=Ad2X5&LNFsQ2Qtx>=>;vNA4>013!FDNIIu2i&|);pHwX z-*B8xlP3kx^gj+C?p+xO_jY7Vg1Q~SXi#8*9FT;f_%=u)lEyBr=Gth zm2(+{36z8cf^!dMY6foK0zhv72?E1YiSyo3li>RGKv1XBHuFpfN^ETGrT&a7naZp; ziLg;K;B{gJoY)c*6M?$+Xbm+>^wA)|nJjVrjm^xAwtshA7jQJsa2g{6Y3*!q+a(|) z!>rZDzk2m5sPAm_bKS=V25Bve$gt3PZna=^G+B>vLoNU9N3M(Vk>3`k(lVPYLd|^C zdxc;LQXY(q-sa}ASWi?H4)u~-)vwsM$2&nnqKnMW&&v&?iF9g51I9J( zo)QQnU4B! z9sIx>W20_sRs=%s=Ya`Fc2ZDZfBi}XrPs2S3|aqny{?z+1O=+R4CTVM?APeuwYrN9 zdHzC;JN^)QQL;=KT~W+b9CZBlH*V`T#Ny!Kgiyyd9q1~U^(4}x08dOxJX?LSZ+`4X z9tM(I2TzS`&TuvzrT6b_d~nnpFVf6NP4yw;)RhI0wf^!o5b;eY#3s9&Af5=dcX7Ho z?Q%*}sY7uULf9`&EZ{f96K3~@aKO}0UXsb_p`oFpiRlYWW at hHt`1qCR`$J7dYg6uZ zRs=t8+jaf-9QADCHiVPnvuC${D&-Fy?F}jr($eCCEXT&hwSYuzZ*NP<%iC6+|M$Q& zW_NFrU;uJ}4J=GxSXk$$XZf`8f+B$D|2!s#8}p^JGrg{k60C)_O8r&xJH81CR2K); z%@iEZSx91qJorE?koW_yO$4kQx`OO)gBkJxC4>vi_!Nw*{Lfi{xjA3tN!-5|=kDuy zLzZWYkN!S(b>*LPcwo#;0eDT-QK+0(V%$aK>+dhh6!n-Xik^puq&w!e82poQ;5d;D zV$te%6?rrRpc*+F0>#Z{NWk2p-crWR?SWdMrr*+%xvPiApCp&5mfDw#<3A;yf at wmb z?wNk?V;{UN=y|l|RXwdV5#1k{v|slIF}7!CXSgwGq$7%s-U`UR%Fe>LU0(5m;oYB2 zYWZ2f)O^PcK!Tq>ePUop2UeawtV~}X%ppbz*w6U1?9d20_m%ewKLsl!>S1bX%Jbs4 zxQwi<_4d!M^Mdy=TG{oU>ZBmPXbZk*8e2qa#aTB|QM0*eJZ~ zcTd$lFHj4N$w*^UR`ZHCW_wg>Y at jG}ztVEk at 1Eo)t-P;C&fkCmSO^_)X(Urt^g ztI)xyQN^oscV!k5xvIZ6CZg|@P9~WaQ$MWl zW);=5FVgcv1+%h?Qe0&ZDhBx4|a=Q*+X|2cztc(|HQr${2MUH;#?(Y}6 z=)8=QH!+!~`rB9+8J3ntkMa2uIvO$7rI6mmd%`Kbt`C#T8WmZLL%3q%jO!!AtD^#>(9UQ3>nk-4&4l(q*VnK5;HGg=3A|Qnjf_Noz;u{C+s9{2 z)6egGEN}8U<+iii#GGLH?d>H47DV}0+}cF7!u)h?DWgjVOLeKz>!v1Q48_f$)twy& z%-rNXwLSW%F3Mm2**ukcjmeI1n=z>FTDO*SjojsQPD#mGmU#xNY$jq$OSz(?ZaNL~ z-d}E5c8`fAtoZNAojfi}W+wB$A5 zXDJBc{n6}NX-O{LO8Z$+=iLRl`R35yEDe>B@bF<77MKlOJ{gYHFIf|CpRi z>S%AzwQ;ur-;SFr_}e(7bZ*WVlgAE6>2cD3y7TAD`#6ys`cS!?hFQZXsRULmSKehF zDL1@&JaW9dI27ZEiBJ6%RRTPP`N#76WW5W;){e2Y5y>NakemG?1t}^0rlzKEoTOV0 zzVoxQEr^a#)3BM#42;#j%;DDPNw{C!MTgm`8?%Nk*V{-=LQ>+qK3+OAGb1s<`rFRZ zU5 at K~_vG3E)cF-pOlN0jT+~FlnZnz1w(OA=c2r-6ZT1dKgX zgp|6B49 at C!W&U_%4^-ysTV*x$0s`^#i;MSQ at Bkp|!5F>^4mJb?58>+6*49>+mBnRe zVbOP}O^p8Vp=7i9Z&MGUwHL`V3A5hhLYGDcKHB$R{BbE-85kMI26GgsycC>{Go;P- zSBAbd>(tmAl>aTNeI}u+tNXpPGkZ$uAfJS552OItg)2|>8)!vbWhF1hg`QMj)>Cp! zpUYRSl>h#1sP^nx2N4m`hsCS9=eSL$8-kcV6jW5 at Aa*6lt12pO?S?O41n%9t=M1Q+ zrliyYu4fdY{sTGp$aAT_Q9homyz@`=3Ms-SP{*vlhSRgDgxp>pdM73(D!S6hzt at u_ zDA)A9?vTAmalj2jtJ>B?LPBBj61TQAbLc)!Kfiaw;K*wh{ z_0Ys5L7l at CfJJ|@&dEqXKtT2;QR!=pD`g;>BOb}~&tU4l_w|J%yH?|2?AlDovlUE4 zL6i at xx6Y1Vni?3iWGkdxNK7cJsda)kj|&RICJqw+-rfClzKR{<=(b){qK3Nq-899o z1%-tg_V#!4^YZdsP60-=%1!ZBM~dkNM at Gz`OCTJdWS>gkBm%I_wl>n&{|v(SrM0bX z`h at 89iTTN^j?r!mOfxexxP7eDO~am^p0%ynw*4xbALqeV9~`F?_4RL*S643zgz%Yc z{Hld!D;n8#w_NTJDtq(h4W;!x?+ef2G_fl^*;KqV>(8Bd&6JX z8wl^;zrVDy!obQJw5m8BV%L052J_;l&aogs5<0WA6iCP at F@ya50ZC5R=!B8K=m>ep zTwGG}C4tVwx*z=!-5Qtgr z0CA~z>6yD%9taAGDJWcnTg?lQq(1 at 4ECJ*Q>@dW=e%EK!9?Rmzp84BH=Ii5?{4N{R zkpG=dEaFeK8(awy$ORO>MV(%0p1Dir>FH^|zw&HuZcao`FJ;wUipu%#g%>|R|L3VG zT0j+>;qq`Si+3!`4SrXy(ITsVM?2k|rEs&NHhLRX4GjtiVHAq|jFnun!#0i0IM!R9NVjk->{P z+uMCt!vp$O)v2ps77f29+o!(Q2b#3RWL|Gz_P at wm-f=7 zOZuk=>qlq1y|wrO<~~Rxz&L at q41D+Qt;mfTft#ZIX>CrZ at vptT*Dhba9Q*IXEi=o@ zT~1QIVj3D6$iZN0YHH-X2GV~CMjBq^*k*cq`VwV`%P3SgF56%HHFb5rfg~O4Z|?MD zWM)W&a2=hV{(%|b_d5B%TDa~&s at pGqjqH_5GO}JGD-mzVxOmNLMzRuRMcJz)T&~TP z8!da3n^hDcn~acKq(n#=$qdQ(o#*$ye^%e_z0dhR=QEz?oTKn#VWE~St||}p-=iBN zrK^0v*MbL`;BPtl%!DF%mV;G)e5j<6k&!_&vIZ#>^nBXpW-uzq1~x=Vjm&j50BR zDWSaqyg*q?tNrRqHEL2)EC9osf5!KclX#WP&npO(=N3>5KtutbqEn#YkdTYRW%iH_ zySK_;Z7~6tip$7Yh1knvpeixca)^cwz&lSC{!9CrS(m@${QMZLy=~xr>VdX!L}{rstdqXhj!i8)rJ~}*=I`Ilt*tcC(b4F(E=Mz} zhKwDkBR*3#vK;X9a_FRi%V^{?bvZC7B+8Qz$k{3WIZc?&$X)P=)P(3~% z^VA6}_9LBvG9N1FQ9X0U?ELvSV5_E)k-PJ$udhH(e3_h#JS^+*s9uJHA*Z;Qum_HJ zW~@O)_wD|{=cYnbouTVL;!s29m!=4AMNe*|Ed%tv9s(sJHd@4#$wwt6$IDuUF}uI}!zXul{yZuz1zBJu)(VQI!sX39 ztK`Jg=W#{D3cZjQa+kXg`p*ndf!AukMMYD?rjNeh-x&ju at ImQ!?i?QmvNAgDd*b9t zbB8ywoN*(x*-Cq&B?8GMQm1!r_369Wven=V}6rjE(O--tm)z#%M%{1uL zzP^6D^v_!gB?|nLvi}SQX!OQXGktd<{NkCs6a>Qw$P*kML z-t&FF(XcUQF{b$<8UpozA zhw^4mu^!_}SRQfB&VBik$IQ&E+jX@*c$s==Xs9M=@#y9aHJ82zak%@6k9X8A=;-MD z9WZwgAVP>M!DnYH;N8$r9)z- at x>{#(tO1SpL!%18fj_VdUz==a{`&p9h?3Ib#_bhm zUq3&RAr8k1tT6$U*&IcW=KIZjdQsjzo~<5!v^9p+!k8?fMu9!v1o0Ug8{<~LsS1+9 zHtZ9F`@HyU6%|Mo#7z77`IFo!Md)G_lt$)>i;>;zQw93BZMM0#fKT;IOmxl7In&b8 z(B%#6GKflvl#7dt^ic6jbV1Zjl6>9W9ceA~PiUy}QPp4!Gc$8dT^$--=I!GXQCoZZ zRz^M#KR-VUuObVuvIKv3hbSD%P+DdI-oGl6m+(N?z zwFqnt?-`=xV%FCDe~dQ@`1tq)ug&b=T3=ue3JOBs0m60Q{d=!C+~g?gqImEiv7q3u zg2F;gvD at er4MMjGuMYK^)NhPkwe5OndP5IDJ*g-B2R3pnSi}L;;pX32eAX6qXLPEZ{K1dKN=_&M0`D?fwI@@ z^2XhGcp$%`f&;9F4!oQ!)etlcfn?h$&Jc|~(UJaQYH_qyzJyFhV-`RLDP3vP=DBnC zZro|VZ>Z%E%`ZZu&q4(S01ylEMIGvcBZ^J3OQawNgIB&Xq6+z*4B33Y?|tZe6r{`W zga>P_T{0>)nH?-dl_7cg`JdWO8REU8(~`MBlsBhKSa|>VAcrRqrpm*2B>CgW`YtYF zVkTKEKxwcKKYaM$&Ce;s6PK2Jr=+B$1 zEd($KrTbT#q+Rr3l@ z^75#Ylan`Bv-c7~o`Cezb~b(UgiAtFvNcWAu;S`x{RQ>&D?#AIw2j6l+d}oGAc~SqD zS^htdt7Q$8-y9?AIbOaT4O*i&Q!WbhfW=G0{iu(rl!XD z)9_(%`^kK#^E{7{3`QQA<@0#9uMf`JaFMP;d^S}@JL=AiAT^3 zs7cuDsXf!;Qc`V2xcg1qT6%i1Pwlm}|KFtv(hrqM1#hiM06e4;i4n_FoirHKO;%N< zkeQh&sHn&_Jv}WhE898NaQ*G8uMa`9< zdQe%d)ocn!Qb)j$$hGn(PZFsdkC|k7l6IFGiCACXr`_}L$4eONa)*ZW^1_0G3ZJQt zO87-`KmsIU3?SK4d$4s!FwcSQ=>9B#r=@8EAUWsyIqS at IZj6ho>$zin at Eb|bHx|cT zG1ed<$AUlYiHnGctOH6`*+8g{AOCBB8vdD3SSZ@&B*(|k|9&hxFEyv6gbBE$dzf5O z0^%sAygX%Y&I7TBMUjzv)fbJ*KG+11P_SwOk!aT7#md4WNYpbmGovEv6+AUw{y-0Y z|E#6u<2H%gU2|uXs{`fmAX?B+I8nnpRp>Pxzvn=q=2r=%P3dfHC9s3}1zeMC;%;q; zX7*YAh69-qB}e!8?8g!xZ~KyMpl-xC0KF3mY*+;Y$x51Esb~7}@MS8qCDR#O6{fdv19-I|dx7-NOy at QBrLsv&12G0~OHH4+FqtTZ9PU z%&n~%?i$g77%S5f!utU-pnIUc2d60|VRZZUp&}g+VPJn4n3$+BiM&c|m;rG0n5Gg- zGE$Zhp1;JzoIX~$2aNTMj|*T5S#L#l%>JM%7X%6lc&F9*fL-Dw^t7M{EB1_f$vP za{bpYONnTgAD6qdJ}}C^F0Ew|k~Nf3HmUwzNbtMq^#P zyxv4cMV-gta1H8P=8w}$&p(R+rI0c^KQB+zYXg)xkHtoI2gXE4OMK$y=B at xOSGlz0 zO~S8wJV^42dinWP*nM=unwZE^y`iILtMUGd%_Z2p5X=@}CZ+=i4ghlATDgaAKa)j- zwB-TzgcIfdR#8E0@^|o(;F){(oaN-?kRM%HSxIj{gs-1}l&X=i^siGy6Vlw=#D^#9 zDX3-!%@_VP_dea5A!J_?pgaT;qz7sprKO|utf~0f2bP2zYnX9R{mqj3aTW(mWK^`=A%tR}pb!dpIE@&~vRQxO0Wd)`)mIj8> z4)xrSF{rK#j^y+?q!(LUc?crjkXhRe6KvJ!?*J`=ju^xp_& zUZtyF5 at cWHBzQB1(D*;>Q!x^|{a;fy*osad at BIf4;(_oY`irObV^Benu!ufTa{zA9~kIYioEQ+~JFyc>&_HsV-ezU7G(IhSaRAWZ*9U z#W8*mJm5Sb6=_0$%@q>W-+{A0@%5e^6h)PU^704HmDxoYiD`|$3Paya6wz&IZ`b*) z=Y@?8H60YX_5^@^WMl-{k-$JjKzvYv)F8BAXScwvZn(bj->_LncXviZL&Iu^!hUWo zP>Vl5*AXHksYh%5=utJ{%^hXPLO^_Q{q)G>oj%<>GIE5CjcsPIFyW(EIfaL7$?RE6Ja|y!IEfnL at 9&SqkXqQ5cm|L# zB=fHgAqiv0HrZ$Y(n@{E3JPYcuTb;Kdi}tWe8E)K3tKWdQ$O$+9*7W(bqB7F2g!T F{tpk90)YSk diff --git a/docs/source/index.rst b/docs/source/index.rst --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -12,8 +12,12 @@ :maxdepth: 2 metadata + pkgutil + depgraph new_commands test_framework + pypi + version Indices and tables ================== diff --git a/docs/source/metadata.rst b/docs/source/metadata.rst --- a/docs/source/metadata.rst +++ b/docs/source/metadata.rst @@ -5,19 +5,19 @@ Distutils2 provides a :class:`DistributionMetadata` class that can read and write Metadata files. This class is compatible with all versions of Metadata: -- 1.0 : PEP 241 -- 1.1 : PEP 314 -- 1.2 : PEP 345 +* 1.0 : :pep:`241` +* 1.1 : :pep:`314` +* 1.2 : :pep:`345` -The PEP 345 implementation supports the micro-language for the environment +The :pep:`345` implementation supports the micro-language for the environment markers, and displays warnings when versions that are supposed to be -PEP 386 are violating the scheme. +:pep:`386` are violating the scheme. Reading metadata ================ -The :class:`DistributionMetadata` class can be instanciated with the path of +The :class:`DistributionMetadata` class can be instantiated with the path of the metadata file, and provides a dict-like interface to the values:: >>> from distutils2.metadata import DistributionMetadata @@ -32,14 +32,14 @@ ["pywin32; sys.platform == 'win32'", "Sphinx"] The fields that supports environment markers can be automatically ignored if -the object is instanciated using the ``platform_dependant`` option. +the object is instantiated using the ``platform_dependent`` option. :class:`DistributionMetadata` will interpret in the case the markers and will automatically remove the fields that are not compliant with the running environment. Here's an example under Mac OS X. The win32 dependency we saw earlier is ignored:: >>> from distutils2.metadata import DistributionMetadata - >>> metadata = DistributionMetadata('PKG-INFO', platform_dependant=True) + >>> metadata = DistributionMetadata('PKG-INFO', platform_dependent=True) >>> metadata['Requires-Dist'] ['bar'] @@ -53,7 +53,7 @@ >>> from distutils2.metadata import DistributionMetadata >>> context = {'sys.platform': 'win32'} - >>> metadata = DistributionMetadata('PKG-INFO', platform_dependant=True, + >>> metadata = DistributionMetadata('PKG-INFO', platform_dependent=True, ... execution_context=context) ... >>> metadata['Requires-Dist'] = ["pywin32; sys.platform == 'win32'", @@ -71,15 +71,15 @@ >>> metadata.write('/to/my/PKG-INFO') The class will pick the best version for the metadata, depending on the values -provided. If all the values provided exists in all versions, the class will +provided. If all the values provided exist in all versions, the class will use :attr:`metadata.PKG_INFO_PREFERRED_VERSION`. It is set by default to 1.0. Conflict checking and best version ================================== -Some fields in PEP 345 have to follow a version scheme in their versions -predicate. When the scheme is violated, a warning is emited:: +Some fields in :pep:`345` have to follow a version scheme in their versions +predicate. When the scheme is violated, a warning is emitted:: >>> from distutils2.metadata import DistributionMetadata >>> metadata = DistributionMetadata() @@ -89,7 +89,4 @@ -XXX talk about check() - - - +.. TODO talk about check() diff --git a/docs/source/pkgutil.rst b/docs/source/pkgutil.rst new file mode 100644 --- /dev/null +++ b/docs/source/pkgutil.rst @@ -0,0 +1,143 @@ +======= +pkgutil +======= + +Introduction +============ + +This module provides the necessary functions to provide support for +the "Importer Protocol" as described in :pep:`302` and for working with +the database of installed Python distributions which is specified in +:pep:`376`. In addition to the functions required in :pep:`376`, back support +for older ``.egg`` and ``.egg-info`` distributions is provided as well. These +distributions are represented by the class +:class:`distutils2._backport.pkgutil.EggInfoDistribution` and +most functions provide an extra argument ``use_egg_info`` which indicates if +they should consider these old styled distributions. In this document, +first a complete documentation of the functions and classes +is provided and then several use cases are presented. + +API Reference +============= + +.. automodule:: distutils2._backport.pkgutil + :members: + +Example Usage +============= + +Print All Information About a Distribution +++++++++++++++++++++++++++++++++++++++++++ + +Given a path to a ``.dist-info`` distribution, we shall print out all +information that can be obtained using functions provided in this module:: + + from distutils2._backport import pkgutil + import sys + + path = raw_input() # read the path from the keyboard + # first create the Distribution instance + try: + dist = pkgutil.Distribution(path) + except IOError: + print('No such distribution') + sys.exit(1) + + print('Information about %s' % dist.name) + print('Files') + print('=====') + for (path, md5, size) in dist.get_installed_files(): + print('* Path: %s' % path) + print(' Hash %s, Size: %s bytes' % (md5, size)) + print('Metadata') + print('========') + for key, value in dist.metadata.items(): + print('%20s: %s' % (key, value)) + print('Extra') + print('=====') + if dist.requested: + print('* It was installed by user request') + else: + print('* It was installed as a dependency') + +If we save the script above as ``print_info.py`` and we are intested in the +distribution located at +``/home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9`` +then by typing in the console: + +.. code-block:: bash + + $ echo /home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info | python print_info.py + +we get the following output: + +.. code-block:: none + + Information about choxie + Files + ===== + * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9/truffles.py + Hash 5e052db6a478d06bad9ae033e6bc08af, Size: 111 bytes + * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py + Hash ac56bf496d8d1d26f866235b95f31030, Size: 214 bytes + * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py + Hash 416aab08dfa846f473129e89a7625bbc, Size: 25 bytes + * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER + Hash d41d8cd98f00b204e9800998ecf8427e, Size: 0 bytes + * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA + Hash 696a209967fef3c8b8f5a7bb10386385, Size: 225 bytes + * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED + Hash d41d8cd98f00b204e9800998ecf8427e, Size: 0 bytes + * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD + Hash None, Size: None bytes + Metadata + ======== + Metadata-Version: 1.2 + Name: choxie + Version: 2.0.0.9 + Platform: [] + Supported-Platform: UNKNOWN + Summary: Chocolate with a kick! + Description: UNKNOWN + Keywords: [] + Home-page: UNKNOWN + Author: UNKNOWN + Author-email: UNKNOWN + Maintainer: UNKNOWN + Maintainer-email: UNKNOWN + License: UNKNOWN + Classifier: [] + Download-URL: UNKNOWN + Obsoletes-Dist: ['truffles (<=0.8,>=0.5)', 'truffles (<=0.9,>=0.6)'] + Project-URL: [] + Provides-Dist: ['truffles (1.0)'] + Requires-Dist: ['towel-stuff (0.1)'] + Requires-Python: UNKNOWN + Requires-External: [] + Extra + ===== + * It was installed as a dependency + +Find Out Obsoleted Distributions +++++++++++++++++++++++++++++++++ + +Now, we take tackle a different problem, we are interested in finding out +which distributions have been obsoleted. This can be easily done as follows:: + + from distutils2._backport import pkgutil + + # iterate over all distributions in the system + for dist in pkgutil.get_distributions(): + name = dist.name + version = dist.metadata['Version'] + # find out which distributions obsolete this name/version combination + for obsoleted_by in pkgutil.obsoletes_distribution(name, version): + print('%s(%s) is obsoleted by %s' % (name, version, obsoleted_by.name)) + +This is how the output might look like: + +.. code-block:: none + + strawberry(0.6) is obsoleted by choxie + grammar(1.0a4) is obsoleted by towel-stuff + diff --git a/docs/source/pypi.rst b/docs/source/pypi.rst new file mode 100644 --- /dev/null +++ b/docs/source/pypi.rst @@ -0,0 +1,195 @@ +========================================= +Tools to query PyPI: the PyPI package +========================================= + +Distutils2 comes with a module (eg. `distutils2.pypi`) which contains +facilities to access the Python Package Index (named "pypi", and avalaible on +the url `http://pypi.python.org`. + +There is two ways to retrieve data from pypi: using the *simple* API, and using +*XML-RPC*. The first one is in fact a set of HTML pages avalaible at +`http://pypi.python.org/simple/`, and the second one contains a set of XML-RPC +methods. In order to reduce the overload caused by running distant methods on +the pypi server (by using the XML-RPC methods), the best way to retrieve +informations is by using the simple API, when it contains the information you +need. + +Distutils2 provides two python modules to ease the work with those two APIs: +`distutils2.pypi.simple` and `distutils2.pypi.xmlrpc`. Both of them depends on +another python module: `distutils2.pypi.dist`. + + +Requesting information via the "simple" API `distutils2.pypi.simple` +==================================================================== + +`distutils2.pypi.simple` can process the Python Package Index and return and +download urls of distributions, for specific versions or latests, but it also +can process external html pages, with the goal to find *pypi unhosted* versions +of python distributions. + +You should use `distutils2.pypi.simple` for: + + * Search distributions by name and versions. + * Process pypi external pages. + * Download distributions by name and versions. + +And should not be used to: + + * Things that will end up in too long index processing (like "finding all + distributions with a specific version, no matters the name") + +API +---- + +Here is a complete overview of the APIs of the SimpleIndex class. + +.. autoclass:: distutils2.pypi.simple.SimpleIndex + :members: + +Usage Exemples +--------------- + +To help you understand how using the `SimpleIndex` class, here are some basic +usages. + +Request PyPI to get a specific distribution +++++++++++++++++++++++++++++++++++++++++++++ + +Supposing you want to scan the PyPI index to get a list of distributions for +the "foobar" project. You can use the "find" method for that:: + + >>> from distutils2.pypi import SimpleIndex + >>> client = SimpleIndex() + >>> client.find("foobar") + [, ] + +Note that you also can request the client about specific versions, using version +specifiers (described in `PEP 345 +`_):: + + >>> client.find("foobar < 1.2") + [, ] + +`find` returns a list of distributions, but you also can get the last +distribution (the more up to date) that fullfil your requirements, like this:: + + >>> client.get("foobar < 1.2") + + +Download distributions ++++++++++++++++++++++++ + +As it can get the urls of distributions provided by PyPI, the `SimpleIndex` +client also can download the distributions and put it for you in a temporary +destination:: + + >>> client.download("foobar") + /tmp/temp_dir/foobar-1.2.tar.gz + +You also can specify the directory you want to download to:: + + >>> client.download("foobar", "/path/to/my/dir") + /path/to/my/dir/foobar-1.2.tar.gz + +While downloading, the md5 of the archive will be checked, if not matches, it +will try another time, then if fails again, raise `MD5HashDoesNotMatchError`. + +Internally, that's not the SimpleIndex which download the distributions, but the +`PyPIDistribution` class. Please refer to this documentation for more details. + +Following PyPI external links +++++++++++++++++++++++++++++++ + +The default behavior for distutils2 is to *not* follow the links provided +by HTML pages in the "simple index", to find distributions related +downloads. + +It's possible to tell the PyPIClient to follow external links by setting the +`follow_externals` attribute, on instanciation or after:: + + >>> client = SimpleIndex(follow_externals=True) + +or :: + + >>> client = SimpleIndex() + >>> client.follow_externals = True + +Working with external indexes, and mirrors ++++++++++++++++++++++++++++++++++++++++++++ + +The default `SimpleIndex` behavior is to rely on the Python Package index stored +on PyPI (http://pypi.python.org/simple). + +As you can need to work with a local index, or private indexes, you can specify +it using the index_url parameter:: + + >>> client = SimpleIndex(index_url="file://filesystem/path/") + +or :: + + >>> client = SimpleIndex(index_url="http://some.specific.url/") + +You also can specify mirrors to fallback on in case the first index_url you +provided doesnt respond, or not correctly. The default behavior for +`SimpleIndex` is to use the list provided by Python.org DNS records, as +described in the :pep:`381` about mirroring infrastructure. + +If you don't want to rely on these, you could specify the list of mirrors you +want to try by specifying the `mirrors` attribute. It's a simple iterable:: + + >>> mirrors = ["http://first.mirror","http://second.mirror"] + >>> client = SimpleIndex(mirrors=mirrors) + + +Requesting informations via XML-RPC (`distutils2.pypi.XmlRpcIndex`) +========================================================================== + +The other method to request the Python package index, is using the XML-RPC +methods. Distutils2 provides a simple wrapper around `xmlrpclib +`_, that can return you +`PyPIDistribution` objects. + +:: + >>> from distutils2.pypi import XmlRpcIndex() + >>> client = XmlRpcIndex() + + +PyPI Distributions +================== + +Both `SimpleIndex` and `XmlRpcIndex` classes works with the classes provided +in the `pypi.dist` package. + +`PyPIDistribution` +------------------ + +`PyPIDistribution` is a simple class that defines the following attributes: + +:name: + The name of the package. `foobar` in our exemples here +:version: + The version of the package +:location: + If the files from the archive has been downloaded, here is the path where + you can find them. +:url: + The url of the distribution + +.. autoclass:: distutils2.pypi.dist.PyPIDistribution + :members: + +`PyPIDistributions` +------------------- + +The `dist` module also provides another class, to work with lists of +`PyPIDistribution` classes. It allow to filter results and is used as a +container of + +.. autoclass:: distutils2.pypi.dist.PyPIDistributions + :members: + +At a higher level +================= + +XXX : A description about a wraper around PyPI simple and XmlRpc Indexes +(PyPIIndex ?) diff --git a/docs/source/version.rst b/docs/source/version.rst new file mode 100644 --- /dev/null +++ b/docs/source/version.rst @@ -0,0 +1,64 @@ +====================== +Working with versions +====================== + +Distutils2 ships with a python package capable to work with version numbers. +It's an implementation of version specifiers `as defined in PEP 345 +`_ about +Metadata. + +`distutils2.version.NormalizedVersion` +====================================== + +A Normalized version corresponds to a specific version of a distribution, as +described in the PEP 345. So, you can work with the `NormalizedVersion` like +this:: + + >>> NormalizedVersion("1.2b1") + NormalizedVersion('1.2b1') + +If you try to use irrational version specifiers, an `IrrationalVersionError` +will be raised:: + + >>> NormalizedVersion("irrational_version_number") + ... + IrrationalVersionError: irrational_version_number + +You can compare NormalizedVersion objects, like this:: + + >>> NormalizedVersion("1.2b1") < NormalizedVersion("1.2") + True + +NormalizedVersion is used internally by `VersionPredicate` to do his stuff. + +`distutils2.version.suggest_normalized_version` +----------------------------------------------- + +You also can let the normalized version be suggested to you, using the +`suggest_normalized_version` function:: + + >>> suggest_normalized_version('2.1-rc1') + 2.1c1 + +If `suggest_normalized_version` can't actually suggest you a version, it will +return `None`:: + + >>> print suggest_normalized_version('not a version') + None + +`distutils2.version.VersionPredicate` +===================================== + +`VersionPredicate` knows how to parse stuff like "ProjectName (>=version)", the +class also provides a `match` method to test if a version number is the version +predicate:: + + >>> version = VersionPredicate("ProjectName (<1.2,>1.0") + >>> version.match("1.2.1") + False + >>> version.match("1.1.1") + True + +`is_valid_predicate` +-------------------- + diff --git a/src/CONTRIBUTORS.txt b/src/CONTRIBUTORS.txt --- a/src/CONTRIBUTORS.txt +++ b/src/CONTRIBUTORS.txt @@ -5,7 +5,7 @@ Distutils2 is a project that was started and that is maintained by Tarek Ziad??, and many people are contributing to the project. -If you did, please add your name below in alphabetical order ! +If you did, please add your name below in alphabetical order! Thanks to: @@ -13,14 +13,18 @@ - Pior Bastida - Titus Brown - Nicolas Cadou +- Konrad Delong - Josip Djolonga - Yannick Gringas +- Jeremy Kloth +- Martin von L??wis - Carl Meyer +- Alexis M??taireau +- Zubin Mithra - Michael Mulich -- George Peris +- George Peristerakis - Sean Reifschneider +- Luis Rojas - Erik Rose - Brian Rosner - Alexandre Vassalotti -- Martin von L??wis - diff --git a/src/DEVNOTES.txt b/src/DEVNOTES.txt --- a/src/DEVNOTES.txt +++ b/src/DEVNOTES.txt @@ -3,8 +3,8 @@ - Distutils2 runs from 2.4 to 3.2 (3.x not implemented yet), so make sure you don't use a syntax that doesn't work under - a specific Python version. + one of these Python versions. - Always run tests.sh before you push a change. This implies - that you have all Python versions installed. + that you have all Python versions installed from 2.4 to 2.6. diff --git a/src/Modules/_hashopenssl.c b/src/Modules/_hashopenssl.c new file mode 100644 --- /dev/null +++ b/src/Modules/_hashopenssl.c @@ -0,0 +1,524 @@ +/* Module that wraps all OpenSSL hash algorithms */ + +/* + * Copyright (C) 2005 Gregory P. Smith (greg at krypto.org) + * Licensed to PSF under a Contributor Agreement. + * + * Derived from a skeleton of shamodule.c containing work performed by: + * + * Andrew Kuchling (amk at amk.ca) + * Greg Stein (gstein at lyra.org) + * + */ + +#define PY_SSIZE_T_CLEAN + +#include "Python.h" +#include "structmember.h" + +#if (PY_VERSION_HEX < 0x02050000) +#define Py_ssize_t int +#endif + +/* EVP is the preferred interface to hashing in OpenSSL */ +#include + +#define MUNCH_SIZE INT_MAX + + +#ifndef HASH_OBJ_CONSTRUCTOR +#define HASH_OBJ_CONSTRUCTOR 0 +#endif + +typedef struct { + PyObject_HEAD + PyObject *name; /* name of this hash algorithm */ + EVP_MD_CTX ctx; /* OpenSSL message digest context */ +} EVPobject; + + +static PyTypeObject EVPtype; + + +#define DEFINE_CONSTS_FOR_NEW(Name) \ + static PyObject *CONST_ ## Name ## _name_obj; \ + static EVP_MD_CTX CONST_new_ ## Name ## _ctx; \ + static EVP_MD_CTX *CONST_new_ ## Name ## _ctx_p = NULL; + +DEFINE_CONSTS_FOR_NEW(md5) +DEFINE_CONSTS_FOR_NEW(sha1) +DEFINE_CONSTS_FOR_NEW(sha224) +DEFINE_CONSTS_FOR_NEW(sha256) +DEFINE_CONSTS_FOR_NEW(sha384) +DEFINE_CONSTS_FOR_NEW(sha512) + + +static EVPobject * +newEVPobject(PyObject *name) +{ + EVPobject *retval = (EVPobject *)PyObject_New(EVPobject, &EVPtype); + + /* save the name for .name to return */ + if (retval != NULL) { + Py_INCREF(name); + retval->name = name; + } + + return retval; +} + +/* Internal methods for a hash object */ + +static void +EVP_dealloc(PyObject *ptr) +{ + EVP_MD_CTX_cleanup(&((EVPobject *)ptr)->ctx); + Py_XDECREF(((EVPobject *)ptr)->name); + PyObject_Del(ptr); +} + + +/* External methods for a hash object */ + +PyDoc_STRVAR(EVP_copy__doc__, "Return a copy of the hash object."); + +static PyObject * +EVP_copy(EVPobject *self, PyObject *unused) +{ + EVPobject *newobj; + + if ( (newobj = newEVPobject(self->name))==NULL) + return NULL; + + EVP_MD_CTX_copy(&newobj->ctx, &self->ctx); + return (PyObject *)newobj; +} + +PyDoc_STRVAR(EVP_digest__doc__, +"Return the digest value as a string of binary data."); + +static PyObject * +EVP_digest(EVPobject *self, PyObject *unused) +{ + unsigned char digest[EVP_MAX_MD_SIZE]; + EVP_MD_CTX temp_ctx; + PyObject *retval; + unsigned int digest_size; + + EVP_MD_CTX_copy(&temp_ctx, &self->ctx); + digest_size = EVP_MD_CTX_size(&temp_ctx); + EVP_DigestFinal(&temp_ctx, digest, NULL); + + retval = PyString_FromStringAndSize((const char *)digest, digest_size); + EVP_MD_CTX_cleanup(&temp_ctx); + return retval; +} + +PyDoc_STRVAR(EVP_hexdigest__doc__, +"Return the digest value as a string of hexadecimal digits."); + +static PyObject * +EVP_hexdigest(EVPobject *self, PyObject *unused) +{ + unsigned char digest[EVP_MAX_MD_SIZE]; + EVP_MD_CTX temp_ctx; + PyObject *retval; + char *hex_digest; + unsigned int i, j, digest_size; + + /* Get the raw (binary) digest value */ + EVP_MD_CTX_copy(&temp_ctx, &self->ctx); + digest_size = EVP_MD_CTX_size(&temp_ctx); + EVP_DigestFinal(&temp_ctx, digest, NULL); + + EVP_MD_CTX_cleanup(&temp_ctx); + + /* Create a new string */ + /* NOTE: not thread safe! modifying an already created string object */ + /* (not a problem because we hold the GIL by default) */ + retval = PyString_FromStringAndSize(NULL, digest_size * 2); + if (!retval) + return NULL; + hex_digest = PyString_AsString(retval); + if (!hex_digest) { + Py_DECREF(retval); + return NULL; + } + + /* Make hex version of the digest */ + for(i=j=0; i> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + } + return retval; +} + +PyDoc_STRVAR(EVP_update__doc__, +"Update this hash object's state with the provided string."); + +static PyObject * +EVP_update(EVPobject *self, PyObject *args) +{ + unsigned char *cp; + Py_ssize_t len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + if (len > 0 && len <= MUNCH_SIZE) { + EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, + unsigned int)); + } else { + Py_ssize_t offset = 0; + while (len) { + unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; + EVP_DigestUpdate(&self->ctx, cp + offset, process); + len -= process; + offset += process; + } + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef EVP_methods[] = { + {"update", (PyCFunction)EVP_update, METH_VARARGS, EVP_update__doc__}, + {"digest", (PyCFunction)EVP_digest, METH_NOARGS, EVP_digest__doc__}, + {"hexdigest", (PyCFunction)EVP_hexdigest, METH_NOARGS, EVP_hexdigest__doc__}, + {"copy", (PyCFunction)EVP_copy, METH_NOARGS, EVP_copy__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +EVP_get_block_size(EVPobject *self, void *closure) +{ + return PyInt_FromLong(EVP_MD_CTX_block_size(&((EVPobject *)self)->ctx)); +} + +static PyObject * +EVP_get_digest_size(EVPobject *self, void *closure) +{ + return PyInt_FromLong(EVP_MD_CTX_size(&((EVPobject *)self)->ctx)); +} + +static PyMemberDef EVP_members[] = { + {"name", T_OBJECT, offsetof(EVPobject, name), READONLY, PyDoc_STR("algorithm name.")}, + {NULL} /* Sentinel */ +}; + +static PyGetSetDef EVP_getseters[] = { + {"digest_size", + (getter)EVP_get_digest_size, NULL, + NULL, + NULL}, + {"block_size", + (getter)EVP_get_block_size, NULL, + NULL, + NULL}, + /* the old md5 and sha modules support 'digest_size' as in PEP 247. + * the old sha module also supported 'digestsize'. ugh. */ + {"digestsize", + (getter)EVP_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + + +static PyObject * +EVP_repr(PyObject *self) +{ + char buf[100]; + PyOS_snprintf(buf, sizeof(buf), "<%s HASH object @ %p>", + PyString_AsString(((EVPobject *)self)->name), self); + return PyString_FromString(buf); +} + +#if HASH_OBJ_CONSTRUCTOR +static int +EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", "string", NULL}; + PyObject *name_obj = NULL; + char *nameStr; + unsigned char *cp = NULL; + Py_ssize_t len = 0; + const EVP_MD *digest; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s#:HASH", kwlist, + &name_obj, &cp, &len)) { + return -1; + } + + if (!PyArg_Parse(name_obj, "s", &nameStr)) { + PyErr_SetString(PyExc_TypeError, "name must be a string"); + return -1; + } + + digest = EVP_get_digestbyname(nameStr); + if (!digest) { + PyErr_SetString(PyExc_ValueError, "unknown hash function"); + return -1; + } + EVP_DigestInit(&self->ctx, digest); + + self->name = name_obj; + Py_INCREF(self->name); + + if (cp && len) { + if (len > 0 && len <= MUNCH_SIZE) { + EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, + unsigned int)); + } else { + Py_ssize_t offset = 0; + while (len) { + unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; + EVP_DigestUpdate(&self->ctx, cp + offset, process); + len -= process; + offset += process; + } + } + } + + return 0; +} +#endif + + +PyDoc_STRVAR(hashtype_doc, +"A hash represents the object used to calculate a checksum of a\n\ +string of information.\n\ +\n\ +Methods:\n\ +\n\ +update() -- updates the current digest with an additional string\n\ +digest() -- return the current digest value\n\ +hexdigest() -- return the current digest as a string of hexadecimal digits\n\ +copy() -- return a copy of the current hash object\n\ +\n\ +Attributes:\n\ +\n\ +name -- the hash algorithm being used by this object\n\ +digest_size -- number of bytes in this hashes output\n"); + +static PyTypeObject EVPtype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_hashlib.HASH", /*tp_name*/ + sizeof(EVPobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + EVP_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + EVP_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + hashtype_doc, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + EVP_methods, /* tp_methods */ + EVP_members, /* tp_members */ + EVP_getseters, /* tp_getset */ +#if 1 + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ +#endif +#if HASH_OBJ_CONSTRUCTOR + (initproc)EVP_tp_init, /* tp_init */ +#endif +}; + +static PyObject * +EVPnew(PyObject *name_obj, + const EVP_MD *digest, const EVP_MD_CTX *initial_ctx, + const unsigned char *cp, Py_ssize_t len) +{ + EVPobject *self; + + if (!digest && !initial_ctx) { + PyErr_SetString(PyExc_ValueError, "unsupported hash type"); + return NULL; + } + + if ((self = newEVPobject(name_obj)) == NULL) + return NULL; + + if (initial_ctx) { + EVP_MD_CTX_copy(&self->ctx, initial_ctx); + } else { + EVP_DigestInit(&self->ctx, digest); + } + + if (cp && len) { + if (len > 0 && len <= MUNCH_SIZE) { + EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, + unsigned int)); + } else { + Py_ssize_t offset = 0; + while (len) { + unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; + EVP_DigestUpdate(&self->ctx, cp + offset, process); + len -= process; + offset += process; + } + } + } + + return (PyObject *)self; +} + + +/* The module-level function: new() */ + +PyDoc_STRVAR(EVP_new__doc__, +"Return a new hash object using the named algorithm.\n\ +An optional string argument may be provided and will be\n\ +automatically hashed.\n\ +\n\ +The MD5 and SHA1 algorithms are always supported.\n"); + +static PyObject * +EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"name", "string", NULL}; + PyObject *name_obj = NULL; + char *name; + const EVP_MD *digest; + unsigned char *cp = NULL; + Py_ssize_t len = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s#:new", kwlist, + &name_obj, &cp, &len)) { + return NULL; + } + + if (!PyArg_Parse(name_obj, "s", &name)) { + PyErr_SetString(PyExc_TypeError, "name must be a string"); + return NULL; + } + + digest = EVP_get_digestbyname(name); + + return EVPnew(name_obj, digest, NULL, cp, len); +} + +/* + * This macro generates constructor function definitions for specific + * hash algorithms. These constructors are much faster than calling + * the generic one passing it a python string and are noticably + * faster than calling a python new() wrapper. Thats important for + * code that wants to make hashes of a bunch of small strings. + */ +#define GEN_CONSTRUCTOR(NAME) \ + static PyObject * \ + EVP_new_ ## NAME (PyObject *self, PyObject *args) \ + { \ + unsigned char *cp = NULL; \ + Py_ssize_t len = 0; \ + \ + if (!PyArg_ParseTuple(args, "|s#:" #NAME , &cp, &len)) { \ + return NULL; \ + } \ + \ + return EVPnew( \ + CONST_ ## NAME ## _name_obj, \ + NULL, \ + CONST_new_ ## NAME ## _ctx_p, \ + cp, len); \ + } + +/* a PyMethodDef structure for the constructor */ +#define CONSTRUCTOR_METH_DEF(NAME) \ + {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS, \ + PyDoc_STR("Returns a " #NAME \ + " hash object; optionally initialized with a string") \ + } + +/* used in the init function to setup a constructor */ +#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \ + CONST_ ## NAME ## _name_obj = PyString_FromString(#NAME); \ + if (EVP_get_digestbyname(#NAME)) { \ + CONST_new_ ## NAME ## _ctx_p = &CONST_new_ ## NAME ## _ctx; \ + EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME)); \ + } \ +} while (0); + +GEN_CONSTRUCTOR(md5) +GEN_CONSTRUCTOR(sha1) +GEN_CONSTRUCTOR(sha224) +GEN_CONSTRUCTOR(sha256) +GEN_CONSTRUCTOR(sha384) +GEN_CONSTRUCTOR(sha512) + +/* List of functions exported by this module */ + +static struct PyMethodDef EVP_functions[] = { + {"new", (PyCFunction)EVP_new, METH_VARARGS|METH_KEYWORDS, EVP_new__doc__}, + CONSTRUCTOR_METH_DEF(md5), + CONSTRUCTOR_METH_DEF(sha1), + CONSTRUCTOR_METH_DEF(sha224), + CONSTRUCTOR_METH_DEF(sha256), + CONSTRUCTOR_METH_DEF(sha384), + CONSTRUCTOR_METH_DEF(sha512), + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +PyMODINIT_FUNC +init_hashlib(void) +{ + PyObject *m; + + OpenSSL_add_all_digests(); + + /* TODO build EVP_functions openssl_* entries dynamically based + * on what hashes are supported rather than listing many + * but having some be unsupported. Only init appropriate + * constants. */ + + EVPtype.ob_type = &PyType_Type; + if (PyType_Ready(&EVPtype) < 0) + return; + + m = Py_InitModule("_hashlib", EVP_functions); + if (m == NULL) + return; + +#if HASH_OBJ_CONSTRUCTOR + Py_INCREF(&EVPtype); + PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype); +#endif + + /* these constants are used by the convenience constructors */ + INIT_CONSTRUCTOR_CONSTANTS(md5); + INIT_CONSTRUCTOR_CONSTANTS(sha1); + INIT_CONSTRUCTOR_CONSTANTS(sha224); + INIT_CONSTRUCTOR_CONSTANTS(sha256); + INIT_CONSTRUCTOR_CONSTANTS(sha384); + INIT_CONSTRUCTOR_CONSTANTS(sha512); +} diff --git a/src/Modules/md5.c b/src/Modules/md5.c new file mode 100644 --- /dev/null +++ b/src/Modules/md5.c @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost at aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/src/Modules/md5.h b/src/Modules/md5.h new file mode 100644 --- /dev/null +++ b/src/Modules/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost at aladdin.com + + */ +/* $Id: md5.h 43594 2006-04-03 16:27:50Z matthias.klose $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/src/Modules/md5module.c b/src/Modules/md5module.c new file mode 100644 --- /dev/null +++ b/src/Modules/md5module.c @@ -0,0 +1,312 @@ + +/* MD5 module */ + +/* This module provides an interface to the RSA Data Security, + Inc. MD5 Message-Digest Algorithm, described in RFC 1321. + It requires the files md5c.c and md5.h (which are slightly changed + from the versions in the RFC to avoid the "global.h" file.) */ + + +/* MD5 objects */ + +#include "Python.h" +#include "structmember.h" +#include "md5.h" + +typedef struct { + PyObject_HEAD + md5_state_t md5; /* the context holder */ +} md5object; + +static PyTypeObject MD5type; + +#define is_md5object(v) ((v)->ob_type == &MD5type) + +static md5object * +newmd5object(void) +{ + md5object *md5p; + + md5p = PyObject_New(md5object, &MD5type); + if (md5p == NULL) + return NULL; + + md5_init(&md5p->md5); /* actual initialisation */ + return md5p; +} + + +/* MD5 methods */ + +static void +md5_dealloc(md5object *md5p) +{ + PyObject_Del(md5p); +} + + +/* MD5 methods-as-attributes */ + +static PyObject * +md5_update(md5object *self, PyObject *args) +{ + unsigned char *cp; + int len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + md5_append(&self->md5, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(update_doc, +"update (arg)\n\ +\n\ +Update the md5 object with the string arg. Repeated calls are\n\ +equivalent to a single call with the concatenation of all the\n\ +arguments."); + + +static PyObject * +md5_digest(md5object *self) +{ + md5_state_t mdContext; + unsigned char aDigest[16]; + + /* make a temporary copy, and perform the final */ + mdContext = self->md5; + md5_finish(&mdContext, aDigest); + + return PyString_FromStringAndSize((char *)aDigest, 16); +} + +PyDoc_STRVAR(digest_doc, +"digest() -> string\n\ +\n\ +Return the digest of the strings passed to the update() method so\n\ +far. This is a 16-byte string which may contain non-ASCII characters,\n\ +including null bytes."); + + +static PyObject * +md5_hexdigest(md5object *self) +{ + md5_state_t mdContext; + unsigned char digest[16]; + unsigned char hexdigest[32]; + int i, j; + + /* make a temporary copy, and perform the final */ + mdContext = self->md5; + md5_finish(&mdContext, digest); + + /* Make hex version of the digest */ + for(i=j=0; i<16; i++) { + char c; + c = (digest[i] >> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hexdigest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hexdigest[j++] = c; + } + return PyString_FromStringAndSize((char*)hexdigest, 32); +} + + +PyDoc_STRVAR(hexdigest_doc, +"hexdigest() -> string\n\ +\n\ +Like digest(), but returns the digest as a string of hexadecimal digits."); + + +static PyObject * +md5_copy(md5object *self) +{ + md5object *md5p; + + if ((md5p = newmd5object()) == NULL) + return NULL; + + md5p->md5 = self->md5; + + return (PyObject *)md5p; +} + +PyDoc_STRVAR(copy_doc, +"copy() -> md5 object\n\ +\n\ +Return a copy (``clone'') of the md5 object."); + + +static PyMethodDef md5_methods[] = { + {"update", (PyCFunction)md5_update, METH_VARARGS, update_doc}, + {"digest", (PyCFunction)md5_digest, METH_NOARGS, digest_doc}, + {"hexdigest", (PyCFunction)md5_hexdigest, METH_NOARGS, hexdigest_doc}, + {"copy", (PyCFunction)md5_copy, METH_NOARGS, copy_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +md5_get_block_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(64); +} + +static PyObject * +md5_get_digest_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(16); +} + +static PyObject * +md5_get_name(PyObject *self, void *closure) +{ + return PyString_FromStringAndSize("MD5", 3); +} + +static PyGetSetDef md5_getseters[] = { + {"digest_size", + (getter)md5_get_digest_size, NULL, + NULL, + NULL}, + {"block_size", + (getter)md5_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)md5_get_name, NULL, + NULL, + NULL}, + /* the old md5 and sha modules support 'digest_size' as in PEP 247. + * the old sha module also supported 'digestsize'. ugh. */ + {"digestsize", + (getter)md5_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + + +PyDoc_STRVAR(module_doc, +"This module implements the interface to RSA's MD5 message digest\n\ +algorithm (see also Internet RFC 1321). Its use is quite\n\ +straightforward: use the new() to create an md5 object. You can now\n\ +feed this object with arbitrary strings using the update() method, and\n\ +at any point you can ask it for the digest (a strong kind of 128-bit\n\ +checksum, a.k.a. ``fingerprint'') of the concatenation of the strings\n\ +fed to it so far using the digest() method.\n\ +\n\ +Functions:\n\ +\n\ +new([arg]) -- return a new md5 object, initialized with arg if provided\n\ +md5([arg]) -- DEPRECATED, same as new, but for compatibility\n\ +\n\ +Special Objects:\n\ +\n\ +MD5Type -- type object for md5 objects"); + +PyDoc_STRVAR(md5type_doc, +"An md5 represents the object used to calculate the MD5 checksum of a\n\ +string of information.\n\ +\n\ +Methods:\n\ +\n\ +update() -- updates the current digest with an additional string\n\ +digest() -- return the current digest value\n\ +hexdigest() -- return the current digest as a string of hexadecimal digits\n\ +copy() -- return a copy of the current md5 object"); + +static PyTypeObject MD5type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_md5.md5", /*tp_name*/ + sizeof(md5object), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)md5_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + md5type_doc, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + md5_methods, /*tp_methods*/ + 0, /*tp_members*/ + md5_getseters, /*tp_getset*/ +}; + + +/* MD5 functions */ + +static PyObject * +MD5_new(PyObject *self, PyObject *args) +{ + md5object *md5p; + unsigned char *cp = NULL; + int len = 0; + + if (!PyArg_ParseTuple(args, "|s#:new", &cp, &len)) + return NULL; + + if ((md5p = newmd5object()) == NULL) + return NULL; + + if (cp) + md5_append(&md5p->md5, cp, len); + + return (PyObject *)md5p; +} + +PyDoc_STRVAR(new_doc, +"new([arg]) -> md5 object\n\ +\n\ +Return a new md5 object. If arg is present, the method call update(arg)\n\ +is made."); + + +/* List of functions exported by this module */ + +static PyMethodDef md5_functions[] = { + {"new", (PyCFunction)MD5_new, METH_VARARGS, new_doc}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +PyMODINIT_FUNC +init_md5(void) +{ + PyObject *m, *d; + + MD5type.ob_type = &PyType_Type; + if (PyType_Ready(&MD5type) < 0) + return; + m = Py_InitModule3("_md5", md5_functions, module_doc); + if (m == NULL) + return; + d = PyModule_GetDict(m); + PyDict_SetItemString(d, "MD5Type", (PyObject *)&MD5type); + PyModule_AddIntConstant(m, "digest_size", 16); + /* No need to check the error here, the caller will do that */ +} diff --git a/src/Modules/sha256module.c b/src/Modules/sha256module.c new file mode 100644 --- /dev/null +++ b/src/Modules/sha256module.c @@ -0,0 +1,701 @@ +/* SHA256 module */ + +/* This module provides an interface to NIST's SHA-256 and SHA-224 Algorithms */ + +/* See below for information about the original code this module was + based upon. Additional work performed by: + + Andrew Kuchling (amk at amk.ca) + Greg Stein (gstein at lyra.org) + Trevor Perrin (trevp at trevp.net) + + Copyright (C) 2005 Gregory P. Smith (greg at krypto.org) + Licensed to PSF under a Contributor Agreement. + +*/ + +/* SHA objects */ + +#include "Python.h" +#include "structmember.h" + + +/* Endianness testing and definitions */ +#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ + if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} + +#define PCT_LITTLE_ENDIAN 1 +#define PCT_BIG_ENDIAN 0 + +/* Some useful types */ + +typedef unsigned char SHA_BYTE; + +#if SIZEOF_INT == 4 +typedef unsigned int SHA_INT32; /* 32-bit integer */ +#else +/* not defined. compilation will die. */ +#endif + +/* The SHA block size and message digest sizes, in bytes */ + +#define SHA_BLOCKSIZE 64 +#define SHA_DIGESTSIZE 32 + +/* The structure for storing SHA info */ + +typedef struct { + PyObject_HEAD + SHA_INT32 digest[8]; /* Message digest */ + SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ + SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ + int Endianness; + int local; /* unprocessed amount in data */ + int digestsize; +} SHAobject; + +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. */ + +static void longReverse(SHA_INT32 *buffer, int byteCount, int Endianness) +{ + SHA_INT32 value; + + if ( Endianness == PCT_BIG_ENDIAN ) + return; + + byteCount /= sizeof(*buffer); + while (byteCount--) { + value = *buffer; + value = ( ( value & 0xFF00FF00L ) >> 8 ) | \ + ( ( value & 0x00FF00FFL ) << 8 ); + *buffer++ = ( value << 16 ) | ( value >> 16 ); + } +} + +static void SHAcopy(SHAobject *src, SHAobject *dest) +{ + dest->Endianness = src->Endianness; + dest->local = src->local; + dest->digestsize = src->digestsize; + dest->count_lo = src->count_lo; + dest->count_hi = src->count_hi; + memcpy(dest->digest, src->digest, sizeof(src->digest)); + memcpy(dest->data, src->data, sizeof(src->data)); +} + + +/* ------------------------------------------------------------------------ + * + * This code for the SHA-256 algorithm was noted as public domain. The + * original headers are pasted below. + * + * Several changes have been made to make it more compatible with the + * Python environment and desired interface. + * + */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * gurantee it works. + * + * Tom St Denis, tomstdenis at iahu.ca, http://libtomcrypt.org + */ + + +/* SHA256 by Tom St Denis */ + +/* Various logical functions */ +#define ROR(x, y)\ +( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | \ +((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR((x),(n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + + +static void +sha_transform(SHAobject *sha_info) +{ + int i; + SHA_INT32 S[8], W[64], t0, t1; + + memcpy(W, sha_info->data, sizeof(sha_info->data)); + longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness); + + for (i = 16; i < 64; ++i) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + for (i = 0; i < 8; ++i) { + S[i] = sha_info->digest[i]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i,ki) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); + +#undef RND + + /* feedback */ + for (i = 0; i < 8; i++) { + sha_info->digest[i] = sha_info->digest[i] + S[i]; + } + +} + + + +/* initialize the SHA digest */ + +static void +sha_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + sha_info->digest[0] = 0x6A09E667L; + sha_info->digest[1] = 0xBB67AE85L; + sha_info->digest[2] = 0x3C6EF372L; + sha_info->digest[3] = 0xA54FF53AL; + sha_info->digest[4] = 0x510E527FL; + sha_info->digest[5] = 0x9B05688CL; + sha_info->digest[6] = 0x1F83D9ABL; + sha_info->digest[7] = 0x5BE0CD19L; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; + sha_info->digestsize = 32; +} + +static void +sha224_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + sha_info->digest[0] = 0xc1059ed8L; + sha_info->digest[1] = 0x367cd507L; + sha_info->digest[2] = 0x3070dd17L; + sha_info->digest[3] = 0xf70e5939L; + sha_info->digest[4] = 0xffc00b31L; + sha_info->digest[5] = 0x68581511L; + sha_info->digest[6] = 0x64f98fa7L; + sha_info->digest[7] = 0xbefa4fa4L; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; + sha_info->digestsize = 28; +} + + +/* update the SHA digest */ + +static void +sha_update(SHAobject *sha_info, SHA_BYTE *buffer, int count) +{ + int i; + SHA_INT32 clo; + + clo = sha_info->count_lo + ((SHA_INT32) count << 3); + if (clo < sha_info->count_lo) { + ++sha_info->count_hi; + } + sha_info->count_lo = clo; + sha_info->count_hi += (SHA_INT32) count >> 29; + if (sha_info->local) { + i = SHA_BLOCKSIZE - sha_info->local; + if (i > count) { + i = count; + } + memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); + count -= i; + buffer += i; + sha_info->local += i; + if (sha_info->local == SHA_BLOCKSIZE) { + sha_transform(sha_info); + } + else { + return; + } + } + while (count >= SHA_BLOCKSIZE) { + memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); + buffer += SHA_BLOCKSIZE; + count -= SHA_BLOCKSIZE; + sha_transform(sha_info); + } + memcpy(sha_info->data, buffer, count); + sha_info->local = count; +} + +/* finish computing the SHA digest */ + +static void +sha_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info) +{ + int count; + SHA_INT32 lo_bit_count, hi_bit_count; + + lo_bit_count = sha_info->count_lo; + hi_bit_count = sha_info->count_hi; + count = (int) ((lo_bit_count >> 3) & 0x3f); + ((SHA_BYTE *) sha_info->data)[count++] = 0x80; + if (count > SHA_BLOCKSIZE - 8) { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - count); + sha_transform(sha_info); + memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8); + } + else { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - 8 - count); + } + + /* GJS: note that we add the hi/lo in big-endian. sha_transform will + swap these values into host-order. */ + sha_info->data[56] = (hi_bit_count >> 24) & 0xff; + sha_info->data[57] = (hi_bit_count >> 16) & 0xff; + sha_info->data[58] = (hi_bit_count >> 8) & 0xff; + sha_info->data[59] = (hi_bit_count >> 0) & 0xff; + sha_info->data[60] = (lo_bit_count >> 24) & 0xff; + sha_info->data[61] = (lo_bit_count >> 16) & 0xff; + sha_info->data[62] = (lo_bit_count >> 8) & 0xff; + sha_info->data[63] = (lo_bit_count >> 0) & 0xff; + sha_transform(sha_info); + digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); + digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); + digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); + digest[ 3] = (unsigned char) ((sha_info->digest[0] ) & 0xff); + digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); + digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); + digest[ 6] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); + digest[ 7] = (unsigned char) ((sha_info->digest[1] ) & 0xff); + digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); + digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); + digest[10] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); + digest[11] = (unsigned char) ((sha_info->digest[2] ) & 0xff); + digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); + digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); + digest[14] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); + digest[15] = (unsigned char) ((sha_info->digest[3] ) & 0xff); + digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); + digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); + digest[18] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); + digest[19] = (unsigned char) ((sha_info->digest[4] ) & 0xff); + digest[20] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff); + digest[21] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff); + digest[22] = (unsigned char) ((sha_info->digest[5] >> 8) & 0xff); + digest[23] = (unsigned char) ((sha_info->digest[5] ) & 0xff); + digest[24] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff); + digest[25] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff); + digest[26] = (unsigned char) ((sha_info->digest[6] >> 8) & 0xff); + digest[27] = (unsigned char) ((sha_info->digest[6] ) & 0xff); + digest[28] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff); + digest[29] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff); + digest[30] = (unsigned char) ((sha_info->digest[7] >> 8) & 0xff); + digest[31] = (unsigned char) ((sha_info->digest[7] ) & 0xff); +} + +/* + * End of copied SHA code. + * + * ------------------------------------------------------------------------ + */ + +static PyTypeObject SHA224type; +static PyTypeObject SHA256type; + + +static SHAobject * +newSHA224object(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHA224type); +} + +static SHAobject * +newSHA256object(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHA256type); +} + +/* Internal methods for a hash object */ + +static void +SHA_dealloc(PyObject *ptr) +{ + PyObject_Del(ptr); +} + + +/* External methods for a hash object */ + +PyDoc_STRVAR(SHA256_copy__doc__, "Return a copy of the hash object."); + +static PyObject * +SHA256_copy(SHAobject *self, PyObject *unused) +{ + SHAobject *newobj; + + if (((PyObject*)self)->ob_type == &SHA256type) { + if ( (newobj = newSHA256object())==NULL) + return NULL; + } else { + if ( (newobj = newSHA224object())==NULL) + return NULL; + } + + SHAcopy(self, newobj); + return (PyObject *)newobj; +} + +PyDoc_STRVAR(SHA256_digest__doc__, +"Return the digest value as a string of binary data."); + +static PyObject * +SHA256_digest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + + SHAcopy(self, &temp); + sha_final(digest, &temp); + return PyString_FromStringAndSize((const char *)digest, self->digestsize); +} + +PyDoc_STRVAR(SHA256_hexdigest__doc__, +"Return the digest value as a string of hexadecimal digits."); + +static PyObject * +SHA256_hexdigest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + PyObject *retval; + char *hex_digest; + int i, j; + + /* Get the raw (binary) digest value */ + SHAcopy(self, &temp); + sha_final(digest, &temp); + + /* Create a new string */ + retval = PyString_FromStringAndSize(NULL, self->digestsize * 2); + if (!retval) + return NULL; + hex_digest = PyString_AsString(retval); + if (!hex_digest) { + Py_DECREF(retval); + return NULL; + } + + /* Make hex version of the digest */ + for(i=j=0; idigestsize; i++) { + char c; + c = (digest[i] >> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + } + return retval; +} + +PyDoc_STRVAR(SHA256_update__doc__, +"Update this hash object's state with the provided string."); + +static PyObject * +SHA256_update(SHAobject *self, PyObject *args) +{ + unsigned char *cp; + int len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + sha_update(self, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef SHA_methods[] = { + {"copy", (PyCFunction)SHA256_copy, METH_NOARGS, SHA256_copy__doc__}, + {"digest", (PyCFunction)SHA256_digest, METH_NOARGS, SHA256_digest__doc__}, + {"hexdigest", (PyCFunction)SHA256_hexdigest, METH_NOARGS, SHA256_hexdigest__doc__}, + {"update", (PyCFunction)SHA256_update, METH_VARARGS, SHA256_update__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SHA256_get_block_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(SHA_BLOCKSIZE); +} + +static PyObject * +SHA256_get_name(PyObject *self, void *closure) +{ + if (((SHAobject *)self)->digestsize == 32) + return PyString_FromStringAndSize("SHA256", 6); + else + return PyString_FromStringAndSize("SHA224", 6); +} + +static PyGetSetDef SHA_getseters[] = { + {"block_size", + (getter)SHA256_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)SHA256_get_name, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef SHA_members[] = { + {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, + /* the old md5 and sha modules support 'digest_size' as in PEP 247. + * the old sha module also supported 'digestsize'. ugh. */ + {"digestsize", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject SHA224type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_sha256.sha224", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SHA_methods, /* tp_methods */ + SHA_members, /* tp_members */ + SHA_getseters, /* tp_getset */ +}; + +static PyTypeObject SHA256type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_sha256.sha256", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SHA_methods, /* tp_methods */ + SHA_members, /* tp_members */ + SHA_getseters, /* tp_getset */ +}; + + +/* The single module-level function: new() */ + +PyDoc_STRVAR(SHA256_new__doc__, +"Return a new SHA-256 hash object; optionally initialized with a string."); + +static PyObject * +SHA256_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHA256object()) == NULL) + return NULL; + + sha_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha_update(new, cp, len); + + return (PyObject *)new; +} + +PyDoc_STRVAR(SHA224_new__doc__, +"Return a new SHA-224 hash object; optionally initialized with a string."); + +static PyObject * +SHA224_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHA224object()) == NULL) + return NULL; + + sha224_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha_update(new, cp, len); + + return (PyObject *)new; +} + + +/* List of functions exported by this module */ + +static struct PyMethodDef SHA_functions[] = { + {"sha256", (PyCFunction)SHA256_new, METH_VARARGS|METH_KEYWORDS, SHA256_new__doc__}, + {"sha224", (PyCFunction)SHA224_new, METH_VARARGS|METH_KEYWORDS, SHA224_new__doc__}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +#define insint(n,v) { PyModule_AddIntConstant(m,n,v); } + +PyMODINIT_FUNC +init_sha256(void) +{ + PyObject *m; + + SHA224type.ob_type = &PyType_Type; + if (PyType_Ready(&SHA224type) < 0) + return; + SHA256type.ob_type = &PyType_Type; + if (PyType_Ready(&SHA256type) < 0) + return; + m = Py_InitModule("_sha256", SHA_functions); + if (m == NULL) + return; +} diff --git a/src/Modules/sha512module.c b/src/Modules/sha512module.c new file mode 100644 --- /dev/null +++ b/src/Modules/sha512module.c @@ -0,0 +1,769 @@ +/* SHA512 module */ + +/* This module provides an interface to NIST's SHA-512 and SHA-384 Algorithms */ + +/* See below for information about the original code this module was + based upon. Additional work performed by: + + Andrew Kuchling (amk at amk.ca) + Greg Stein (gstein at lyra.org) + Trevor Perrin (trevp at trevp.net) + + Copyright (C) 2005 Gregory P. Smith (greg at krypto.org) + Licensed to PSF under a Contributor Agreement. + +*/ + +/* SHA objects */ + +#include "Python.h" +#include "structmember.h" + +#ifdef PY_LONG_LONG /* If no PY_LONG_LONG, don't compile anything! */ + +/* Endianness testing and definitions */ +#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ + if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} + +#define PCT_LITTLE_ENDIAN 1 +#define PCT_BIG_ENDIAN 0 + +/* Some useful types */ + +typedef unsigned char SHA_BYTE; + +#if SIZEOF_INT == 4 +typedef unsigned int SHA_INT32; /* 32-bit integer */ +typedef unsigned PY_LONG_LONG SHA_INT64; /* 64-bit integer */ +#else +/* not defined. compilation will die. */ +#endif + +/* The SHA block size and message digest sizes, in bytes */ + +#define SHA_BLOCKSIZE 128 +#define SHA_DIGESTSIZE 64 + +/* The structure for storing SHA info */ + +typedef struct { + PyObject_HEAD + SHA_INT64 digest[8]; /* Message digest */ + SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ + SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ + int Endianness; + int local; /* unprocessed amount in data */ + int digestsize; +} SHAobject; + +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. */ + +static void longReverse(SHA_INT64 *buffer, int byteCount, int Endianness) +{ + SHA_INT64 value; + + if ( Endianness == PCT_BIG_ENDIAN ) + return; + + byteCount /= sizeof(*buffer); + while (byteCount--) { + value = *buffer; + + ((unsigned char*)buffer)[0] = (unsigned char)(value >> 56) & 0xff; + ((unsigned char*)buffer)[1] = (unsigned char)(value >> 48) & 0xff; + ((unsigned char*)buffer)[2] = (unsigned char)(value >> 40) & 0xff; + ((unsigned char*)buffer)[3] = (unsigned char)(value >> 32) & 0xff; + ((unsigned char*)buffer)[4] = (unsigned char)(value >> 24) & 0xff; + ((unsigned char*)buffer)[5] = (unsigned char)(value >> 16) & 0xff; + ((unsigned char*)buffer)[6] = (unsigned char)(value >> 8) & 0xff; + ((unsigned char*)buffer)[7] = (unsigned char)(value ) & 0xff; + + buffer++; + } +} + +static void SHAcopy(SHAobject *src, SHAobject *dest) +{ + dest->Endianness = src->Endianness; + dest->local = src->local; + dest->digestsize = src->digestsize; + dest->count_lo = src->count_lo; + dest->count_hi = src->count_hi; + memcpy(dest->digest, src->digest, sizeof(src->digest)); + memcpy(dest->data, src->data, sizeof(src->data)); +} + + +/* ------------------------------------------------------------------------ + * + * This code for the SHA-512 algorithm was noted as public domain. The + * original headers are pasted below. + * + * Several changes have been made to make it more compatible with the + * Python environment and desired interface. + * + */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * gurantee it works. + * + * Tom St Denis, tomstdenis at iahu.ca, http://libtomcrypt.org + */ + + +/* SHA512 by Tom St Denis */ + +/* Various logical functions */ +#define ROR64(x, y) \ + ( ((((x) & 0xFFFFFFFFFFFFFFFFULL)>>((unsigned PY_LONG_LONG)(y) & 63)) | \ + ((x)<<((unsigned PY_LONG_LONG)(64-((y) & 63))))) & 0xFFFFFFFFFFFFFFFFULL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64((x),(n)) +#define R(x, n) (((x) & 0xFFFFFFFFFFFFFFFFULL) >> ((unsigned PY_LONG_LONG)n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) + + +static void +sha512_transform(SHAobject *sha_info) +{ + int i; + SHA_INT64 S[8], W[80], t0, t1; + + memcpy(W, sha_info->data, sizeof(sha_info->data)); + longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness); + + for (i = 16; i < 80; ++i) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + for (i = 0; i < 8; ++i) { + S[i] = sha_info->digest[i]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i,ki) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98d728ae22ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x7137449123ef65cdULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcfec4d3b2fULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba58189dbbcULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25bf348b538ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1b605d019ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4af194f9bULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5da6d8118ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98a3030242ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b0145706fbeULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be4ee4b28cULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3d5ffb4e2ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74f27b896fULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe3b1696b1ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a725c71235ULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174cf692694ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c19ef14ad2ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786384f25e3ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc68b8cd5b5ULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc77ac9c65ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f592b0275ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa6ea6e483ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dcbd41fbd4ULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da831153b5ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152ee66dfabULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d2db43210ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c898fb213fULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7beef0ee4ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf33da88fc2ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147930aa725ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351e003826fULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x142929670a0e6e70ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a8546d22ffcULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b21385c26c926ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc5ac42aedULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d139d95b3dfULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a73548baf63deULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb3c77b2a8ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e47edaee6ULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c851482353bULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a14cf10364ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664bbc423001ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70d0f89791ULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a30654be30ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819d6ef5218ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd69906245565a910ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e35855771202aULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa07032bbd1b8ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116b8d2d0c8ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c085141ab53ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774cdf8eeb99ULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5e19b48a8ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3c5c95a63ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4ae3418acbULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f7763e373ULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3d6b2b8a3ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee5defb2fcULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f43172f60ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814a1f0ab72ULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc702081a6439ecULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa23631e28ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506cebde82bde9ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7b2c67915ULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2e372532bULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],64,0xca273eceea26619cULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],65,0xd186b8c721c0c207ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],66,0xeada7dd6cde0eb1eULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],67,0xf57d4f7fee6ed178ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],68,0x06f067aa72176fbaULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],69,0x0a637dc5a2c898a6ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],70,0x113f9804bef90daeULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],71,0x1b710b35131c471bULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],72,0x28db77f523047d84ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],73,0x32caab7b40c72493ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],74,0x3c9ebe0a15c9bebcULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],75,0x431d67c49c100d4cULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],76,0x4cc5d4becb3e42b6ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],77,0x597f299cfc657e2aULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],78,0x5fcb6fab3ad6faecULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],79,0x6c44198c4a475817ULL); + +#undef RND + + /* feedback */ + for (i = 0; i < 8; i++) { + sha_info->digest[i] = sha_info->digest[i] + S[i]; + } + +} + + + +/* initialize the SHA digest */ + +static void +sha512_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + sha_info->digest[0] = 0x6a09e667f3bcc908ULL; + sha_info->digest[1] = 0xbb67ae8584caa73bULL; + sha_info->digest[2] = 0x3c6ef372fe94f82bULL; + sha_info->digest[3] = 0xa54ff53a5f1d36f1ULL; + sha_info->digest[4] = 0x510e527fade682d1ULL; + sha_info->digest[5] = 0x9b05688c2b3e6c1fULL; + sha_info->digest[6] = 0x1f83d9abfb41bd6bULL; + sha_info->digest[7] = 0x5be0cd19137e2179ULL; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; + sha_info->digestsize = 64; +} + +static void +sha384_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + sha_info->digest[0] = 0xcbbb9d5dc1059ed8ULL; + sha_info->digest[1] = 0x629a292a367cd507ULL; + sha_info->digest[2] = 0x9159015a3070dd17ULL; + sha_info->digest[3] = 0x152fecd8f70e5939ULL; + sha_info->digest[4] = 0x67332667ffc00b31ULL; + sha_info->digest[5] = 0x8eb44a8768581511ULL; + sha_info->digest[6] = 0xdb0c2e0d64f98fa7ULL; + sha_info->digest[7] = 0x47b5481dbefa4fa4ULL; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; + sha_info->digestsize = 48; +} + + +/* update the SHA digest */ + +static void +sha512_update(SHAobject *sha_info, SHA_BYTE *buffer, int count) +{ + int i; + SHA_INT32 clo; + + clo = sha_info->count_lo + ((SHA_INT32) count << 3); + if (clo < sha_info->count_lo) { + ++sha_info->count_hi; + } + sha_info->count_lo = clo; + sha_info->count_hi += (SHA_INT32) count >> 29; + if (sha_info->local) { + i = SHA_BLOCKSIZE - sha_info->local; + if (i > count) { + i = count; + } + memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); + count -= i; + buffer += i; + sha_info->local += i; + if (sha_info->local == SHA_BLOCKSIZE) { + sha512_transform(sha_info); + } + else { + return; + } + } + while (count >= SHA_BLOCKSIZE) { + memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); + buffer += SHA_BLOCKSIZE; + count -= SHA_BLOCKSIZE; + sha512_transform(sha_info); + } + memcpy(sha_info->data, buffer, count); + sha_info->local = count; +} + +/* finish computing the SHA digest */ + +static void +sha512_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info) +{ + int count; + SHA_INT32 lo_bit_count, hi_bit_count; + + lo_bit_count = sha_info->count_lo; + hi_bit_count = sha_info->count_hi; + count = (int) ((lo_bit_count >> 3) & 0x7f); + ((SHA_BYTE *) sha_info->data)[count++] = 0x80; + if (count > SHA_BLOCKSIZE - 16) { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - count); + sha512_transform(sha_info); + memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 16); + } + else { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - 16 - count); + } + + /* GJS: note that we add the hi/lo in big-endian. sha512_transform will + swap these values into host-order. */ + sha_info->data[112] = 0; + sha_info->data[113] = 0; + sha_info->data[114] = 0; + sha_info->data[115] = 0; + sha_info->data[116] = 0; + sha_info->data[117] = 0; + sha_info->data[118] = 0; + sha_info->data[119] = 0; + sha_info->data[120] = (hi_bit_count >> 24) & 0xff; + sha_info->data[121] = (hi_bit_count >> 16) & 0xff; + sha_info->data[122] = (hi_bit_count >> 8) & 0xff; + sha_info->data[123] = (hi_bit_count >> 0) & 0xff; + sha_info->data[124] = (lo_bit_count >> 24) & 0xff; + sha_info->data[125] = (lo_bit_count >> 16) & 0xff; + sha_info->data[126] = (lo_bit_count >> 8) & 0xff; + sha_info->data[127] = (lo_bit_count >> 0) & 0xff; + sha512_transform(sha_info); + digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 56) & 0xff); + digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 48) & 0xff); + digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 40) & 0xff); + digest[ 3] = (unsigned char) ((sha_info->digest[0] >> 32) & 0xff); + digest[ 4] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); + digest[ 5] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); + digest[ 6] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); + digest[ 7] = (unsigned char) ((sha_info->digest[0] ) & 0xff); + digest[ 8] = (unsigned char) ((sha_info->digest[1] >> 56) & 0xff); + digest[ 9] = (unsigned char) ((sha_info->digest[1] >> 48) & 0xff); + digest[10] = (unsigned char) ((sha_info->digest[1] >> 40) & 0xff); + digest[11] = (unsigned char) ((sha_info->digest[1] >> 32) & 0xff); + digest[12] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); + digest[13] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); + digest[14] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); + digest[15] = (unsigned char) ((sha_info->digest[1] ) & 0xff); + digest[16] = (unsigned char) ((sha_info->digest[2] >> 56) & 0xff); + digest[17] = (unsigned char) ((sha_info->digest[2] >> 48) & 0xff); + digest[18] = (unsigned char) ((sha_info->digest[2] >> 40) & 0xff); + digest[19] = (unsigned char) ((sha_info->digest[2] >> 32) & 0xff); + digest[20] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); + digest[21] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); + digest[22] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); + digest[23] = (unsigned char) ((sha_info->digest[2] ) & 0xff); + digest[24] = (unsigned char) ((sha_info->digest[3] >> 56) & 0xff); + digest[25] = (unsigned char) ((sha_info->digest[3] >> 48) & 0xff); + digest[26] = (unsigned char) ((sha_info->digest[3] >> 40) & 0xff); + digest[27] = (unsigned char) ((sha_info->digest[3] >> 32) & 0xff); + digest[28] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); + digest[29] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); + digest[30] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); + digest[31] = (unsigned char) ((sha_info->digest[3] ) & 0xff); + digest[32] = (unsigned char) ((sha_info->digest[4] >> 56) & 0xff); + digest[33] = (unsigned char) ((sha_info->digest[4] >> 48) & 0xff); + digest[34] = (unsigned char) ((sha_info->digest[4] >> 40) & 0xff); + digest[35] = (unsigned char) ((sha_info->digest[4] >> 32) & 0xff); + digest[36] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); + digest[37] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); + digest[38] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); + digest[39] = (unsigned char) ((sha_info->digest[4] ) & 0xff); + digest[40] = (unsigned char) ((sha_info->digest[5] >> 56) & 0xff); + digest[41] = (unsigned char) ((sha_info->digest[5] >> 48) & 0xff); + digest[42] = (unsigned char) ((sha_info->digest[5] >> 40) & 0xff); + digest[43] = (unsigned char) ((sha_info->digest[5] >> 32) & 0xff); + digest[44] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff); + digest[45] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff); + digest[46] = (unsigned char) ((sha_info->digest[5] >> 8) & 0xff); + digest[47] = (unsigned char) ((sha_info->digest[5] ) & 0xff); + digest[48] = (unsigned char) ((sha_info->digest[6] >> 56) & 0xff); + digest[49] = (unsigned char) ((sha_info->digest[6] >> 48) & 0xff); + digest[50] = (unsigned char) ((sha_info->digest[6] >> 40) & 0xff); + digest[51] = (unsigned char) ((sha_info->digest[6] >> 32) & 0xff); + digest[52] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff); + digest[53] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff); + digest[54] = (unsigned char) ((sha_info->digest[6] >> 8) & 0xff); + digest[55] = (unsigned char) ((sha_info->digest[6] ) & 0xff); + digest[56] = (unsigned char) ((sha_info->digest[7] >> 56) & 0xff); + digest[57] = (unsigned char) ((sha_info->digest[7] >> 48) & 0xff); + digest[58] = (unsigned char) ((sha_info->digest[7] >> 40) & 0xff); + digest[59] = (unsigned char) ((sha_info->digest[7] >> 32) & 0xff); + digest[60] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff); + digest[61] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff); + digest[62] = (unsigned char) ((sha_info->digest[7] >> 8) & 0xff); + digest[63] = (unsigned char) ((sha_info->digest[7] ) & 0xff); +} + +/* + * End of copied SHA code. + * + * ------------------------------------------------------------------------ + */ + +static PyTypeObject SHA384type; +static PyTypeObject SHA512type; + + +static SHAobject * +newSHA384object(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHA384type); +} + +static SHAobject * +newSHA512object(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHA512type); +} + +/* Internal methods for a hash object */ + +static void +SHA512_dealloc(PyObject *ptr) +{ + PyObject_Del(ptr); +} + + +/* External methods for a hash object */ + +PyDoc_STRVAR(SHA512_copy__doc__, "Return a copy of the hash object."); + +static PyObject * +SHA512_copy(SHAobject *self, PyObject *unused) +{ + SHAobject *newobj; + + if (((PyObject*)self)->ob_type == &SHA512type) { + if ( (newobj = newSHA512object())==NULL) + return NULL; + } else { + if ( (newobj = newSHA384object())==NULL) + return NULL; + } + + SHAcopy(self, newobj); + return (PyObject *)newobj; +} + +PyDoc_STRVAR(SHA512_digest__doc__, +"Return the digest value as a string of binary data."); + +static PyObject * +SHA512_digest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + + SHAcopy(self, &temp); + sha512_final(digest, &temp); + return PyString_FromStringAndSize((const char *)digest, self->digestsize); +} + +PyDoc_STRVAR(SHA512_hexdigest__doc__, +"Return the digest value as a string of hexadecimal digits."); + +static PyObject * +SHA512_hexdigest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + PyObject *retval; + char *hex_digest; + int i, j; + + /* Get the raw (binary) digest value */ + SHAcopy(self, &temp); + sha512_final(digest, &temp); + + /* Create a new string */ + retval = PyString_FromStringAndSize(NULL, self->digestsize * 2); + if (!retval) + return NULL; + hex_digest = PyString_AsString(retval); + if (!hex_digest) { + Py_DECREF(retval); + return NULL; + } + + /* Make hex version of the digest */ + for (i=j=0; idigestsize; i++) { + char c; + c = (digest[i] >> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + } + return retval; +} + +PyDoc_STRVAR(SHA512_update__doc__, +"Update this hash object's state with the provided string."); + +static PyObject * +SHA512_update(SHAobject *self, PyObject *args) +{ + unsigned char *cp; + int len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + sha512_update(self, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef SHA_methods[] = { + {"copy", (PyCFunction)SHA512_copy, METH_NOARGS, SHA512_copy__doc__}, + {"digest", (PyCFunction)SHA512_digest, METH_NOARGS, SHA512_digest__doc__}, + {"hexdigest", (PyCFunction)SHA512_hexdigest, METH_NOARGS, SHA512_hexdigest__doc__}, + {"update", (PyCFunction)SHA512_update, METH_VARARGS, SHA512_update__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SHA512_get_block_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(SHA_BLOCKSIZE); +} + +static PyObject * +SHA512_get_name(PyObject *self, void *closure) +{ + if (((SHAobject *)self)->digestsize == 64) + return PyString_FromStringAndSize("SHA512", 6); + else + return PyString_FromStringAndSize("SHA384", 6); +} + +static PyGetSetDef SHA_getseters[] = { + {"block_size", + (getter)SHA512_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)SHA512_get_name, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef SHA_members[] = { + {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, + /* the old md5 and sha modules support 'digest_size' as in PEP 247. + * the old sha module also supported 'digestsize'. ugh. */ + {"digestsize", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject SHA384type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_sha512.sha384", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA512_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SHA_methods, /* tp_methods */ + SHA_members, /* tp_members */ + SHA_getseters, /* tp_getset */ +}; + +static PyTypeObject SHA512type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_sha512.sha512", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA512_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SHA_methods, /* tp_methods */ + SHA_members, /* tp_members */ + SHA_getseters, /* tp_getset */ +}; + + +/* The single module-level function: new() */ + +PyDoc_STRVAR(SHA512_new__doc__, +"Return a new SHA-512 hash object; optionally initialized with a string."); + +static PyObject * +SHA512_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHA512object()) == NULL) + return NULL; + + sha512_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha512_update(new, cp, len); + + return (PyObject *)new; +} + +PyDoc_STRVAR(SHA384_new__doc__, +"Return a new SHA-384 hash object; optionally initialized with a string."); + +static PyObject * +SHA384_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHA384object()) == NULL) + return NULL; + + sha384_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha512_update(new, cp, len); + + return (PyObject *)new; +} + + +/* List of functions exported by this module */ + +static struct PyMethodDef SHA_functions[] = { + {"sha512", (PyCFunction)SHA512_new, METH_VARARGS|METH_KEYWORDS, SHA512_new__doc__}, + {"sha384", (PyCFunction)SHA384_new, METH_VARARGS|METH_KEYWORDS, SHA384_new__doc__}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +#define insint(n,v) { PyModule_AddIntConstant(m,n,v); } + +PyMODINIT_FUNC +init_sha512(void) +{ + PyObject *m; + + SHA384type.ob_type = &PyType_Type; + if (PyType_Ready(&SHA384type) < 0) + return; + SHA512type.ob_type = &PyType_Type; + if (PyType_Ready(&SHA512type) < 0) + return; + m = Py_InitModule("_sha512", SHA_functions); + if (m == NULL) + return; +} + +#endif diff --git a/src/Modules/shamodule.c b/src/Modules/shamodule.c new file mode 100644 --- /dev/null +++ b/src/Modules/shamodule.c @@ -0,0 +1,593 @@ +/* SHA module */ + +/* This module provides an interface to NIST's Secure Hash Algorithm */ + +/* See below for information about the original code this module was + based upon. Additional work performed by: + + Andrew Kuchling (amk at amk.ca) + Greg Stein (gstein at lyra.org) + + Copyright (C) 2005 Gregory P. Smith (greg at krypto.org) + Licensed to PSF under a Contributor Agreement. + +*/ + +/* SHA objects */ + +#include "Python.h" +#include "structmember.h" + + +/* Endianness testing and definitions */ +#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ + if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} + +#define PCT_LITTLE_ENDIAN 1 +#define PCT_BIG_ENDIAN 0 + +/* Some useful types */ + +typedef unsigned char SHA_BYTE; + +#if SIZEOF_INT == 4 +typedef unsigned int SHA_INT32; /* 32-bit integer */ +#else +/* not defined. compilation will die. */ +#endif + +/* The SHA block size and message digest sizes, in bytes */ + +#define SHA_BLOCKSIZE 64 +#define SHA_DIGESTSIZE 20 + +/* The structure for storing SHS info */ + +typedef struct { + PyObject_HEAD + SHA_INT32 digest[5]; /* Message digest */ + SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ + SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ + int Endianness; + int local; /* unprocessed amount in data */ +} SHAobject; + +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. */ + +static void longReverse(SHA_INT32 *buffer, int byteCount, int Endianness) +{ + SHA_INT32 value; + + if ( Endianness == PCT_BIG_ENDIAN ) + return; + + byteCount /= sizeof(*buffer); + while (byteCount--) { + value = *buffer; + value = ( ( value & 0xFF00FF00L ) >> 8 ) | \ + ( ( value & 0x00FF00FFL ) << 8 ); + *buffer++ = ( value << 16 ) | ( value >> 16 ); + } +} + +static void SHAcopy(SHAobject *src, SHAobject *dest) +{ + dest->Endianness = src->Endianness; + dest->local = src->local; + dest->count_lo = src->count_lo; + dest->count_hi = src->count_hi; + memcpy(dest->digest, src->digest, sizeof(src->digest)); + memcpy(dest->data, src->data, sizeof(src->data)); +} + + +/* ------------------------------------------------------------------------ + * + * This code for the SHA algorithm was noted as public domain. The original + * headers are pasted below. + * + * Several changes have been made to make it more compatible with the + * Python environment and desired interface. + * + */ + +/* NIST Secure Hash Algorithm */ +/* heavily modified by Uwe Hollerbach */ +/* from Peter C. Gutmann's implementation as found in */ +/* Applied Cryptography by Bruce Schneier */ +/* Further modifications to include the "UNRAVEL" stuff, below */ + +/* This code is in the public domain */ + +/* UNRAVEL should be fastest & biggest */ +/* UNROLL_LOOPS should be just as big, but slightly slower */ +/* both undefined should be smallest and slowest */ + +#define UNRAVEL +/* #define UNROLL_LOOPS */ + +/* The SHA f()-functions. The f1 and f3 functions can be optimized to + save one boolean operation each - thanks to Rich Schroeppel, + rcs at cs.arizona.edu for discovering this */ + +/*#define f1(x,y,z) ((x & y) | (~x & z)) // Rounds 0-19 */ +#define f1(x,y,z) (z ^ (x & (y ^ z))) /* Rounds 0-19 */ +#define f2(x,y,z) (x ^ y ^ z) /* Rounds 20-39 */ +/*#define f3(x,y,z) ((x & y) | (x & z) | (y & z)) // Rounds 40-59 */ +#define f3(x,y,z) ((x & y) | (z & (x | y))) /* Rounds 40-59 */ +#define f4(x,y,z) (x ^ y ^ z) /* Rounds 60-79 */ + +/* SHA constants */ + +#define CONST1 0x5a827999L /* Rounds 0-19 */ +#define CONST2 0x6ed9eba1L /* Rounds 20-39 */ +#define CONST3 0x8f1bbcdcL /* Rounds 40-59 */ +#define CONST4 0xca62c1d6L /* Rounds 60-79 */ + +/* 32-bit rotate */ + +#define R32(x,n) ((x << n) | (x >> (32 - n))) + +/* the generic case, for when the overall rotation is not unraveled */ + +#define FG(n) \ + T = R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n; \ + E = D; D = C; C = R32(B,30); B = A; A = T + +/* specific cases, for when the overall rotation is unraveled */ + +#define FA(n) \ + T = R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n; B = R32(B,30) + +#define FB(n) \ + E = R32(T,5) + f##n(A,B,C) + D + *WP++ + CONST##n; A = R32(A,30) + +#define FC(n) \ + D = R32(E,5) + f##n(T,A,B) + C + *WP++ + CONST##n; T = R32(T,30) + +#define FD(n) \ + C = R32(D,5) + f##n(E,T,A) + B + *WP++ + CONST##n; E = R32(E,30) + +#define FE(n) \ + B = R32(C,5) + f##n(D,E,T) + A + *WP++ + CONST##n; D = R32(D,30) + +#define FT(n) \ + A = R32(B,5) + f##n(C,D,E) + T + *WP++ + CONST##n; C = R32(C,30) + +/* do SHA transformation */ + +static void +sha_transform(SHAobject *sha_info) +{ + int i; + SHA_INT32 T, A, B, C, D, E, W[80], *WP; + + memcpy(W, sha_info->data, sizeof(sha_info->data)); + longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness); + + for (i = 16; i < 80; ++i) { + W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]; + + /* extra rotation fix */ + W[i] = R32(W[i], 1); + } + A = sha_info->digest[0]; + B = sha_info->digest[1]; + C = sha_info->digest[2]; + D = sha_info->digest[3]; + E = sha_info->digest[4]; + WP = W; +#ifdef UNRAVEL + FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); + FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); + FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); + FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); + FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); + FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); + FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); + FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); + sha_info->digest[0] += E; + sha_info->digest[1] += T; + sha_info->digest[2] += A; + sha_info->digest[3] += B; + sha_info->digest[4] += C; +#else /* !UNRAVEL */ +#ifdef UNROLL_LOOPS + FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); + FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); + FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); + FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); + FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); + FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); + FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); + FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); +#else /* !UNROLL_LOOPS */ + for (i = 0; i < 20; ++i) { FG(1); } + for (i = 20; i < 40; ++i) { FG(2); } + for (i = 40; i < 60; ++i) { FG(3); } + for (i = 60; i < 80; ++i) { FG(4); } +#endif /* !UNROLL_LOOPS */ + sha_info->digest[0] += A; + sha_info->digest[1] += B; + sha_info->digest[2] += C; + sha_info->digest[3] += D; + sha_info->digest[4] += E; +#endif /* !UNRAVEL */ +} + +/* initialize the SHA digest */ + +static void +sha_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + + sha_info->digest[0] = 0x67452301L; + sha_info->digest[1] = 0xefcdab89L; + sha_info->digest[2] = 0x98badcfeL; + sha_info->digest[3] = 0x10325476L; + sha_info->digest[4] = 0xc3d2e1f0L; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; +} + +/* update the SHA digest */ + +static void +sha_update(SHAobject *sha_info, SHA_BYTE *buffer, int count) +{ + int i; + SHA_INT32 clo; + + clo = sha_info->count_lo + ((SHA_INT32) count << 3); + if (clo < sha_info->count_lo) { + ++sha_info->count_hi; + } + sha_info->count_lo = clo; + sha_info->count_hi += (SHA_INT32) count >> 29; + if (sha_info->local) { + i = SHA_BLOCKSIZE - sha_info->local; + if (i > count) { + i = count; + } + memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); + count -= i; + buffer += i; + sha_info->local += i; + if (sha_info->local == SHA_BLOCKSIZE) { + sha_transform(sha_info); + } + else { + return; + } + } + while (count >= SHA_BLOCKSIZE) { + memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); + buffer += SHA_BLOCKSIZE; + count -= SHA_BLOCKSIZE; + sha_transform(sha_info); + } + memcpy(sha_info->data, buffer, count); + sha_info->local = count; +} + +/* finish computing the SHA digest */ + +static void +sha_final(unsigned char digest[20], SHAobject *sha_info) +{ + int count; + SHA_INT32 lo_bit_count, hi_bit_count; + + lo_bit_count = sha_info->count_lo; + hi_bit_count = sha_info->count_hi; + count = (int) ((lo_bit_count >> 3) & 0x3f); + ((SHA_BYTE *) sha_info->data)[count++] = 0x80; + if (count > SHA_BLOCKSIZE - 8) { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - count); + sha_transform(sha_info); + memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8); + } + else { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - 8 - count); + } + + /* GJS: note that we add the hi/lo in big-endian. sha_transform will + swap these values into host-order. */ + sha_info->data[56] = (hi_bit_count >> 24) & 0xff; + sha_info->data[57] = (hi_bit_count >> 16) & 0xff; + sha_info->data[58] = (hi_bit_count >> 8) & 0xff; + sha_info->data[59] = (hi_bit_count >> 0) & 0xff; + sha_info->data[60] = (lo_bit_count >> 24) & 0xff; + sha_info->data[61] = (lo_bit_count >> 16) & 0xff; + sha_info->data[62] = (lo_bit_count >> 8) & 0xff; + sha_info->data[63] = (lo_bit_count >> 0) & 0xff; + sha_transform(sha_info); + digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); + digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); + digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); + digest[ 3] = (unsigned char) ((sha_info->digest[0] ) & 0xff); + digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); + digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); + digest[ 6] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); + digest[ 7] = (unsigned char) ((sha_info->digest[1] ) & 0xff); + digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); + digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); + digest[10] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); + digest[11] = (unsigned char) ((sha_info->digest[2] ) & 0xff); + digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); + digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); + digest[14] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); + digest[15] = (unsigned char) ((sha_info->digest[3] ) & 0xff); + digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); + digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); + digest[18] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); + digest[19] = (unsigned char) ((sha_info->digest[4] ) & 0xff); +} + +/* + * End of copied SHA code. + * + * ------------------------------------------------------------------------ + */ + +static PyTypeObject SHAtype; + + +static SHAobject * +newSHAobject(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHAtype); +} + +/* Internal methods for a hashing object */ + +static void +SHA_dealloc(PyObject *ptr) +{ + PyObject_Del(ptr); +} + + +/* External methods for a hashing object */ + +PyDoc_STRVAR(SHA_copy__doc__, "Return a copy of the hashing object."); + +static PyObject * +SHA_copy(SHAobject *self, PyObject *unused) +{ + SHAobject *newobj; + + if ( (newobj = newSHAobject())==NULL) + return NULL; + + SHAcopy(self, newobj); + return (PyObject *)newobj; +} + +PyDoc_STRVAR(SHA_digest__doc__, +"Return the digest value as a string of binary data."); + +static PyObject * +SHA_digest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + + SHAcopy(self, &temp); + sha_final(digest, &temp); + return PyString_FromStringAndSize((const char *)digest, sizeof(digest)); +} + +PyDoc_STRVAR(SHA_hexdigest__doc__, +"Return the digest value as a string of hexadecimal digits."); + +static PyObject * +SHA_hexdigest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + PyObject *retval; + char *hex_digest; + int i, j; + + /* Get the raw (binary) digest value */ + SHAcopy(self, &temp); + sha_final(digest, &temp); + + /* Create a new string */ + retval = PyString_FromStringAndSize(NULL, sizeof(digest) * 2); + if (!retval) + return NULL; + hex_digest = PyString_AsString(retval); + if (!hex_digest) { + Py_DECREF(retval); + return NULL; + } + + /* Make hex version of the digest */ + for(i=j=0; i> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + } + return retval; +} + +PyDoc_STRVAR(SHA_update__doc__, +"Update this hashing object's state with the provided string."); + +static PyObject * +SHA_update(SHAobject *self, PyObject *args) +{ + unsigned char *cp; + int len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + sha_update(self, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef SHA_methods[] = { + {"copy", (PyCFunction)SHA_copy, METH_NOARGS, SHA_copy__doc__}, + {"digest", (PyCFunction)SHA_digest, METH_NOARGS, SHA_digest__doc__}, + {"hexdigest", (PyCFunction)SHA_hexdigest, METH_NOARGS, SHA_hexdigest__doc__}, + {"update", (PyCFunction)SHA_update, METH_VARARGS, SHA_update__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SHA_get_block_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(SHA_BLOCKSIZE); +} + +static PyObject * +SHA_get_digest_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(SHA_DIGESTSIZE); +} + +static PyObject * +SHA_get_name(PyObject *self, void *closure) +{ + return PyString_FromStringAndSize("SHA1", 4); +} + +static PyGetSetDef SHA_getseters[] = { + {"digest_size", + (getter)SHA_get_digest_size, NULL, + NULL, + NULL}, + {"block_size", + (getter)SHA_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)SHA_get_name, NULL, + NULL, + NULL}, + /* the old md5 and sha modules support 'digest_size' as in PEP 247. + * the old sha module also supported 'digestsize'. ugh. */ + {"digestsize", + (getter)SHA_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject SHAtype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_sha.sha", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SHA_methods, /* tp_methods */ + 0, /* tp_members */ + SHA_getseters, /* tp_getset */ +}; + + +/* The single module-level function: new() */ + +PyDoc_STRVAR(SHA_new__doc__, +"Return a new SHA hashing object. An optional string argument\n\ +may be provided; if present, this string will be automatically\n\ +hashed."); + +static PyObject * +SHA_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHAobject()) == NULL) + return NULL; + + sha_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha_update(new, cp, len); + + return (PyObject *)new; +} + + +/* List of functions exported by this module */ + +static struct PyMethodDef SHA_functions[] = { + {"new", (PyCFunction)SHA_new, METH_VARARGS|METH_KEYWORDS, SHA_new__doc__}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +#define insint(n,v) { PyModule_AddIntConstant(m,n,v); } + +PyMODINIT_FUNC +init_sha(void) +{ + PyObject *m; + + SHAtype.ob_type = &PyType_Type; + if (PyType_Ready(&SHAtype) < 0) + return; + m = Py_InitModule("_sha", SHA_functions); + if (m == NULL) + return; + + /* Add some symbolic constants to the module */ + insint("blocksize", 1); /* For future use, in case some hash + functions require an integral number of + blocks */ + insint("digestsize", 20); + insint("digest_size", 20); +} diff --git a/src/README.txt b/src/README.txt --- a/src/README.txt +++ b/src/README.txt @@ -2,7 +2,7 @@ Distutils2 ========== -Welcome to Distutils2 ! +Welcome to Distutils2! Distutils2 is the new version of Distutils. It's not backward compatible with Distutils but provides more features, and implement most new packaging @@ -10,6 +10,6 @@ See the documentation at http://packages.python.org/Distutils2 for more info. -**Beware that Distutils2 is its in early stage and should not be used in +**Beware that Distutils2 is in its early stage and should not be used in production. Its API is subject to changes** diff --git a/src/check.sh b/src/check.sh new file mode 100755 --- /dev/null +++ b/src/check.sh @@ -0,0 +1,2 @@ +pep8 distutils2 +pyflakes distutils2 diff --git a/src/distutils2/README b/src/distutils2/README --- a/src/distutils2/README +++ b/src/distutils2/README @@ -1,4 +1,4 @@ -This directory contains the Distutils package. +This directory contains the Distutils2 package. There's a full documentation available at: @@ -8,6 +8,6 @@ http://www.python.org/sigs/distutils-sig/ -WARNING: Distutils2 must remain compatible with 2.4 +WARNING: Distutils2 must remain compatible with Python 2.4 $Id: README 70017 2009-02-27 12:53:34Z tarek.ziade $ diff --git a/src/distutils2/__init__.py b/src/distutils2/__init__.py --- a/src/distutils2/__init__.py +++ b/src/distutils2/__init__.py @@ -1,16 +1,26 @@ """distutils -The main package for the Python Module Distribution Utilities. Normally -used from a setup script as +The main package for the Python Distribution Utilities 2. Setup +scripts should import the setup function from distutils2.core: - from distutils.core import setup + from distutils2.core import setup - setup (...) + setup(name=..., version=..., ...) + +Third-party tools can use parts of Distutils2 as building blocks +without causing the other modules to be imported: + + import distutils2.version + import distutils2.pypi.simple + import distutils2.tests.pypi_server """ -__all__ = ['__version__', 'setup'] +__all__ = ['__version__'] __revision__ = "$Id: __init__.py 78020 2010-02-06 16:37:32Z benjamin.peterson $" __version__ = "1.0a2" -from distutils2.core import setup +# when set to True, converts doctests by default too +run_2to3_on_doctests = True +# Standard package names for fixer packages +lib2to3_fixer_packages = ['lib2to3.fixes'] diff --git a/src/distutils2/_backport/hashlib.py b/src/distutils2/_backport/hashlib.py new file mode 100644 --- /dev/null +++ b/src/distutils2/_backport/hashlib.py @@ -0,0 +1,143 @@ +# $Id$ +# +# Copyright (C) 2005 Gregory P. Smith (greg at krypto.org) +# Licensed to PSF under a Contributor Agreement. +# + +__doc__ = """hashlib module - A common interface to many hash functions. + +new(name, string='') - returns a new hash object implementing the + given hash function; initializing the hash + using the given string data. + +Named constructor functions are also available, these are much faster +than using new(): + +md5(), sha1(), sha224(), sha256(), sha384(), and sha512() + +More algorithms may be available on your platform but the above are +guaranteed to exist. + +NOTE: If you want the adler32 or crc32 hash functions they are available in +the zlib module. + +Choose your hash function wisely. Some have known collision weaknesses. +sha384 and sha512 will be slow on 32 bit platforms. + +Hash objects have these methods: + - update(arg): Update the hash object with the string arg. Repeated calls + are equivalent to a single call with the concatenation of all + the arguments. + - digest(): Return the digest of the strings passed to the update() method + so far. This may contain non-ASCII characters, including + NUL bytes. + - hexdigest(): Like digest() except the digest is returned as a string of + double length, containing only hexadecimal digits. + - copy(): Return a copy (clone) of the hash object. This can be used to + efficiently compute the digests of strings that share a common + initial substring. + +For example, to obtain the digest of the string 'Nobody inspects the +spammish repetition': + + >>> import hashlib + >>> m = hashlib.md5() + >>> m.update("Nobody inspects") + >>> m.update(" the spammish repetition") + >>> m.digest() + '\\xbbd\\x9c\\x83\\xdd\\x1e\\xa5\\xc9\\xd9\\xde\\xc9\\xa1\\x8d\\xf0\\xff\\xe9' + +More condensed: + + >>> hashlib.sha224("Nobody inspects the spammish repetition").hexdigest() + 'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2' + +""" + +# This tuple and __get_builtin_constructor() must be modified if a new +# always available algorithm is added. +__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') + +algorithms = __always_supported + +__all__ = __always_supported + ('new', 'algorithms') + + +def __get_builtin_constructor(name): + if name in ('SHA1', 'sha1'): + import _sha + return _sha.new + elif name in ('MD5', 'md5'): + import _md5 + return _md5.new + elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'): + import _sha256 + bs = name[3:] + if bs == '256': + return _sha256.sha256 + elif bs == '224': + return _sha256.sha224 + elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'): + import _sha512 + bs = name[3:] + if bs == '512': + return _sha512.sha512 + elif bs == '384': + return _sha512.sha384 + + raise ValueError('unsupported hash type %s' % name) + + +def __get_openssl_constructor(name): + try: + f = getattr(_hashlib, 'openssl_' + name) + # Allow the C module to raise ValueError. The function will be + # defined but the hash not actually available thanks to OpenSSL. + f() + # Use the C function directly (very fast) + return f + except (AttributeError, ValueError): + return __get_builtin_constructor(name) + + +def __py_new(name, string=''): + """new(name, string='') - Return a new hashing object using the named algorithm; + optionally initialized with a string. + """ + return __get_builtin_constructor(name)(string) + + +def __hash_new(name, string=''): + """new(name, string='') - Return a new hashing object using the named algorithm; + optionally initialized with a string. + """ + try: + return _hashlib.new(name, string) + except ValueError: + # If the _hashlib module (OpenSSL) doesn't support the named + # hash, try using our builtin implementations. + # This allows for SHA224/256 and SHA384/512 support even though + # the OpenSSL library prior to 0.9.8 doesn't provide them. + return __get_builtin_constructor(name)(string) + + +try: + import _hashlib + new = __hash_new + __get_hash = __get_openssl_constructor +except ImportError: + new = __py_new + __get_hash = __get_builtin_constructor + +for __func_name in __always_supported: + # try them all, some may not work due to the OpenSSL + # version not supporting that algorithm. + try: + globals()[__func_name] = __get_hash(__func_name) + except ValueError: + import logging + logging.exception('code for hash %s was not found.', __func_name) + +# Cleanup locals() +del __always_supported, __func_name, __get_hash +del __py_new, __hash_new, __get_openssl_constructor diff --git a/src/distutils2/_backport/pkgutil.py b/src/distutils2/_backport/pkgutil.py --- a/src/distutils2/_backport/pkgutil.py +++ b/src/distutils2/_backport/pkgutil.py @@ -12,10 +12,17 @@ from distutils2.errors import DistutilsError from distutils2.metadata import DistributionMetadata from distutils2.version import suggest_normalized_version, VersionPredicate +import zipimport +try: + import cStringIO as StringIO +except ImportError: + import StringIO +import re +import warnings __all__ = [ 'get_importer', 'iter_importers', 'get_loader', 'find_loader', - 'walk_packages', 'iter_modules', + 'walk_packages', 'iter_modules', 'get_data', 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path', 'Distribution', 'EggInfoDistribution', 'distinfo_dirname', 'get_distributions', 'get_distribution', 'get_file_users', @@ -24,7 +31,7 @@ def read_code(stream): - # This helper is needed in order for the PEP 302 emulation to + # This helper is needed in order for the :pep:`302` emulation to # correctly handle compiled files import marshal @@ -79,32 +86,34 @@ def walk_packages(path=None, prefix='', onerror=None): - """Yields (module_loader, name, ispkg) for all modules recursively - on path, or, if path is None, all accessible modules. + """Yields ``(module_loader, name, ispkg)`` for all modules recursively + on *path*, or, if *path* is ``None``, all accessible modules. - 'path' should be either None or a list of paths to look for - modules in. + :parameter path: should be either ``None`` or a list of paths to look for + modules in. + :parameter prefix: is a string to output on the front of every module name + on output. - 'prefix' is a string to output on the front of every module name - on output. - - Note that this function must import all *packages* (NOT all - modules!) on the given path, in order to access the __path__ + Note that this function must import all packages (NOT all + modules!) on the given path, in order to access the ``__path__`` attribute to find submodules. - 'onerror' is a function which gets called with one argument (the + *onerror* is a function which gets called with one argument (the name of the package which was being imported) if any exception occurs while trying to import a package. If no onerror function is - supplied, ImportErrors are caught and ignored, while all other + supplied, ``ImportErrors`` are caught and ignored, while all other exceptions are propagated, terminating the search. Examples: - # list all modules python can access - walk_packages() + * list all modules python can access:: - # list all submodules of ctypes - walk_packages(ctypes.__path__, ctypes.__name__+'.') + walk_packages() + + * list all submodules of ctypes:: + + walk_packages(ctypes.__path__, ctypes.__name__+'.') + """ def seen(p, m={}): @@ -137,14 +146,14 @@ def iter_modules(path=None, prefix=''): - """Yields (module_loader, name, ispkg) for all submodules on path, - or, if path is None, all top-level modules on sys.path. + """Yields ``(module_loader, name, ispkg)`` for all submodules on path, + or, if *path* is ``None``, all top-level modules on ``sys.path``. - 'path' should be either None or a list of paths to look for - modules in. + :parameter path: should be either None or a list of paths to look for + modules in. + :parameter prefix: is a string to output on the front of every module name + on output. - 'prefix' is a string to output on the front of every module name - on output. """ if path is None: @@ -162,6 +171,7 @@ #@simplegeneric def iter_importer_modules(importer, prefix=''): + "" if not hasattr(importer, 'iter_modules'): return [] return importer.iter_modules(prefix) @@ -169,15 +179,16 @@ iter_importer_modules = simplegeneric(iter_importer_modules) -class ImpImporter: - """PEP 302 Importer that wraps Python's "classic" import algorithm +class ImpImporter(object): + """:pep:`302` Importer that wraps Python's "classic" import algorithm - ImpImporter(dirname) produces a PEP 302 importer that searches that - directory. ImpImporter(None) produces a PEP 302 importer that searches - the current sys.path, plus any modules that are frozen or built-in. + ``ImpImporter(dirname)`` produces a :pep:`302` importer that searches that + directory. ``ImpImporter(None)`` produces a :pep:`302` importer that + searches the current ``sys.path``, plus any modules that are frozen + or built-in. - Note that ImpImporter does not currently support being used by placement - on sys.meta_path. + Note that :class:`ImpImporter` does not currently support being used by placement + on ``sys.meta_path``. """ def __init__(self, path=None): @@ -231,9 +242,9 @@ yield prefix + modname, ispkg -class ImpLoader: - """PEP 302 Loader that wraps Python's "classic" import algorithm - """ +class ImpLoader(object): + """:pep:`302` Loader that wraps Python's "classic" import algorithm """ + code = source = None def __init__(self, fullname, file, filename, etc): @@ -365,17 +376,17 @@ def get_importer(path_item): - """Retrieve a PEP 302 importer for the given path item + """Retrieve a :pep:`302` importer for the given path item - The returned importer is cached in sys.path_importer_cache + The returned importer is cached in ``sys.path_importer_cache`` if it was newly created by a path hook. If there is no importer, a wrapper around the basic import machinery is returned. This wrapper is never inserted into - the importer cache (None is inserted instead). + the importer cache (``None`` is inserted instead). The cache (or part of it) can be cleared manually if a - rescan of sys.path_hooks is necessary. + rescan of ``sys.path_hooks`` is necessary. """ try: importer = sys.path_importer_cache[path_item] @@ -399,7 +410,7 @@ def iter_importers(fullname=""): - """Yield PEP 302 importers for the given module name + """Yield :pep:`302` importers for the given module name If fullname contains a '.', the importers will be for the package containing fullname, otherwise they will be importers for sys.meta_path, @@ -407,20 +418,20 @@ the named module is in a package, that package is imported as a side effect of invoking this function. - Non PEP 302 mechanisms (e.g. the Windows registry) used by the + Non :pep:`302` mechanisms (e.g. the Windows registry) used by the standard import machinery to find files in alternative locations - are partially supported, but are searched AFTER sys.path. Normally, - these locations are searched BEFORE sys.path, preventing sys.path + are partially supported, but are searched AFTER ``sys.path``. Normally, + these locations are searched BEFORE sys.path, preventing ``sys.path`` entries from shadowing them. For this to cause a visible difference in behaviour, there must be a module or package name that is accessible via both sys.path - and one of the non PEP 302 file system mechanisms. In this case, + and one of the non :pep:`302` file system mechanisms. In this case, the emulation will find the former version, while the builtin import mechanism will find the latter. Items of the following types can be affected by this discrepancy: - imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY + ``imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY`` """ if fullname.startswith('.'): raise ImportError("Relative module names not supported") @@ -441,15 +452,15 @@ def get_loader(module_or_name): - """Get a PEP 302 "loader" object for module_or_name + """Get a :pep:`302` "loader" object for module_or_name If the module or package is accessible via the normal import mechanism, a wrapper around the relevant part of that machinery is returned. Returns None if the module cannot be found or imported. If the named module is not already imported, its containing package - (if any) is imported, in order to establish the package __path__. + (if any) is imported, in order to establish the package ``__path__``. - This function uses iter_importers(), and is thus subject to the same + This function uses :func:`iter_importers`, and is thus subject to the same limitations regarding platform-specific special import locations such as the Windows registry. """ @@ -467,12 +478,13 @@ def find_loader(fullname): - """Find a PEP 302 "loader" object for fullname + """Find a :pep:`302` "loader" object for fullname - If fullname contains dots, path must be the containing package's __path__. - Returns None if the module cannot be found or imported. This function uses - iter_importers(), and is thus subject to the same limitations regarding - platform-specific special import locations such as the Windows registry. + If fullname contains dots, path must be the containing package's + ``__path__``. Returns ``None`` if the module cannot be found or imported. + This function uses :func:`iter_importers`, and is thus subject to the same + limitations regarding platform-specific special import locations such as + the Windows registry. """ for importer in iter_importers(fullname): loader = importer.find_module(fullname) @@ -485,21 +497,22 @@ def extend_path(path, name): """Extend a package's path. - Intended use is to place the following code in a package's __init__.py: + Intended use is to place the following code in a package's + ``__init__.py``:: from pkgutil import extend_path __path__ = extend_path(__path__, __name__) - This will add to the package's __path__ all subdirectories of - directories on sys.path named after the package. This is useful + This will add to the package's ``__path__`` all subdirectories of + directories on ``sys.path`` named after the package. This is useful if one wants to distribute different parts of a single logical package as multiple directories. - It also looks for *.pkg files beginning where * matches the name - argument. This feature is similar to *.pth files (see site.py), - except that it doesn't special-case lines starting with 'import'. - A *.pkg file is trusted at face value: apart from checking for - duplicates, all entries found in a *.pkg file are added to the + It also looks for ``*.pkg`` files beginning where ``*`` matches the name + argument. This feature is similar to ``*.pth`` files (see ``site.py``), + except that it doesn't special-case lines starting with ``import``. + A ``*.pkg`` file is trusted at face value: apart from checking for + duplicates, all entries found in a ``*.pkg`` file are added to the path, regardless of whether they are exist the filesystem. (This is a feature.) @@ -512,7 +525,7 @@ are not (unicode or 8-bit) strings referring to existing directories are ignored. Unicode items of sys.path that cause errors when used as filenames may cause this function to raise an - exception (in line with os.path.isdir() behavior). + exception (in line with ``os.path.isdir()`` behavior). """ if not isinstance(path, list): @@ -560,23 +573,23 @@ def get_data(package, resource): """Get a resource from a package. - This is a wrapper round the PEP 302 loader get_data API. The package + This is a wrapper round the :pep:`302` loader get_data API. The package argument should be the name of a package, in standard module format - (foo.bar). The resource argument should be in the form of a relative - filename, using '/' as the path separator. The parent directory name '..' - is not allowed, and nor is a rooted name (starting with a '/'). + (``foo.bar``). The resource argument should be in the form of a relative + filename, using ``'/'`` as the path separator. The parent directory name + ``'..'`` is not allowed, and nor is a rooted name (starting with a ``'/'``). The function returns a binary string, which is the contents of the specified resource. For packages located in the filesystem, which have already been imported, - this is the rough equivalent of + this is the rough equivalent of:: d = os.path.dirname(sys.modules[package].__file__) data = open(os.path.join(d, resource), 'rb').read() - If the package cannot be located or loaded, or it uses a PEP 302 loader - which does not support get_data(), then None is returned. + If the package cannot be located or loaded, or it uses a :pep:`302` loader + which does not support :func:`get_data`, then ``None`` is returned. """ loader = get_loader(package) @@ -603,7 +616,7 @@ class Distribution(object): """Created with the *path* of the ``.dist-info`` directory provided to the - constructor. It reads the metadata contained in METADATA when it is + constructor. It reads the metadata contained in ``METADATA`` when it is instantiated.""" # Attribute documenting for Sphinx style documentation, see for more info: @@ -612,9 +625,9 @@ """The name of the distribution.""" metadata = None """A :class:`distutils2.metadata.DistributionMetadata` instance loaded with - the distribution's METADATA file.""" + the distribution's ``METADATA`` file.""" requested = False - """A boolean that indicates whether the REQUESTED metadata file is present + """A boolean that indicates whether the ``REQUESTED`` metadata file is present (in other words, whether the package was installed by user request).""" def __init__(self, path): @@ -635,15 +648,17 @@ def get_installed_files(self, local=False): """ - Iterates over the RECORD entries and returns a tuple (path, md5, size) - for each line. If *local* is ``True``, the returned path is transformed - into a local absolute path. Otherwise the raw value from RECORD is - returned. + Iterates over the ``RECORD`` entries and returns a tuple + ``(path, md5, size)`` for each line. If *local* is ``True``, + the returned path is transformed into a local absolute path. + Otherwise the raw value from RECORD is returned. A local absolute path is an absolute path in which occurrences of ``'/'`` have been replaced by the system separator given by ``os.sep``. + :parameter local: flag to say if the path should be returned a local absolute path + :type local: boolean :returns: iterator of (path, md5, size) """ @@ -651,7 +666,7 @@ def uses(self, path): """ - Returns ``True`` if path is listed in RECORD. *path* can be a local + Returns ``True`` if path is listed in ``RECORD``. *path* can be a local absolute path or a relative ``'/'``-separated path. :rtype: boolean @@ -673,8 +688,8 @@ directory path, a :class:`DistutilsError` is raised :type path: string :parameter binary: If *binary* is ``True``, opens the file in read-only - binary mode (rb), otherwise opens it in read-only - mode (r). + binary mode (``rb``), otherwise opens it in read-only + mode (``r``). :rtype: file object """ open_flags = 'r' @@ -701,37 +716,129 @@ def get_distinfo_files(self, local=False): """ - Iterates over the RECORD entries and returns paths for each line if the - path is pointing to a file located in the ``.dist-info`` directory or - one of its subdirectories. + Iterates over the ``RECORD`` entries and returns paths for each line if + the path is pointing to a file located in the ``.dist-info`` directory + or one of its subdirectories. :parameter local: If *local* is ``True``, each returned path is transformed into a local absolute path. Otherwise the - raw value from RECORD is returned. + raw value from ``RECORD`` is returned. :type local: boolean :returns: iterator of paths """ for path, md5, size in self._get_records(local): yield path + def __eq__(self, other): + return isinstance(other, Distribution) and self.path == other.path + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + class EggInfoDistribution(object): """Created with the *path* of the ``.egg-info`` directory or file provided to the constructor. It reads the metadata contained in the file itself, or if the given path happens to be a directory, the metadata is read from the - file PKG-INFO under that directory.""" + file ``PKG-INFO`` under that directory.""" name = '' """The name of the distribution.""" metadata = None """A :class:`distutils2.metadata.DistributionMetadata` instance loaded with - the distribution's METADATA file.""" + the distribution's ``METADATA`` file.""" + _REQUIREMENT = re.compile( \ + r'(?P[-A-Za-z0-9_.]+)\s*' \ + r'(?P(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)?\s*' \ + r'(?P(?:\s*,\s*(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)*)\s*' \ + r'(?P\[.*\])?') def __init__(self, path): - if os.path.isdir(path): - path = os.path.join(path, 'PKG-INFO') - self.metadata = DistributionMetadata(path=path) - self.name = self.metadata['name'] + self.path = path + + # reused from Distribute's pkg_resources + def yield_lines(strs): + """Yield non-empty/non-comment lines of a ``basestring`` or sequence""" + if isinstance(strs, basestring): + for s in strs.splitlines(): + s = s.strip() + if s and not s.startswith('#'): # skip blank lines/comments + yield s + else: + for ss in strs: + for s in yield_lines(ss): + yield s + + requires = None + if path.endswith('.egg'): + if os.path.isdir(path): + meta_path = os.path.join(path, 'EGG-INFO', 'PKG-INFO') + self.metadata = DistributionMetadata(path=meta_path) + try: + req_path = os.path.join(path, 'EGG-INFO', 'requires.txt') + requires = open(req_path, 'r').read() + except IOError: + requires = None + else: + zipf = zipimport.zipimporter(path) + fileobj = StringIO.StringIO(zipf.get_data('EGG-INFO/PKG-INFO')) + self.metadata = DistributionMetadata(fileobj=fileobj) + try: + requires = zipf.get_data('EGG-INFO/requires.txt') + except IOError: + requires = None + self.name = self.metadata['name'] + elif path.endswith('.egg-info'): + if os.path.isdir(path): + path = os.path.join(path, 'PKG-INFO') + try: + req_f = open(os.path.join(path, 'requires.txt'), 'r') + requires = req_f.read() + except IOError: + requires = None + self.metadata = DistributionMetadata(path=path) + self.name = self.metadata['name'] + else: + raise ValueError('The path must end with .egg-info or .egg') + + provides = "%s (%s)" % (self.metadata['name'], + self.metadata['version']) + if self.metadata['Metadata-Version'] == '1.2': + self.metadata['Provides-Dist'] += (provides,) + else: + self.metadata['Provides'] += (provides,) + reqs = [] + if requires is not None: + for line in yield_lines(requires): + if line[0] == '[': + warnings.warn('distutils2 does not support extensions in requires.txt') + break + else: + match = self._REQUIREMENT.match(line.strip()) + if not match: + raise ValueError('Distribution %s has ill formed ' + 'requires.txt file (%s)' % + (self.name, line)) + else: + if match.group('extras'): + s = 'Distribution %s uses extra requirements which'\ + ' are not supported in distutils' % (self.name) + warnings.warn(s) + name = match.group('name') + version = None + if match.group('first'): + version = match.group('first') + if match.group('rest'): + version += match.group('rest') + version = version.replace(' ', '') # trim spaces + if version is None: + reqs.append(name) + else: + reqs.append('%s (%s)' % (name, version)) + if self.metadata['Metadata-Version'] == '1.2': + self.metadata['Requires-Dist'] += reqs + else: + self.metadata['Requires'] += reqs def get_installed_files(self, local=False): return [] @@ -739,6 +846,13 @@ def uses(self, path): return False + def __eq__(self, other): + return isinstance(other, EggInfoDistribution) and \ + self.path == other.path + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + def _normalize_dist_name(name): """Returns a normalized name from the given *name*. @@ -792,7 +906,8 @@ if dir.endswith('.dist-info'): dist = Distribution(os.path.join(realpath, dir)) yield dist - elif use_egg_info and dir.endswith('.egg-info'): + elif use_egg_info and (dir.endswith('.egg-info') or + dir.endswith('.egg')): dist = EggInfoDistribution(os.path.join(realpath, dir)) yield dist @@ -801,18 +916,18 @@ """ Scans all elements in ``sys.path`` and looks for all directories ending with ``.dist-info``. Returns a :class:`Distribution` corresponding to the - ``.dist-info`` directory that contains the METADATA that matches *name* for - the *name* metadata field. + ``.dist-info`` directory that contains the ``METADATA`` that matches *name* + for the *name* metadata field. If no distribution exists with the given *name* and the parameter *use_egg_info* is set to ``True``, then all files and directories ending with ``.egg-info`` are scanned. A :class:`EggInfoDistribution` instance is returned if one is found that has metadata that matches *name* for the *name* metadata field. - This function only returns the first result founded, as no more than one + This function only returns the first result found, as no more than one value is expected. If the directory is not found, ``None`` is returned. - :rtype: :class:`Distribution` or :class:`EggInfoDistribution: or None""" + :rtype: :class:`Distribution` or :class:`EggInfoDistribution` or None""" found = None for dist in get_distributions(): if dist.name == name: @@ -867,7 +982,7 @@ then all files and directories ending with ``.egg-info`` are considered as well and returns an :class:`EggInfoDistribution` instance. - This function only returns the first result founded, since no more than + This function only returns the first result found, since no more than one values are expected. If the directory is not found, returns ``None``. :parameter version: a version specifier that indicates the version @@ -887,7 +1002,7 @@ provided = dist.metadata['Provides-Dist'] + dist.metadata['Provides'] for p in provided: - p_components = p.split(' ', 1) + p_components = p.rsplit(' ', 1) if len(p_components) == 1 or predicate is None: if name == p_components[0]: yield dist @@ -896,7 +1011,8 @@ p_name, p_ver = p_components if len(p_ver) < 2 or p_ver[0] != '(' or p_ver[-1] != ')': raise DistutilsError(('Distribution %s has invalid ' + - 'provides field') % (dist.name,)) + 'provides field: %s') \ + % (dist.name, p)) p_ver = p_ver[1:-1] # trim off the parenthesis if p_name == name and predicate.match(p_ver): yield dist diff --git a/src/distutils2/_backport/sysconfig.cfg b/src/distutils2/_backport/sysconfig.cfg --- a/src/distutils2/_backport/sysconfig.cfg +++ b/src/distutils2/_backport/sysconfig.cfg @@ -1,6 +1,6 @@ [globals] # These are the useful categories that are sometimes referenced at runtime, -# using pkgutils.open(): +# using pkgutil.open(): config = {confdir}/{distribution.name} # Configuration files appdata = {datadir}/{distribution.name} # Non-writable data that is independent of architecture (images, many xml/text files) appdata.arch = {libdir}/{distribution.name} # Non-writable data that is architecture-dependent (some binary data formats) diff --git a/src/distutils2/_backport/sysconfig.py b/src/distutils2/_backport/sysconfig.py --- a/src/distutils2/_backport/sysconfig.py +++ b/src/distutils2/_backport/sysconfig.py @@ -5,7 +5,7 @@ import os import re from os.path import pardir, abspath -from ConfigParser import ConfigParser +from ConfigParser import RawConfigParser _PREFIX = os.path.normpath(sys.prefix) _EXEC_PREFIX = os.path.normpath(sys.exec_prefix) @@ -14,7 +14,7 @@ # XXX _CONFIG_DIR will be set by the Makefile later _CONFIG_DIR = os.path.normpath(os.path.dirname(__file__)) _CONFIG_FILE = os.path.join(_CONFIG_DIR, 'sysconfig.cfg') -_SCHEMES = ConfigParser() +_SCHEMES = RawConfigParser() _SCHEMES.read(_CONFIG_FILE) _VAR_REPL = re.compile(r'\{([^{]*?)\}') @@ -580,10 +580,11 @@ # behaviour. pass else: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - f.close() + try: + m = re.search(r'ProductUserVisibleVersion\s*' + r'(.*?)', f.read()) + finally: + f.close() if m is not None: macrelease = '.'.join(m.group(1).split('.')[:2]) # else: fall back to the default behaviour diff --git a/src/distutils2/_backport/tarfile.py b/src/distutils2/_backport/tarfile.py --- a/src/distutils2/_backport/tarfile.py +++ b/src/distutils2/_backport/tarfile.py @@ -370,7 +370,7 @@ #--------------------------- # internal stream interface #--------------------------- -class _LowLevelFile: +class _LowLevelFile(object): """Low-level file object. Supports reading and writing. It is used instead of a regular file object for streaming access. @@ -394,7 +394,7 @@ def write(self, s): os.write(self.fd, s) -class _Stream: +class _Stream(object): """Class that serves as an adapter between TarFile and a stream-like object. The stream-like object only needs to have a read() or write() method and is accessed @@ -2003,8 +2003,10 @@ # Append the tar header and data to the archive. if tarinfo.isreg(): f = bltn_open(name, "rb") - self.addfile(tarinfo, f) - f.close() + try: + self.addfile(tarinfo, f) + finally: + f.close() elif tarinfo.isdir(): self.addfile(tarinfo) @@ -2214,9 +2216,11 @@ """ source = self.extractfile(tarinfo) target = bltn_open(targetpath, "wb") - copyfileobj(source, target) - source.close() - target.close() + try: + copyfileobj(source, target) + finally: + source.close() + target.close() def makeunknown(self, tarinfo, targetpath): """Make a file from a TarInfo object with an unknown type @@ -2423,7 +2427,7 @@ print >> sys.stderr, msg # class TarFile -class TarIter: +class TarIter(object): """Iterator Class. for tarinfo in TarFile(...): @@ -2460,7 +2464,7 @@ return tarinfo # Helper classes for sparse file support -class _section: +class _section(object): """Base class for _data and _hole. """ def __init__(self, offset, size): @@ -2507,7 +2511,7 @@ #--------------------------------------------- TAR_PLAIN = 0 # zipfile.ZIP_STORED TAR_GZIPPED = 8 # zipfile.ZIP_DEFLATED -class TarFileCompat: +class TarFileCompat(object): """TarFile class compatible with standard module zipfile's ZipFile class. """ @@ -2564,8 +2568,10 @@ are able to handle, else return False. """ try: - t = open(name) - t.close() + try: + t = open(name) + finally: + t.close() return True except TarError: return False diff --git a/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO b/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO --- a/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO +++ b/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO @@ -2,4 +2,5 @@ Name: bacon Version: 0.1 Provides-Dist: truffles (2.0) +Provides-Dist: bacon (0.1) Obsoletes-Dist: truffles (>=0.9,<=1.5) diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO new file mode 100644 --- /dev/null +++ b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,18 @@ +Metadata-Version: 1.0 +Name: banana +Version: 0.4 +Summary: A yellow fruit +Home-page: http://en.wikipedia.org/wiki/Banana +Author: Josip Djolonga +Author-email: foo at nbar.com +License: BSD +Description: A fruit +Keywords: foo bar +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Scientific/Engineering :: GIS diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt new file mode 100644 diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt new file mode 100644 --- /dev/null +++ b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1,1 @@ + diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt new file mode 100644 --- /dev/null +++ b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt @@ -0,0 +1,3 @@ + + # -*- Entry points: -*- + \ No newline at end of file diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe new file mode 100644 --- /dev/null +++ b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe @@ -0,0 +1,1 @@ + diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt new file mode 100644 --- /dev/null +++ b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt @@ -0,0 +1,6 @@ +# this should be ignored + +strawberry >=0.5 + +[section ignored] +foo ==0.5 diff --git a/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt b/src/distutils2/_backport/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt new file mode 100644 diff --git a/src/distutils2/_backport/tests/fake_dists/strawberry-0.6.egg b/src/distutils2/_backport/tests/fake_dists/strawberry-0.6.egg new file mode 100644 index 0000000000000000000000000000000000000000..6d160e8b161031ae52638514843592187925b757 GIT binary patch literal 1402 zc$`yK)KALH(=X28%1l#;R!B%nEKbc!%uQ8LF-TCbRZuEUEh#N1$r9UQWv|0ty0Ufu2)H%gjmDgJ}xL0otCbPy`8 at OrXVy z$=M1e`3iV~M*-+)g_5F5g~as4%sjABpm0h{1UV)xlPkcRnT3l11j?rGw_!j6Vhl12 zuI}!-o_=or`X%`V at j0nwsX2Nj6(yk|oD9qiubF*7xU_i9RnLxBqqeU~_GC)B*8Q%^m+PITr6Z&8t31F+o4>xK^{d-p7?^ zx~J#&ZkWuS9=1.0')] checkLists(l, ['choxie', 'towel-stuff']) + l = [dist.name for dist in provides_distribution('strawberry', '0.6', + use_egg_info=True)] + checkLists(l, ['strawberry']) + + l = [dist.name for dist in provides_distribution('strawberry', '>=0.5', + use_egg_info=True)] + checkLists(l, ['strawberry']) + + + l = [dist.name for dist in provides_distribution('strawberry', '>0.6', + use_egg_info=True)] + checkLists(l, []) + + + l = [dist.name for dist in provides_distribution('banana', '0.4', + use_egg_info=True)] + checkLists(l, ['banana']) + + l = [dist.name for dist in provides_distribution('banana', '>=0.3', + use_egg_info=True)] + checkLists(l, ['banana']) + + + l = [dist.name for dist in provides_distribution('banana', '!=0.4', + use_egg_info=True)] + checkLists(l, []) + def test_obsoletes(self): - """ Test looking for distributions based on what they obsolete """ + # Test looking for distributions based on what they obsolete from distutils2._backport.pkgutil import obsoletes_distribution from distutils2.errors import DistutilsError - checkLists = lambda x,y: self.assertListEqual(sorted(x), sorted(y)) + checkLists = lambda x, y: self.assertListEqual(sorted(x), sorted(y)) l = [dist.name for dist in obsoletes_distribution('truffles', '1.0')] checkLists(l, []) @@ -363,7 +542,8 @@ l = [dist.name for dist in obsoletes_distribution('truffles', '0.9.6')] checkLists(l, ['choxie', 'towel-stuff']) - l = [dist.name for dist in obsoletes_distribution('truffles', '0.5.2.3')] + l = [dist.name for dist in obsoletes_distribution('truffles', \ + '0.5.2.3')] checkLists(l, ['choxie', 'towel-stuff']) l = [dist.name for dist in obsoletes_distribution('truffles', '0.2')] @@ -372,26 +552,17 @@ def test_suite(): suite = unittest.TestSuite() - testcase_loader = unittest.loader.defaultTestLoader.loadTestsFromTestCase - suite.addTest(testcase_loader(TestPkgUtilFunctions)) - suite.addTest(testcase_loader(TestPkgUtilDistribution)) + load = unittest.defaultTestLoader.loadTestsFromTestCase + suite.addTest(load(TestPkgUtilData)) + suite.addTest(load(TestPkgUtilDistribution)) + suite.addTest(load(TestPkgUtilPEP302)) + suite.addTest(load(TestPkgUtilPEP376)) return suite + def test_main(): run_unittest(test_suite()) -if __name__ == "__main__": - test_main() - -def test_suite(): - suite = unittest.TestSuite() - testcase_loader = unittest.loader.defaultTestLoader.loadTestsFromTestCase - suite.addTest(testcase_loader(TestPkgUtilFunctions)) - suite.addTest(testcase_loader(TestPkgUtilDistribution)) - return suite - -def test_main(): - run_unittest(test_suite()) if __name__ == "__main__": test_main() diff --git a/src/distutils2/_backport/tests/test_sysconfig.py b/src/distutils2/_backport/tests/test_sysconfig.py --- a/src/distutils2/_backport/tests/test_sysconfig.py +++ b/src/distutils2/_backport/tests/test_sysconfig.py @@ -8,7 +8,7 @@ import os import shutil from copy import copy, deepcopy -from ConfigParser import ConfigParser +from ConfigParser import RawConfigParser from test.test_support import run_unittest, TESTFN @@ -88,15 +88,13 @@ shutil.rmtree(path) def test_nested_var_substitution(self): - """Assert that the {curly brace token} expansion pattern will replace - only the inner {something} on nested expressions like {py{something}} on - the first pass. + # Assert that the {curly brace token} expansion pattern will replace + # only the inner {something} on nested expressions like {py{something}} on + # the first pass. - We have no plans to make use of this, but it keeps the option open for - the future, at the cost only of disallowing { itself as a piece of a - substitution key (which would be weird). - - """ + # We have no plans to make use of this, but it keeps the option open for + # the future, at the cost only of disallowing { itself as a piece of a + # substitution key (which would be weird). self.assertEqual(_subst_vars('{py{version}}', {'version': '31'}), '{py31}') def test_get_paths(self): @@ -107,7 +105,7 @@ wanted.sort() scheme = scheme.items() scheme.sort() - self.assertEquals(scheme, wanted) + self.assertEqual(scheme, wanted) def test_get_config_vars(self): cvars = get_config_vars() @@ -120,21 +118,21 @@ sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Intel)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win32') + self.assertEqual(get_platform(), 'win32') # windows XP, amd64 os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Amd64)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-amd64') + self.assertEqual(get_platform(), 'win-amd64') # windows XP, itanium os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Itanium)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-ia64') + self.assertEqual(get_platform(), 'win-ia64') # macbook os.name = 'posix' @@ -153,9 +151,9 @@ maxint = sys.maxint try: sys.maxint = 2147483647 - self.assertEquals(get_platform(), 'macosx-10.3-ppc') + self.assertEqual(get_platform(), 'macosx-10.3-ppc') sys.maxint = 9223372036854775807 - self.assertEquals(get_platform(), 'macosx-10.3-ppc64') + self.assertEqual(get_platform(), 'macosx-10.3-ppc64') finally: sys.maxint = maxint @@ -173,9 +171,9 @@ maxint = sys.maxint try: sys.maxint = 2147483647 - self.assertEquals(get_platform(), 'macosx-10.3-i386') + self.assertEqual(get_platform(), 'macosx-10.3-i386') sys.maxint = 9223372036854775807 - self.assertEquals(get_platform(), 'macosx-10.3-x86_64') + self.assertEqual(get_platform(), 'macosx-10.3-x86_64') finally: sys.maxint = maxint @@ -186,33 +184,33 @@ '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat') + self.assertEqual(get_platform(), 'macosx-10.4-fat') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-intel') + self.assertEqual(get_platform(), 'macosx-10.4-intel') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat3') + self.assertEqual(get_platform(), 'macosx-10.4-fat3') get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-universal') + self.assertEqual(get_platform(), 'macosx-10.4-universal') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat64') + self.assertEqual(get_platform(), 'macosx-10.4-fat64') for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' @@ -220,7 +218,7 @@ '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3'%(arch,)) - self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) + self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,)) # linux debian sarge os.name = 'posix' @@ -230,7 +228,7 @@ self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) - self.assertEquals(get_platform(), 'linux-i686') + self.assertEqual(get_platform(), 'linux-i686') # XXX more platforms to tests here @@ -241,11 +239,11 @@ def test_get_scheme_names(self): wanted = ('nt', 'nt_user', 'os2', 'os2_home', 'posix_home', 'posix_prefix', 'posix_user') - self.assertEquals(get_scheme_names(), wanted) + self.assertEqual(get_scheme_names(), wanted) def test_expand_globals(self): - config = ConfigParser() + config = RawConfigParser() config.add_section('globals') config.set('globals', 'foo', 'ok') config.add_section('posix') @@ -254,8 +252,8 @@ _expand_globals(config) - self.assertEquals(config.get('posix', 'foo'), 'ok') - self.assertEquals(config.get('posix', 'more'), '/etc/ok') + self.assertEqual(config.get('posix', 'foo'), 'ok') + self.assertEqual(config.get('posix', 'more'), '/etc/ok') # we might not have globals after all # extending again (==no more globals section) diff --git a/src/distutils2/command/bdist.py b/src/distutils2/command/bdist.py --- a/src/distutils2/command/bdist.py +++ b/src/distutils2/command/bdist.py @@ -62,7 +62,7 @@ 'os2': 'zip'} # Establish the preferred order (for the --help-formats option). - format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', + format_commands = ['gztar', 'bztar', 'ztar', 'tar', 'wininst', 'zip', 'msi'] # And the real information. @@ -96,7 +96,7 @@ # 'bdist_base' -- parent of per-built-distribution-format # temporary directories (eg. we'll probably have - # "build/bdist./dumb", "build/bdist./rpm", etc.) + # "build/bdist./dumb", etc.) if self.bdist_base is None: build_base = self.get_finalized_command('build').build_base self.bdist_base = os.path.join(build_base, @@ -126,7 +126,7 @@ # Reinitialize and run each command. for i in range(len(self.formats)): cmd_name = commands[i] - sub_cmd = self.reinitialize_command(cmd_name) + sub_cmd = self.get_reinitialized_command(cmd_name) # passing the owner and group names for tar archiving if cmd_name == 'bdist_dumb': diff --git a/src/distutils2/command/bdist_dumb.py b/src/distutils2/command/bdist_dumb.py --- a/src/distutils2/command/bdist_dumb.py +++ b/src/distutils2/command/bdist_dumb.py @@ -85,7 +85,7 @@ if not self.skip_build: self.run_command('build') - install = self.reinitialize_command('install', reinit_subcommands=1) + install = self.get_reinitialized_command('install', reinit_subcommands=1) install.root = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 @@ -115,8 +115,9 @@ % (repr(install.install_base), repr(install.install_platbase))) else: - archive_root = os.path.join(self.bdist_dir, - ensure_relative(install.install_base)) + archive_root = os.path.join( + self.bdist_dir, + self._ensure_relative(install.install_base)) # Make the archive filename = self.make_archive(pseudoinstall_root, @@ -135,3 +136,9 @@ else: rmtree(self.bdist_dir) + def _ensure_relative(self, path): + # copied from dir_util, deleted + drive, path = os.path.splitdrive(path) + if path[0:1] == os.sep: + path = drive + path[1:] + return path diff --git a/src/distutils2/command/bdist_msi.py b/src/distutils2/command/bdist_msi.py --- a/src/distutils2/command/bdist_msi.py +++ b/src/distutils2/command/bdist_msi.py @@ -177,12 +177,12 @@ if not self.skip_build: self.run_command('build') - install = self.reinitialize_command('install', reinit_subcommands=1) + install = self.get_reinitialized_command('install', reinit_subcommands=1) install.prefix = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 - install_lib = self.reinitialize_command('install_lib') + install_lib = self.get_reinitialized_command('install_lib') # we do not want to include pyc or pyo files install_lib.compile = 0 install_lib.optimize = 0 diff --git a/src/distutils2/command/bdist_wininst.py b/src/distutils2/command/bdist_wininst.py --- a/src/distutils2/command/bdist_wininst.py +++ b/src/distutils2/command/bdist_wininst.py @@ -8,6 +8,7 @@ import sys import os import string +from shutil import rmtree try: from sysconfig import get_python_version except ImportError: @@ -126,13 +127,13 @@ if not self.skip_build: self.run_command('build') - install = self.reinitialize_command('install', reinit_subcommands=1) + install = self.get_reinitialized_command('install', reinit_subcommands=1) install.root = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 install.plat_name = self.plat_name - install_lib = self.reinitialize_command('install_lib') + install_lib = self.get_reinitialized_command('install_lib') # we do not want to include pyc or pyo files install_lib.compile = 0 install_lib.optimize = 0 diff --git a/src/distutils2/command/build_ext.py b/src/distutils2/command/build_ext.py --- a/src/distutils2/command/build_ext.py +++ b/src/distutils2/command/build_ext.py @@ -188,7 +188,24 @@ if self.package is None: self.package = self.distribution.ext_package + # Ensure that the list of extensions is valid, i.e. it is a list of + # Extension objects. self.extensions = self.distribution.ext_modules + if self.extensions: + if not isinstance(self.extensions, (list, tuple)): + type_name = (self.extensions is None and 'None' + or type(self.extensions).__name__) + raise DistutilsSetupError( + "'ext_modules' must be a sequence of Extension instances," + " not %s" % (type_name,)) + for i, ext in enumerate(self.extensions): + if isinstance(ext, Extension): + continue # OK! (assume type-checking done + # by Extension constructor) + type_name = (ext is None and 'None' or type(ext).__name__) + raise DistutilsSetupError( + "'ext_modules' item %d must be an Extension instance," + " not %s" % (i, type_name)) # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. @@ -396,86 +413,7 @@ # Now actually compile and link everything. self.build_extensions() - def check_extensions_list(self, extensions): - """Ensure that the list of extensions (presumably provided as a - command option 'extensions') is valid, i.e. it is a list of - Extension objects. We also support the old-style list of 2-tuples, - where the tuples are (ext_name, build_info), which are converted to - Extension instances here. - - Raise DistutilsSetupError if the structure is invalid anywhere; - just returns otherwise. - """ - if not isinstance(extensions, list): - raise DistutilsSetupError, \ - "'ext_modules' option must be a list of Extension instances" - - for i, ext in enumerate(extensions): - if isinstance(ext, Extension): - continue # OK! (assume type-checking done - # by Extension constructor) - - if not isinstance(ext, tuple) or len(ext) != 2: - raise DistutilsSetupError, \ - ("each element of 'ext_modules' option must be an " - "Extension instance or 2-tuple") - - ext_name, build_info = ext - - log.warn(("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s'" - "-- please convert to Extension instance" % ext_name)) - - if not (isinstance(ext_name, str) and - extension_name_re.match(ext_name)): - raise DistutilsSetupError, \ - ("first element of each tuple in 'ext_modules' " - "must be the extension name (a string)") - - if not isinstance(build_info, dict): - raise DistutilsSetupError, \ - ("second element of each tuple in 'ext_modules' " - "must be a dictionary (build info)") - - # OK, the (ext_name, build_info) dict is type-safe: convert it - # to an Extension instance. - ext = Extension(ext_name, build_info['sources']) - - # Easy stuff: one-to-one mapping from dict elements to - # instance attributes. - for key in ('include_dirs', 'library_dirs', 'libraries', - 'extra_objects', 'extra_compile_args', - 'extra_link_args'): - val = build_info.get(key) - if val is not None: - setattr(ext, key, val) - - # Medium-easy stuff: same syntax/semantics, different names. - ext.runtime_library_dirs = build_info.get('rpath') - if 'def_file' in build_info: - log.warn("'def_file' element of build info dict " - "no longer supported") - - # Non-trivial stuff: 'macros' split into 'define_macros' - # and 'undef_macros'. - macros = build_info.get('macros') - if macros: - ext.define_macros = [] - ext.undef_macros = [] - for macro in macros: - if not (isinstance(macro, tuple) and len(macro) in (1, 2)): - raise DistutilsSetupError, \ - ("'macros' element of build info dict " - "must be 1- or 2-tuple") - if len(macro) == 1: - ext.undef_macros.append(macro[0]) - elif len(macro) == 2: - ext.define_macros.append(macro) - - extensions[i] = ext - def get_source_files(self): - self.check_extensions_list(self.extensions) filenames = [] # Wouldn't it be neat if we knew the names of header files too... @@ -485,11 +423,6 @@ return filenames def get_outputs(self): - # Sanity check the 'extensions' list -- can't assume this is being - # done in the same run as a 'build_extensions()' call (in fact, we - # can probably assume that it *isn't*!). - self.check_extensions_list(self.extensions) - # And build the list of output (built) filenames. Note that this # ignores the 'inplace' flag, and assumes everything goes in the # "build" tree. @@ -499,9 +432,6 @@ return outputs def build_extensions(self): - # First, sanity-check the 'extensions' list - self.check_extensions_list(self.extensions) - for ext in self.extensions: try: self.build_extension(ext) diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -6,14 +6,66 @@ import os import sys +import logging from glob import glob +import distutils2 from distutils2.core import Command from distutils2.errors import DistutilsOptionError, DistutilsFileError from distutils2.util import convert_path -from distutils2 import log +from distutils2.converter.refactor import DistutilsRefactoringTool -class build_py(Command): +# marking public APIs +__all__ = ['Mixin2to3', 'build_py'] + +try: + from distutils2.util import Mixin2to3 as _Mixin2to3 + from lib2to3.refactor import get_fixers_from_package + _CONVERT = True + _KLASS = _Mixin2to3 +except ImportError: + _CONVERT = False + _KLASS = object + +class Mixin2to3(_KLASS): + """ The base class which can be used for refactoring. When run under + Python 3.0, the run_2to3 method provided by Mixin2to3 is overridden. + When run on Python 2.x, it merely creates a class which overrides run_2to3, + yet does nothing in particular with it. + """ + if _CONVERT: + def _run_2to3(self, files, doctests=[]): + """ Takes a list of files and doctests, and performs conversion + on those. + - First, the files which contain the code(`files`) are converted. + - Second, the doctests in `files` are converted. + - Thirdly, the doctests in `doctests` are converted. + """ + + # Convert the ".py" files. + logging.info("Converting Python code") + _KLASS.run_2to3(self, files) + + # Convert the doctests in the ".py" files. + logging.info("Converting doctests with '.py' files") + _KLASS.run_2to3(self, files, doctests_only=True) + + # If the following conditions are met, then convert:- + # 1. User has specified the 'convert_2to3_doctests' option. So, we + # can expect that the list 'doctests' is not empty. + # 2. The default is allow distutils2 to allow conversion of text files + # containing doctests. It is set as + # distutils2.run_2to3_on_doctests + + if doctests != [] and distutils2.run_2to3_on_doctests: + logging.info("Converting text files which contain doctests") + _KLASS.run_2to3(self, doctests, doctests_only=True) + else: + # If run on Python 2.x, there is nothing to do. + def _run_2to3(self, files, doctests=[]): + pass + +class build_py(Command, Mixin2to3): description = "\"build\" pure Python modules (copy to build directory)" @@ -39,6 +91,8 @@ self.compile = 0 self.optimize = 0 self.force = None + self._updated_files = [] + self._doctests_2to3 = [] def finalize_options(self): self.set_undefined_options('build', @@ -93,10 +147,18 @@ self.build_packages() self.build_package_data() + if self.distribution.use_2to3 and self_updated_files: + self.run_2to3(self._updated_files, self._doctests_2to3) + self.byte_compile(self.get_outputs(include_bytecode=0)) + # -- Top-level worker functions ------------------------------------ + def get_data_files(self): - """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" + """Generate list of '(package,src_dir,build_dir,filenames)' tuples. + + Helper function for `finalize_options()`. + """ data = [] if not self.packages: return data @@ -120,7 +182,10 @@ return data def find_data_files(self, package, src_dir): - """Return filenames for package's data files in 'src_dir'""" + """Return filenames for package's data files in 'src_dir'. + + Helper function for `get_data_files()`. + """ globs = (self.package_data.get('', []) + self.package_data.get(package, [])) files = [] @@ -132,14 +197,21 @@ return files def build_package_data(self): - """Copy data files into build directory""" + """Copy data files into build directory. + + Helper function for `run()`. + """ for package, src_dir, build_dir, filenames in self.data_files: for filename in filenames: target = os.path.join(build_dir, filename) self.mkpath(os.path.dirname(target)) - self.copy_file(os.path.join(src_dir, filename), target, - preserve_mode=False) + outf, copied = self.copy_file(os.path.join(src_dir, filename), + target, preserve_mode=False) + if copied and srcfile in self.distribution.convert_2to3.doctests: + self._doctests_2to3.append(outf) + # XXX - this should be moved to the Distribution class as it is not + # only needed for build_py. It also has no dependencies on this class. def get_package_dir(self, package): """Return the directory, relative to the top of the source distribution, where package 'package' should be found @@ -181,6 +253,8 @@ return '' def check_package(self, package, package_dir): + """Helper function for `find_package_modules()` and `find_modules()'. + """ # Empty dir name means current directory, which we can probably # assume exists. Also, os.path.exists and isdir don't know about # my "empty string means current dir" convention, so we have to @@ -200,8 +274,8 @@ if os.path.isfile(init_py): return init_py else: - log.warn(("package init file '%s' not found " + - "(or not a regular file)"), init_py) + logging.warning(("package init file '%s' not found " + + "(or not a regular file)"), init_py) # Either not in a package at all (__init__.py not expected), or # __init__.py doesn't exist -- so don't return the filename. @@ -209,7 +283,8 @@ def check_module(self, module, module_file): if not os.path.isfile(module_file): - log.warn("file %s (for module %s) not found", module_file, module) + logging.warning("file %s (for module %s) not found", + module_file, module) return False else: return True diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -6,7 +6,7 @@ __revision__ = "$Id: cmd.py 75192 2009-10-02 23:49:48Z tarek.ziade $" -import sys, os, re +import os, re from distutils2.errors import DistutilsOptionError from distutils2 import util from distutils2 import log @@ -19,7 +19,7 @@ except ImportError: from distutils2._backport.shutil import make_archive -class Command: +class Command(object): """Abstract base class for defining command classes, the "worker bees" of the Distutils. A useful analogy for command classes is to think of them as subroutines with local variables called "options". The options @@ -188,6 +188,31 @@ """ log.log(level, msg) + # -- External interface -------------------------------------------- + # (called by outsiders) + + def get_source_files(self): + """Return the list of files that are used as inputs to this command, + i.e. the files used to generate the output files. The result is used + by the `sdist` command in determining the set of default files. + + Command classes should implement this method if they operate on files + from the source tree. + """ + return [] + + def get_outputs(self): + """Return the list of files that would be produced if this command + were actually run. Not affected by the "dry-run" flag or whether + any other commands have been run. + + Command classes should implement this method if they produce any + output files that get consumed by another command. e.g., `build_ext` + returns the list of built extension modules, but not any temporary + files used in the compilation process. + """ + return [] + # -- Option validation methods ------------------------------------- # (these are very handy in writing the 'finalize_options()' method) # @@ -307,10 +332,8 @@ cmd_obj.ensure_finalized() return cmd_obj - # XXX rename to 'get_reinitialized_command()'? (should do the - # same in dist.py, if so) - def reinitialize_command(self, command, reinit_subcommands=0): - return self.distribution.reinitialize_command( + def get_reinitialized_command(self, command, reinit_subcommands=0): + return self.distribution.get_reinitialized_command( command, reinit_subcommands) def run_command(self, command): @@ -350,8 +373,10 @@ if os.path.isdir(name) or name == '': return if dry_run: + head = '' for part in name.split(os.sep): - self.log(part) + log.info("created directory %s%s", head, part) + head += part + os.sep return os.makedirs(name, mode) @@ -386,7 +411,7 @@ def spawn(self, cmd, search_path=1, level=1): """Spawn an external command respecting dry-run flag.""" - from distutils2.spawn import spawn + from distutils2.util import spawn spawn(cmd, search_path, dry_run= self.dry_run) def make_archive(self, base_name, format, root_dir=None, base_dir=None, @@ -422,7 +447,7 @@ # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if self.force or dep_util.newer_group(infiles, outfile): + if self.force or util.newer_group(infiles, outfile): self.execute(func, args, exec_msg, level) # Otherwise, print the "skip" message diff --git a/src/distutils2/command/register.py b/src/distutils2/command/register.py --- a/src/distutils2/command/register.py +++ b/src/distutils2/command/register.py @@ -28,8 +28,6 @@ boolean_options = PyPIRCCommand.boolean_options + [ 'verify', 'list-classifiers', 'strict'] - sub_commands = [('check', lambda self: True)] - def initialize_options(self): PyPIRCCommand.initialize_options(self) self.list_classifiers = 0 @@ -46,9 +44,8 @@ self.finalize_options() self._set_config() - # Run sub commands - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) + # Check the package metadata + self.run_command('check') if self.dry_run: self.verify_metadata() diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -18,12 +18,11 @@ from distutils2._backport.shutil import get_archive_formats from distutils2.core import Command -from distutils2 import util from distutils2.errors import (DistutilsPlatformError, DistutilsOptionError, - DistutilsTemplateError) + DistutilsTemplateError) from distutils2.manifest import Manifest from distutils2 import log -from distutils2.util import convert_path, newer +from distutils2.util import convert_path def show_formats(): """Print all possible values for the 'formats' option (used by @@ -45,12 +44,6 @@ description = "create a source distribution (tarball, zip file, etc.)" - def checking_metadata(self): - """Callable used for the check sub-command. - - Placed here so user_options can view it""" - return self.metadata_check - user_options = [ ('template=', 't', "name of manifest template file [default: MANIFEST.in]"), @@ -100,8 +93,6 @@ default_format = {'posix': 'gztar', 'nt': 'zip' } - sub_commands = [('check', checking_metadata)] - def initialize_options(self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. @@ -162,9 +153,9 @@ # manifest self.filelist.clear() - # Run sub commands - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) + # Check the package metadata + if self.metadata_check: + self.run_command('check') # Do whatever it takes to get the list of files to process # (process the manifest template, read an existing manifest, diff --git a/src/distutils2/command/upload.py b/src/distutils2/command/upload.py --- a/src/distutils2/command/upload.py +++ b/src/distutils2/command/upload.py @@ -15,7 +15,7 @@ from distutils2.errors import DistutilsOptionError from distutils2.core import PyPIRCCommand -from distutils2.spawn import spawn +from distutils2.util import spawn from distutils2 import log class upload(PyPIRCCommand): diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -1,4 +1,4 @@ -import base64, httplib, os.path, socket, tempfile, urlparse, zipfile +import base64, httplib, os.path, socket, urlparse, zipfile from cStringIO import StringIO from distutils2 import log from distutils2.command.upload import upload @@ -81,7 +81,6 @@ raise DistutilsFileError(mesg % upload_dir) def run(self): - tmp_dir = tempfile.mkdtemp() name = self.distribution.metadata['Name'] version = self.distribution.metadata['Version'] zip_file = zip_dir(self.upload_dir) @@ -125,7 +124,7 @@ elif r.status == 301: location = r.getheader('Location') if location is None: - location = 'http://packages.python.org/%s/' % meta.get_name() + location = 'http://packages.python.org/%s/' % name self.announce('Upload successful. Visit %s' % location, log.INFO) else: diff --git a/src/distutils2/compiler/bcppcompiler.py b/src/distutils2/compiler/bcppcompiler.py --- a/src/distutils2/compiler/bcppcompiler.py +++ b/src/distutils2/compiler/bcppcompiler.py @@ -16,7 +16,7 @@ import os from distutils2.errors import (DistutilsExecError, CompileError, LibError, - LinkError, UnknownFileError) + LinkError, UnknownFileError) from distutils2.compiler.ccompiler import CCompiler, gen_preprocess_options from distutils2.file_util import write_file from distutils2.dep_util import newer diff --git a/src/distutils2/compiler/ccompiler.py b/src/distutils2/compiler/ccompiler.py --- a/src/distutils2/compiler/ccompiler.py +++ b/src/distutils2/compiler/ccompiler.py @@ -10,9 +10,8 @@ import re from distutils2.errors import (CompileError, LinkError, UnknownFileError, - DistutilsPlatformError, DistutilsModuleError) -from distutils2.spawn import spawn -from distutils2.util import split_quoted, execute, newer_group + DistutilsPlatformError, DistutilsModuleError) +from distutils2.util import split_quoted, execute, newer_group, spawn from distutils2 import log from shutil import move @@ -76,7 +75,7 @@ compiler.shared_lib_extension = so_ext -class CCompiler: +class CCompiler(object): """Abstract base class to define the interface that must be implemented by real compiler classes. Also has some utility methods used by several compiler classes. @@ -800,14 +799,16 @@ library_dirs = [] fd, fname = tempfile.mkstemp(".c", funcname, text=True) f = os.fdopen(fd, "w") - for incl in includes: - f.write("""#include "%s"\n""" % incl) - f.write("""\ + try: + for incl in includes: + f.write("""#include "%s"\n""" % incl) + f.write("""\ main (int argc, char **argv) { %s(); } """ % funcname) - f.close() + finally: + f.close() try: objects = self.compile([fname], include_dirs=include_dirs) except CompileError: @@ -938,8 +939,10 @@ if os.path.isdir(name) or name == '': return if self.dry_run: + head = '' for part in name.split(os.sep): - self.log(part) + log.info("created directory %s%s", head, part) + head += part + os.sep return os.makedirs(name, mode) @@ -1048,7 +1051,7 @@ module_name = "distutils2.compiler." + module_name __import__ (module_name) module = sys.modules[module_name] - klass = vars(module)[class_name] + cls = vars(module)[class_name] except ImportError: raise DistutilsModuleError, \ "can't compile C/C++ code: unable to load module '%s'" % \ @@ -1061,7 +1064,7 @@ # XXX The None is necessary to preserve backwards compatibility # with classes that expect verbose to be the first positional # argument. - return klass(None, dry_run, force) + return cls(None, dry_run, force) def gen_preprocess_options(macros, include_dirs): diff --git a/src/distutils2/compiler/emxccompiler.py b/src/distutils2/compiler/emxccompiler.py --- a/src/distutils2/compiler/emxccompiler.py +++ b/src/distutils2/compiler/emxccompiler.py @@ -25,9 +25,8 @@ from warnings import warn from distutils2.compiler.unixccompiler import UnixCCompiler -from distutils2.util import write_file from distutils2.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils2.util import get_compiler_versions +from distutils2.util import get_compiler_versions, write_file class EMXCCompiler (UnixCCompiler): @@ -273,8 +272,10 @@ # It would probably better to read single lines to search. # But we do this only once, and it is fast enough f = open(fn) - s = f.read() - f.close() + try: + s = f.read() + finally: + f.close() except IOError, exc: # if we can't read this file, we cannot say it is wrong diff --git a/src/distutils2/compiler/msvc9compiler.py b/src/distutils2/compiler/msvc9compiler.py --- a/src/distutils2/compiler/msvc9compiler.py +++ b/src/distutils2/compiler/msvc9compiler.py @@ -20,7 +20,7 @@ import re from distutils2.errors import (DistutilsExecError, DistutilsPlatformError, - CompileError, LibError, LinkError) + CompileError, LibError, LinkError) from distutils2.compiler.ccompiler import CCompiler, gen_lib_options from distutils2 import log from distutils2.util import get_platform @@ -50,7 +50,7 @@ 'win-ia64' : 'ia64', } -class Reg: +class Reg(object): """Helper class to read values from the registry """ @@ -112,7 +112,7 @@ return s convert_mbcs = staticmethod(convert_mbcs) -class MacroExpander: +class MacroExpander(object): def __init__(self, version): self.macros = {} diff --git a/src/distutils2/compiler/msvccompiler.py b/src/distutils2/compiler/msvccompiler.py --- a/src/distutils2/compiler/msvccompiler.py +++ b/src/distutils2/compiler/msvccompiler.py @@ -15,7 +15,7 @@ import string from distutils2.errors import (DistutilsExecError, DistutilsPlatformError, - CompileError, LibError, LinkError) + CompileError, LibError, LinkError) from distutils2.compiler.ccompiler import CCompiler, gen_lib_options from distutils2 import log @@ -104,7 +104,7 @@ pass return s -class MacroExpander: +class MacroExpander(object): def __init__(self, version): self.macros = {} diff --git a/src/distutils2/compiler/unixccompiler.py b/src/distutils2/compiler/unixccompiler.py --- a/src/distutils2/compiler/unixccompiler.py +++ b/src/distutils2/compiler/unixccompiler.py @@ -19,10 +19,10 @@ from types import StringType, NoneType from distutils2.util import newer -from distutils2.compiler.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options -from distutils2.errors import \ - DistutilsExecError, CompileError, LibError, LinkError +from distutils2.compiler.ccompiler import (CCompiler, gen_preprocess_options, + gen_lib_options) +from distutils2.errors import (DistutilsExecError, CompileError, + LibError, LinkError) from distutils2 import log try: diff --git a/src/distutils2/config.py b/src/distutils2/config.py --- a/src/distutils2/config.py +++ b/src/distutils2/config.py @@ -4,7 +4,7 @@ that uses .pypirc in the distutils.command package. """ import os -from ConfigParser import ConfigParser +from ConfigParser import RawConfigParser from distutils2.command.cmd import Command @@ -59,7 +59,7 @@ if os.path.exists(rc): self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY - config = ConfigParser() + config = RawConfigParser() config.read(rc) sections = config.sections() if 'distutils' in sections: diff --git a/src/distutils2/converter/fixers/fix_imports.py b/src/distutils2/converter/fixers/fix_imports.py --- a/src/distutils2/converter/fixers/fix_imports.py +++ b/src/distutils2/converter/fixers/fix_imports.py @@ -36,11 +36,16 @@ pattern = [] next = imp.next_sibling while next is not None: + # Get the first child if we have a Node + if not hasattr(next, "value"): + next = next.children[0] pattern.append(next.value) if not hasattr(next, "next_sibling"): next.next_sibling = next.get_next_sibling() next = next.next_sibling - if pattern == ['import', 'setup']: + + if set(pattern).issubset(set( + ['import', ',', 'setup', 'find_packages'])): imp.value = 'distutils2.core' imp.changed() diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -2,8 +2,9 @@ The only module that needs to be imported to use the Distutils; provides the 'setup' function (which is to be called from the setup script). Also -indirectly provides the Distribution and Command classes, although they are -really defined in distutils2.dist and distutils2.cmd. +exports useful classes so that setup scripts can import them from here +although they are really defined in other modules: Distribution, Command, +PyPIRCommand, Extension, find_packages. """ __revision__ = "$Id: core.py 77704 2010-01-23 09:23:15Z tarek.ziade $" @@ -12,7 +13,7 @@ import os from distutils2.errors import (DistutilsSetupError, DistutilsArgError, - DistutilsError, CCompilerError) + DistutilsError, CCompilerError) from distutils2.util import grok_environment_error # Mainly import these so setup scripts can "from distutils2.core import" them. @@ -20,6 +21,7 @@ from distutils2.command.cmd import Command from distutils2.config import PyPIRCCommand from distutils2.extension import Extension +from distutils2.util import find_packages # This is a barebones help message generated displayed when the user # runs the setup script with no arguments at all. More useful help @@ -47,7 +49,8 @@ 'maintainer', 'maintainer_email', 'url', 'license', 'description', 'long_description', 'keywords', 'platforms', 'classifiers', 'download_url', - 'requires', 'provides', 'obsoletes', + 'requires', 'provides', 'obsoletes', 'use_2to3', + 'convert_2to3_doctests', ) # Legal keyword arguments for the Extension constructor @@ -94,11 +97,7 @@ # Determine the distribution class -- either caller-supplied or # our Distribution (see below). - klass = attrs.get('distclass') - if klass: - del attrs['distclass'] - else: - klass = Distribution + distclass = attrs.pop('distclass', Distribution) if 'script_name' not in attrs: attrs['script_name'] = os.path.basename(sys.argv[0]) @@ -108,7 +107,7 @@ # Create the Distribution instance, using the remaining arguments # (ie. everything except distclass) to initialize it try: - _setup_distribution = dist = klass(attrs) + _setup_distribution = dist = distclass(attrs) except DistutilsSetupError, msg: if 'name' in attrs: raise SystemExit, "error in %s setup command: %s" % \ diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py --- a/src/distutils2/depgraph.py +++ b/src/distutils2/depgraph.py @@ -1,47 +1,49 @@ -""" -A dependency graph generator. The graph is represented as an instance of -:class:`DependencyGraph`, and DOT output is possible as well. +"""Analyse the relationships between the distributions in the system and generate +a dependency graph. """ -from distutils2._backport import pkgutil from distutils2.errors import DistutilsError from distutils2.version import VersionPredicate -__all__ = ['DependencyGraph', 'generate_graph'] +__all__ = ['DependencyGraph', 'generate_graph', 'dependent_dists', + 'graph_to_dot'] class DependencyGraph(object): """ Represents a dependency graph between distributions. - The depedency relationships are stored in an *adjacency_list* that maps + The dependency relationships are stored in an ``adjacency_list`` that maps distributions to a list of ``(other, label)`` tuples where ``other`` is a distribution and the edge is labelled with ``label`` (i.e. the version - specifier, if such was provided). If any missing depencies are found, - they are stored in ``missing``. It maps distributions to a list of - requirements that were not provided by any other distributions. + specifier, if such was provided). Also, for more efficient traversal, for + every distribution ``x``, a list of predecessors is kept in + ``reverse_list[x]``. An edge from distribution ``a`` to + distribution ``b`` means that ``a`` depends on ``b``. If any missing + depencies are found, they are stored in ``missing``, which is a dictionary + that maps distributions to a list of requirements that were not provided by + any other distributions. """ def __init__(self): self.adjacency_list = {} + self.reverse_list = {} self.missing = {} def add_distribution(self, distribution): - """ - Add distribution *x* to the graph. + """Add the *distribution* to the graph. :type distribution: :class:`pkgutil.Distribution` or :class:`pkgutil.EggInfoDistribution` """ self.adjacency_list[distribution] = list() + self.reverse_list[distribution] = list() self.missing[distribution] = list() def add_edge(self, x, y, label=None): - """ - Add an edge from distribution *x* to distribution *y* with the given + """Add an edge from distribution *x* to distribution *y* with the given *label*. - :type x: :class:`pkgutil.Distribution` or :class:`pkgutil.EggInfoDistribution` :type y: :class:`pkgutil.Distribution` or @@ -49,6 +51,9 @@ :type label: ``str`` or ``None`` """ self.adjacency_list[x].append((y, label)) + # multiple edges are allowed, so be careful + if not x in self.reverse_list[y]: + self.reverse_list[y].append(x) def add_missing(self, distribution, requirement): """ @@ -60,45 +65,42 @@ """ self.missing[distribution].append(requirement) - def to_dot(self, f, skip_disconnected=True): - """ - Writes a DOT output for the graph to the provided *file*. - If *skip_disconnected* is set to ``True``, then all distributions - that are not dependent on any other distributions are skipped. - :type f: ``file`` - ;type skip_disconnected: ``bool`` - """ - if not isinstance(f, file): - raise TypeError('the argument has to be of type file') +def graph_to_dot(graph, f, skip_disconnected=True): + """Writes a DOT output for the graph to the provided file *f*. - disconnected = [] + If *skip_disconnected* is set to ``True``, then all distributions + that are not dependent on any other distribution are skipped. - f.write("digraph dependencies {\n") - for dist, adjs in self.adjacency_list.iteritems(): - if len(adjs) == 0 and not skip_disconnected: - disconnected.append(dist) - for (other, label) in adjs: - if not label is None: - f.write('"%s" -> "%s" [label="%s"]\n' % - (dist.name, other.name, label)) - else: - f.write('"%s" -> "%s"\n' % (dist.name, other.name)) - if not skip_disconnected and len(disconnected) > 0: - f.write('subgraph disconnected {\n') - f.write('label = "Disconnected"\n') - f.write('bgcolor = red\n') + :type f: has to support ``file``-like operations + :type skip_disconnected: ``bool`` + """ + disconnected = [] - for dist in disconnected: - f.write('"%s"' % dist.name) - f.write('\n') - f.write('}\n') + f.write("digraph dependencies {\n") + for dist, adjs in graph.adjacency_list.iteritems(): + if len(adjs) == 0 and not skip_disconnected: + disconnected.append(dist) + for (other, label) in adjs: + if not label is None: + f.write('"%s" -> "%s" [label="%s"]\n' % + (dist.name, other.name, label)) + else: + f.write('"%s" -> "%s"\n' % (dist.name, other.name)) + if not skip_disconnected and len(disconnected) > 0: + f.write('subgraph disconnected {\n') + f.write('label = "Disconnected"\n') + f.write('bgcolor = red\n') + + for dist in disconnected: + f.write('"%s"' % dist.name) + f.write('\n') f.write('}\n') + f.write('}\n') def generate_graph(dists): - """ - Generates a dependency graph from the given distributions. + """Generates a dependency graph from the given distributions. :parameter dists: a list of distributions :type dists: list of :class:`pkgutil.Distribution` and @@ -115,7 +117,7 @@ provides = dist.metadata['Provides-Dist'] + dist.metadata['Provides'] for p in provides: - comps = p.split(" ", 1) + comps = p.strip().rsplit(" ", 1) name = comps[0] version = None if len(comps) == 2: @@ -133,22 +135,26 @@ requires = dist.metadata['Requires-Dist'] + dist.metadata['Requires'] for req in requires: predicate = VersionPredicate(req) - comps = req.split(" ", 1) + comps = req.strip().rsplit(" ", 1) name = comps[0] if not name in provided: graph.add_missing(dist, req) else: + matched = False for (version, provider) in provided[name]: if predicate.match(version): graph.add_edge(dist, provider, req) + matched = True + break + if not matched: + graph.add_missing(dist, req) return graph def dependent_dists(dists, dist): - """ - Recursively generate a list of distributions from *dists* that are + """Recursively generate a list of distributions from *dists* that are dependent on *dist*. :param dists: a list of distributions @@ -158,24 +164,26 @@ raise ValueError('The given distribution is not a member of the list') graph = generate_graph(dists) - dep = [dist] - fringe = [dist] # list of nodes we should expand + dep = [dist] # dependent distributions + fringe = graph.reverse_list[dist] # list of nodes we should inspect + while not len(fringe) == 0: - next = graph.adjacency_list[fringe.pop()] - for (dist, label) in next: - if not dist in dep: # avoid infinite loops - dep.append(dist) - fringe.append(dist) + node = fringe.pop() + dep.append(node) + for prev in graph.reverse_list[node]: + if not prev in dep: + fringe.append(prev) - dep.pop() + dep.pop(0) # remove dist from dep, was there to prevent infinite loops return dep if __name__ == '__main__': - dists = list(pkgutil.get_distributions(use_egg_info=True)) + from distutils2._backport.pkgutil import get_distributions + dists = list(get_distributions(use_egg_info=True)) graph = generate_graph(dists) for dist, reqs in graph.missing.iteritems(): if len(reqs) > 0: print("Missing dependencies for %s: %s" % (dist.name, ", ".join(reqs))) f = open('output.dot', 'w') - graph.to_dot(f, True) + graph_to_dot(graph, f, True) diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -13,8 +13,10 @@ except ImportError: warnings = None +from ConfigParser import RawConfigParser + from distutils2.errors import (DistutilsOptionError, DistutilsArgError, - DistutilsModuleError, DistutilsClassError) + DistutilsModuleError, DistutilsClassError) from distutils2.fancy_getopt import FancyGetopt, translate_longopt from distutils2.util import check_environ, strtobool from distutils2 import log @@ -112,7 +114,11 @@ ('tests-require', None, "print the list of packages/modules required to run the test suite"), ('obsoletes', None, - "print the list of packages/modules made obsolete") + "print the list of packages/modules made obsolete"), + ('use-2to3', None, + "use 2to3 to make source python 3.x compatible"), + ('convert-2to3-doctests', None, + "use 2to3 to convert doctests in seperate text files"), ] display_option_names = map(lambda x: translate_longopt(x[0]), display_options) @@ -206,6 +212,8 @@ self.scripts = None self.data_files = None self.password = '' + self.use_2to3 = False + self.convert_2to3_doctests = [] # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to @@ -250,7 +258,7 @@ elif hasattr(self, key): setattr(self, key, val) else: - msg = "Unknown distribution option: %s" % repr(key) + msg = "Unknown distribution option: %r" % key if warnings is not None: warnings.warn(msg) else: @@ -364,14 +372,12 @@ return files def parse_config_files(self, filenames=None): - from ConfigParser import ConfigParser - if filenames is None: filenames = self.find_config_files() log.debug("Distribution.parse_config_files():") - parser = ConfigParser() + parser = RawConfigParser() for filename in filenames: log.debug(" reading %s" % filename) parser.read(filename) @@ -385,7 +391,7 @@ opt = opt.replace('-', '_') opt_dict[opt] = (filename, val) - # Make the ConfigParser forget everything (so we retain + # Make the RawConfigParser forget everything (so we retain # the original filenames that options come from) parser.__init__() @@ -583,15 +589,11 @@ instance, analogous to the .finalize_options() method of Command objects. """ - - # XXX conversion -- removed - #for attr in ('keywords', 'platforms'): - # value = self.metadata.get_field(attr) - # if value is None: - # continue - # if isinstance(value, str): - # value = [elm.strip() for elm in value.split(',')] - # setattr(self.metadata, attr, value) + if getattr(self, 'convert_2to3_doctests', None): + self.convert_2to3_doctests = [os.path.join(p) + for p in self.convert_2to3_doctests] + else: + self.convert_2to3_doctests = [] def _show_help(self, parser, global_options=1, display_options=1, commands=[]): @@ -629,16 +631,16 @@ for command in self.commands: if isinstance(command, type) and issubclass(command, Command): - klass = command + cls = command else: - klass = self.get_command_class(command) - if (hasattr(klass, 'help_options') and - isinstance(klass.help_options, list)): - parser.set_option_table(klass.user_options + - fix_help_options(klass.help_options)) + cls = self.get_command_class(command) + if (hasattr(cls, 'help_options') and + isinstance(cls.help_options, list)): + parser.set_option_table(cls.user_options + + fix_help_options(cls.help_options)) else: - parser.set_option_table(klass.user_options) - parser.print_help("Options for '%s' command:" % klass.__name__) + parser.set_option_table(cls.user_options) + parser.print_help("Options for '%s' command:" % cls.__name__) print('') print(gen_usage(self.script_name)) @@ -690,11 +692,11 @@ print(header + ":") for cmd in commands: - klass = self.cmdclass.get(cmd) - if not klass: - klass = self.get_command_class(cmd) + cls = self.cmdclass.get(cmd) + if not cls: + cls = self.get_command_class(cmd) try: - description = klass.description + description = cls.description except AttributeError: description = "(no description available)" @@ -756,11 +758,11 @@ rv = [] for cmd in (std_commands + extra_commands): - klass = self.cmdclass.get(cmd) - if not klass: - klass = self.get_command_class(cmd) + cls = self.cmdclass.get(cmd) + if not cls: + cls = self.get_command_class(cmd) try: - description = klass.description + description = cls.description except AttributeError: description = "(no description available)" rv.append((cmd, description)) @@ -792,13 +794,13 @@ Raises DistutilsModuleError if the expected module could not be found, or if that module does not define the expected class. """ - klass = self.cmdclass.get(command) - if klass: - return klass + cls = self.cmdclass.get(command) + if cls: + return cls for pkgname in self.get_command_packages(): module_name = "%s.%s" % (pkgname, command) - klass_name = command + class_name = command try: __import__ (module_name) @@ -807,14 +809,14 @@ continue try: - klass = getattr(module, klass_name) + cls = getattr(module, class_name) except AttributeError: raise DistutilsModuleError, \ "invalid command '%s' (no class '%s' in module '%s')" \ - % (command, klass_name, module_name) + % (command, class_name, module_name) - self.cmdclass[command] = klass - return klass + self.cmdclass[command] = cls + return cls raise DistutilsModuleError("invalid command '%s'" % command) @@ -830,8 +832,8 @@ log.debug("Distribution.get_command_obj(): " \ "creating '%s' command object" % command) - klass = self.get_command_class(command) - cmd_obj = self.command_obj[command] = klass(self) + cls = self.get_command_class(command) + cmd_obj = self.command_obj[command] = cls(self) self.have_run[command] = 0 # Set any options that were supplied in config files @@ -887,7 +889,7 @@ except ValueError, msg: raise DistutilsOptionError, msg - def reinitialize_command(self, command, reinit_subcommands=0): + def get_reinitialized_command(self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first returned by 'get_command_obj()': ie., initialized but not yet finalized. This provides the opportunity to sneak option @@ -922,7 +924,7 @@ if reinit_subcommands: for sub in command.get_sub_commands(): - self.reinitialize_command(sub, reinit_subcommands) + self.get_reinitialized_command(sub, reinit_subcommands) return command diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py --- a/src/distutils2/extension.py +++ b/src/distutils2/extension.py @@ -5,14 +5,8 @@ __revision__ = "$Id: extension.py 77704 2010-01-23 09:23:15Z tarek.ziade $" -import os import warnings -try: - import sysconfig -except ImportError: - from distutils2._backport import sysconfig - # This class is really only used by the "build_ext" command, so it might # make sense to put it in distutils.command.build_ext. However, that # module is already big enough, and I want to make this class a bit more @@ -23,7 +17,7 @@ # import that large-ish module (indirectly, through distutils.core) in # order to do anything. -class Extension: +class Extension(object): """Just a collection of attributes that describes an extension module and everything needed to build it (hopefully in a portable way, but there are hooks that let you be as unportable as you need). diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py --- a/src/distutils2/fancy_getopt.py +++ b/src/distutils2/fancy_getopt.py @@ -30,7 +30,7 @@ # (for use as attributes of some object). longopt_xlate = string.maketrans('-', '_') -class FancyGetopt: +class FancyGetopt(object): """Wrapper around the standard 'getopt()' module that provides some handy extra functionality: * short and long options are tied together @@ -473,7 +473,7 @@ return string.translate(opt, longopt_xlate) -class OptionDummy: +class OptionDummy(object): """Dummy class just used as a place to hold command-line option values as instance attributes.""" diff --git a/src/distutils2/log.py b/src/distutils2/log.py --- a/src/distutils2/log.py +++ b/src/distutils2/log.py @@ -11,14 +11,14 @@ import sys -class Log: +class Log(object): def __init__(self, threshold=WARN): self.threshold = threshold def _log(self, level, msg, args): if level not in (DEBUG, INFO, WARN, ERROR, FATAL): - raise ValueError('%s wrong log level' % str(level)) + raise ValueError('%s wrong log level' % level) if level >= self.threshold: if args: diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -105,7 +105,6 @@ keys = fields.keys() possible_versions = ['1.0', '1.1', '1.2'] - # first let's try to see if a field is not part of one of the version for key in keys: if key not in _241_FIELDS and '1.0' in possible_versions: @@ -128,9 +127,9 @@ raise MetadataConflictError('You used incompatible 1.1 and 1.2 fields') # we have the choice, either 1.0, or 1.2 - # - 1.0 has a broken Summary field but work with all tools + # - 1.0 has a broken Summary field but works with all tools # - 1.1 is to avoid - # - 1.2 fixes Summary but is not spreaded yet + # - 1.2 fixes Summary but is not widespread yet if not is_1_1 and not is_1_2: # we couldn't find any specific marker if PKG_INFO_PREFERRED_VERSION in possible_versions: @@ -185,14 +184,16 @@ class DistributionMetadata(object): """Distribution meta-data class (1.0 or 1.2). """ - def __init__(self, path=None, platform_dependant=False, - execution_context=None): + def __init__(self, path=None, platform_dependent=False, + execution_context=None, fileobj=None): self._fields = {} self.version = None self.docutils_support = _HAS_DOCUTILS - self.platform_dependant = platform_dependant + self.platform_dependent = platform_dependent if path is not None: self.read(path) + elif fileobj is not None: + self.read_file(fileobj) self.execution_context = execution_context def _set_best_version(self): @@ -261,7 +262,7 @@ return reporter.messages def _platform(self, value): - if not self.platform_dependant or ';' not in value: + if not self.platform_dependent or ';' not in value: return True, value value, marker = value.split(';') return _interpret(marker, self.execution_context), value @@ -516,7 +517,7 @@ raise NameError(value) def _nonsense_op(self): - msg = 'This operation is not supported : "%s"' % str(self) + msg = 'This operation is not supported : "%s"' % self raise SyntaxError(msg) def __call__(self): @@ -633,4 +634,3 @@ operations = _CHAIN(execution_context) tokenize(StringIO(marker).readline, operations.eat) return operations.result() - diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py --- a/src/distutils2/mkpkg.py +++ b/src/distutils2/mkpkg.py @@ -675,7 +675,7 @@ troveDict = buildTroveDict(troveList) -class SetupClass: +class SetupClass(object): def __init__(self): self.config = None self.classifierDict = {} @@ -717,14 +717,16 @@ def inspectFile(self, path): fp = open(path, 'r') - for line in [ fp.readline() for x in range(10) ]: - m = re.match(r'^#!.*python((?P\d)(\.\d+)?)?$', line) - if m: - if m.group('major') == '3': - self.classifierDict['Programming Language :: Python :: 3'] = 1 - else: - self.classifierDict['Programming Language :: Python :: 2'] = 1 - fp.close() + try: + for line in [ fp.readline() for x in range(10) ]: + m = re.match(r'^#!.*python((?P\d)(\.\d+)?)?$', line) + if m: + if m.group('major') == '3': + self.classifierDict['Programming Language :: Python :: 3'] = 1 + else: + self.classifierDict['Programming Language :: Python :: 2'] = 1 + finally: + fp.close() def inspectDirectory(self): @@ -885,38 +887,33 @@ if os.path.exists('setup.py'): shutil.move('setup.py', 'setup.py.old') fp = open('setup.py', 'w') - fp.write('#!/usr/bin/env python\n\n') - fp.write('from distutils2.core import setup\n\n') + try: + fp.write('#!/usr/bin/env python\n\n') + fp.write('from distutils2.core import setup\n\n') + fp.write('setup(name=%s,\n' % repr(self.setupData['name'])) + fp.write(' version=%s,\n' % repr(self.setupData['version'])) + fp.write(' description=%s,\n' + % repr(self.setupData['description'])) + fp.write(' author=%s,\n' % repr(self.setupData['author'])) + fp.write(' author_email=%s,\n' + % repr(self.setupData['author_email'])) + if self.setupData['url']: + fp.write(' url=%s,\n' % repr(self.setupData['url'])) + if self.setupData['classifier']: + fp.write(' classifier=[\n') + for classifier in sorted(self.setupData['classifier'].keys()): + fp.write(' %s,\n' % repr(classifier)) + fp.write(' ],\n') + if self.setupData['packages']: + fp.write(' packages=%s,\n' + % repr(self._dotted_packages(self.setupData['packages']))) + fp.write(' package_dir=%s,\n' + % repr(self.setupData['packages'])) + fp.write(' #scripts=[\'path/to/script\']\n') - fp.write('from sys import version\n') - fp.write('if version < \'2.2.3\':\n') - fp.write(' from distutils2.dist import DistributionMetadata\n') - fp.write(' DistributionMetadata.classifier = None\n') - fp.write(' DistributionMetadata.download_url = None\n') - - fp.write('setup(name = %s,\n' % repr(self.setupData['name'])) - fp.write(' version = %s,\n' % repr(self.setupData['version'])) - fp.write(' description = %s,\n' - % repr(self.setupData['description'])) - fp.write(' author = %s,\n' % repr(self.setupData['author'])) - fp.write(' author_email = %s,\n' - % repr(self.setupData['author_email'])) - if self.setupData['url']: - fp.write(' url = %s,\n' % repr(self.setupData['url'])) - if self.setupData['classifier']: - fp.write(' classifier = [\n') - for classifier in sorted(self.setupData['classifier'].keys()): - fp.write(' %s,\n' % repr(classifier)) - fp.write(' ],\n') - if self.setupData['packages']: - fp.write(' packages = %s,\n' - % repr(self._dotted_packages(self.setupData['packages']))) - fp.write(' package_dir = %s,\n' - % repr(self.setupData['packages'])) - fp.write(' #scripts = [\'path/to/script\']\n') - - fp.write(' )\n') - fp.close() + fp.write(' )\n') + finally: + fp.close() os.chmod('setup.py', 0755) print 'Wrote "setup.py".' diff --git a/src/distutils2/pypi/__init__.py b/src/distutils2/pypi/__init__.py --- a/src/distutils2/pypi/__init__.py +++ b/src/distutils2/pypi/__init__.py @@ -3,5 +3,6 @@ Package containing ways to interact with the PyPI APIs. """ -__all__ = ['package_index', +__all__ = ['simple', + 'dist', ] diff --git a/src/distutils2/pypi/dist.py b/src/distutils2/pypi/dist.py new file mode 100644 --- /dev/null +++ b/src/distutils2/pypi/dist.py @@ -0,0 +1,315 @@ +"""distutils2.pypi.dist + +Provides the PyPIDistribution class thats represents a distribution retrieved +on PyPI. +""" +import re +import urlparse +import urllib +import tempfile +from operator import attrgetter + +try: + import hashlib +except ImportError: + from distutils2._backport import hashlib + +from distutils2.version import suggest_normalized_version, NormalizedVersion +from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName + +EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz .egg".split() +MD5_HASH = re.compile(r'^.*#md5=([a-f0-9]+)$') + + +class PyPIDistribution(object): + """Represents a distribution retrieved from PyPI. + + This is a simple container for various attributes as name, version, + downloaded_location, url etc. + + The PyPIDistribution class is used by the pypi.*Index class to return + information about distributions. + """ + + @classmethod + def from_url(cls, url, probable_dist_name=None, is_external=True): + """Build a Distribution from a url archive (egg or zip or tgz). + + :param url: complete url of the distribution + :param probable_dist_name: A probable name of the distribution. + :param is_external: Tell if the url commes from an index or from + an external URL. + """ + # if the url contains a md5 hash, get it. + md5_hash = None + match = MD5_HASH.match(url) + if match is not None: + md5_hash = match.group(1) + # remove the hash + url = url.replace("#md5=%s" % md5_hash, "") + + # parse the archive name to find dist name and version + archive_name = urlparse.urlparse(url)[2].split('/')[-1] + extension_matched = False + # remove the extension from the name + for ext in EXTENSIONS: + if archive_name.endswith(ext): + archive_name = archive_name[:-len(ext)] + extension_matched = True + + name, version = split_archive_name(archive_name) + if extension_matched is True: + return PyPIDistribution(name, version, url=url, url_hashname="md5", + url_hashval=md5_hash, + url_is_external=is_external) + + def __init__(self, name, version, type=None, url=None, url_hashname=None, + url_hashval=None, url_is_external=True): + """Create a new instance of PyPIDistribution. + + :param name: the name of the distribution + :param version: the version of the distribution + :param type: the type of the dist (eg. source, bin-*, etc.) + :param url: URL where we found this distribution + :param url_hashname: the name of the hash we want to use. Refer to the + hashlib.new documentation for more information. + :param url_hashval: the hash value. + :param url_is_external: we need to know if the provided url comes from an + index browsing, or from an external resource. + + """ + self.name = name + self.version = NormalizedVersion(version) + self.type = type + # set the downloaded path to None by default. The goal here + # is to not download distributions multiple times + self.downloaded_location = None + # We store urls in dict, because we need to have a bit more informations + # than the simple URL. It will be used later to find the good url to + # use. + # We have two _url* attributes: _url and _urls. _urls contains a list of + # dict for the different urls, and _url contains the choosen url, in + # order to dont make the selection process multiple times. + self._urls = [] + self._url = None + self.add_url(url, url_hashname, url_hashval, url_is_external) + + def add_url(self, url, hashname=None, hashval=None, is_external=True): + """Add a new url to the list of urls""" + if hashname is not None: + try: + hashlib.new(hashname) + except ValueError: + raise UnsupportedHashName(hashname) + + self._urls.append({ + 'url': url, + 'hashname': hashname, + 'hashval': hashval, + 'is_external': is_external, + }) + # reset the url selection process + self._url = None + + @property + def url(self): + """Pick up the right url for the list of urls in self.urls""" + # We return internal urls over externals. + # If there is more than one internal or external, return the first + # one. + if self._url is None: + if len(self._urls) > 1: + internals_urls = [u for u in self._urls \ + if u['is_external'] == False] + if len(internals_urls) >= 1: + self._url = internals_urls[0] + if self._url is None: + self._url = self._urls[0] + return self._url + + @property + def is_source(self): + """return if the distribution is a source one or not""" + return self.type == 'source' + + @property + def is_final(self): + """proxy to version.is_final""" + return self.version.is_final + + def download(self, path=None): + """Download the distribution to a path, and return it. + + If the path is given in path, use this, otherwise, generates a new one + """ + if path is None: + path = tempfile.mkdtemp() + + # if we do not have downloaded it yet, do it. + if self.downloaded_location is None: + url = self.url['url'] + archive_name = urlparse.urlparse(url)[2].split('/')[-1] + filename, headers = urllib.urlretrieve(url, + path + "/" + archive_name) + self.downloaded_location = filename + self._check_md5(filename) + return self.downloaded_location + + def _check_md5(self, filename): + """Check that the md5 checksum of the given file matches the one in + url param""" + hashname = self.url['hashname'] + expected_hashval = self.url['hashval'] + if not None in (expected_hashval, hashname): + f = open(filename) + hashval = hashlib.new(hashname) + hashval.update(f.read()) + if hashval.hexdigest() != expected_hashval: + raise HashDoesNotMatch("got %s instead of %s" + % (hashval.hexdigest(), expected_hashval)) + + def __repr__(self): + return "%s %s %s %s" \ + % (self.__class__.__name__, self.name, self.version, + self.type or "") + + def _check_is_comparable(self, other): + if not isinstance(other, PyPIDistribution): + raise TypeError("cannot compare %s and %s" + % (type(self).__name__, type(other).__name__)) + elif self.name != other.name: + raise TypeError("cannot compare %s and %s" + % (self.name, other.name)) + + def __eq__(self, other): + self._check_is_comparable(other) + return self.version == other.version + + def __lt__(self, other): + self._check_is_comparable(other) + return self.version < other.version + + def __ne__(self, other): + return not self.__eq__(other) + + def __gt__(self, other): + return not (self.__lt__(other) or self.__eq__(other)) + + def __le__(self, other): + return self.__eq__(other) or self.__lt__(other) + + def __ge__(self, other): + return self.__eq__(other) or self.__gt__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + + +class PyPIDistributions(list): + """A container of PyPIDistribution objects. + + Contains methods and facilities to sort and filter distributions. + """ + def __init__(self, list=[]): + # To disable the ability to pass lists on instanciation + super(PyPIDistributions, self).__init__() + for item in list: + self.append(item) + + def filter(self, predicate): + """Filter the distributions and return a subset of distributions that + match the given predicate + """ + return PyPIDistributions( + [dist for dist in self if dist.name == predicate.name and + predicate.match(dist.version)]) + + def get_last(self, predicate, prefer_source=None, prefer_final=None): + """Return the most up to date version, that satisfy the given + predicate + """ + distributions = self.filter(predicate) + distributions.sort_distributions(prefer_source, prefer_final, reverse=True) + return distributions[0] + + def get_same_name_and_version(self): + """Return lists of PyPIDistribution objects that refer to the same + name and version number. This do not consider the type (source, binary, + etc.)""" + processed = [] + duplicates = [] + for dist in self: + if (dist.name, dist.version) not in processed: + processed.append((dist.name, dist.version)) + found_duplicates = [d for d in self if d.name == dist.name and + d.version == dist.version] + if len(found_duplicates) > 1: + duplicates.append(found_duplicates) + return duplicates + + def append(self, o): + """Append a new distribution to the list. + + If a distribution with the same name and version exists, just grab the + URL informations and add a new new url for the existing one. + """ + similar_dists = [d for d in self if d.name == o.name and + d.version == o.version and d.type == o.type] + if len(similar_dists) > 0: + dist = similar_dists[0] + dist.add_url(**o.url) + else: + super(PyPIDistributions, self).append(o) + + def sort_distributions(self, prefer_source=True, prefer_final=False, + reverse=True, *args, **kwargs): + """order the results with the given properties""" + + sort_by = [] + if prefer_final: + sort_by.append("is_final") + sort_by.append("version") + + if prefer_source: + sort_by.append("is_source") + + super(PyPIDistributions, self).sort( + key=lambda i: [getattr(i, arg) for arg in sort_by], + reverse=reverse, *args, **kwargs) + + +def split_archive_name(archive_name, probable_name=None): + """Split an archive name into two parts: name and version. + + Return the tuple (name, version) + """ + # Try to determine wich part is the name and wich is the version using the + # "-" separator. Take the larger part to be the version number then reduce + # if this not works. + def eager_split(str, maxsplit=2): + # split using the "-" separator + splits = str.rsplit("-", maxsplit) + name = splits[0] + version = "-".join(splits[1:]) + if version.startswith("-"): + version = version[1:] + if suggest_normalized_version(version) is None and maxsplit >= 0: + # we dont get a good version number: recurse ! + return eager_split(str, maxsplit - 1) + else: + return (name, version) + if probable_name is not None: + probable_name = probable_name.lower() + name = None + if probable_name is not None and probable_name in archive_name: + # we get the name from probable_name, if given. + name = probable_name + version = archive_name.lstrip(name) + else: + name, version = eager_split(archive_name) + + version = suggest_normalized_version(version) + if version != "" and name != "": + return (name.lower(), version) + else: + raise CantParseArchiveName(archive_name) diff --git a/src/distutils2/pypi/errors.py b/src/distutils2/pypi/errors.py new file mode 100644 --- /dev/null +++ b/src/distutils2/pypi/errors.py @@ -0,0 +1,33 @@ +"""distutils2.pypi.errors + +All errors and exceptions raised by PyPiIndex classes. +""" +from distutils2.errors import DistutilsError + + +class PyPIError(DistutilsError): + """The base class for errors of the pypi python package.""" + + +class DistributionNotFound(PyPIError): + """No distribution match the given requirements.""" + + +class CantParseArchiveName(PyPIError): + """An archive name can't be parsed to find distribution name and version""" + + +class DownloadError(PyPIError): + """An error has occurs while downloading""" + + +class HashDoesNotMatch(DownloadError): + """Compared hashes does not match""" + + +class UnsupportedHashName(PyPIError): + """A unsupported hashname has been used""" + + +class UnableToDownload(PyPIError): + """All mirrors have been tried, without success""" diff --git a/src/distutils2/pypi/pkg_resources.py b/src/distutils2/pypi/pkg_resources.py deleted file mode 100644 --- a/src/distutils2/pypi/pkg_resources.py +++ /dev/null @@ -1,2693 +0,0 @@ -"""Package resource API --------------------- - -A resource is a logical file contained within a package, or a logical -subdirectory thereof. The package resource API expects resource names -to have their path parts separated with ``/``, *not* whatever the local -path separator is. Do not use os.path operations to manipulate resource -names being passed into the API. - -The package resource API is designed to work with normal filesystem packages, -.egg files, and unpacked .egg files. It can also work in a limited way with -.zip files and with custom PEP 302 loaders that support the ``get_data()`` -method. -""" - -import sys, os, zipimport, time, re, imp, types -from urlparse import urlparse, urlunparse - -try: - frozenset -except NameError: - from sets import ImmutableSet as frozenset - -# capture these to bypass sandboxing -from os import utime -try: - from os import mkdir, rename, unlink - WRITE_SUPPORT = True -except ImportError: - # no write support, probably under GAE - WRITE_SUPPORT = False - -from os import open as os_open -from os.path import isdir, split - -# This marker is used to simplify the process that checks is the -# setuptools package was installed by the Setuptools project -# or by the Distribute project, in case Setuptools creates -# a distribution with the same version. -# -# The bootstrapping script for instance, will check if this -# attribute is present to decide wether to reinstall the package -_distribute = True - -def _bypass_ensure_directory(name, mode=0777): - # Sandbox-bypassing version of ensure_directory() - if not WRITE_SUPPORT: - raise IOError('"os.mkdir" not supported on this platform.') - dirname, filename = split(name) - if dirname and filename and not isdir(dirname): - _bypass_ensure_directory(dirname) - mkdir(dirname, mode) - - - - - - - - -def get_supported_platform(): - """Return this platform's maximum compatible version. - - distutils.util.get_platform() normally reports the minimum version - of Mac OS X that would be required to *use* extensions produced by - distutils. But what we want when checking compatibility is to know the - version of Mac OS X that we are *running*. To allow usage of packages that - explicitly require a newer version of Mac OS X, we must also know the - current version of the OS. - - If this condition occurs for any other platform with a version in its - platform strings, this function should be extended accordingly. - """ - plat = get_build_platform(); m = macosVersionString.match(plat) - if m is not None and sys.platform == "darwin": - try: - plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) - except ValueError: - pass # not Mac OS X - return plat - - - - - - - - - - - - - - - - - - - - - -__all__ = [ - # Basic resource access and distribution/entry point discovery - 'require', 'run_script', 'get_provider', 'get_distribution', - 'load_entry_point', 'get_entry_map', 'get_entry_info', 'iter_entry_points', - 'resource_string', 'resource_stream', 'resource_filename', - 'resource_listdir', 'resource_exists', 'resource_isdir', - - # Environmental control - 'declare_namespace', 'working_set', 'add_activation_listener', - 'find_distributions', 'set_extraction_path', 'cleanup_resources', - 'get_default_cache', - - # Primary implementation classes - 'Environment', 'WorkingSet', 'ResourceManager', - 'Distribution', 'Requirement', 'EntryPoint', - - # Exceptions - 'ResolutionError','VersionConflict','DistributionNotFound','UnknownExtra', - 'ExtractionError', - - # Parsing functions and string utilities - 'parse_requirements', 'parse_version', 'safe_name', 'safe_version', - 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', - 'safe_extra', 'to_filename', - - # filesystem utilities - 'ensure_directory', 'normalize_path', - - # Distribution "precedence" constants - 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST', - - # "Provider" interfaces, implementations, and registration/lookup APIs - 'IMetadataProvider', 'IResourceProvider', 'FileMetadata', - 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider', - 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider', - 'register_finder', 'register_namespace_handler', 'register_loader_type', - 'fixup_namespace_packages', 'get_importer', - - # Deprecated/backward compatibility only - 'run_main', 'AvailableDistributions', -] -class ResolutionError(Exception): - """Abstract base for dependency resolution errors""" - def __repr__(self): - return self.__class__.__name__+repr(self.args) - -class VersionConflict(ResolutionError): - """An already-installed version conflicts with the requested version""" - -class DistributionNotFound(ResolutionError): - """A requested distribution was not found""" - -class UnknownExtra(ResolutionError): - """Distribution doesn't have an "extra feature" of the given name""" -_provider_factories = {} - -PY_MAJOR = sys.version[:3] -EGG_DIST = 3 -BINARY_DIST = 2 -SOURCE_DIST = 1 -CHECKOUT_DIST = 0 -DEVELOP_DIST = -1 - -def register_loader_type(loader_type, provider_factory): - """Register `provider_factory` to make providers for `loader_type` - - `loader_type` is the type or class of a PEP 302 ``module.__loader__``, - and `provider_factory` is a function that, passed a *module* object, - returns an ``IResourceProvider`` for that module. - """ - _provider_factories[loader_type] = provider_factory - -def get_provider(moduleOrReq): - """Return an IResourceProvider for the named module or requirement""" - if isinstance(moduleOrReq,Requirement): - return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0] - try: - module = sys.modules[moduleOrReq] - except KeyError: - __import__(moduleOrReq) - module = sys.modules[moduleOrReq] - loader = getattr(module, '__loader__', None) - return _find_adapter(_provider_factories, loader)(module) - -def _macosx_vers(_cache=[]): - if not _cache: - import platform - version = platform.mac_ver()[0] - # fallback for MacPorts - if version == '': - import plistlib - plist = '/System/Library/CoreServices/SystemVersion.plist' - if os.path.exists(plist): - if hasattr(plistlib, 'readPlist'): - plist_content = plistlib.readPlist(plist) - if 'ProductVersion' in plist_content: - version = plist_content['ProductVersion'] - - _cache.append(version.split('.')) - return _cache[0] - -def _macosx_arch(machine): - return {'PowerPC':'ppc', 'Power_Macintosh':'ppc'}.get(machine,machine) - -def get_build_platform(): - """Return this platform's string for platform-specific distributions - - XXX Currently this is the same as ``distutils.util.get_platform()``, but it - needs some hacks for Linux and Mac OS X. - """ - try: - from distutils.util import get_platform - except ImportError: - from sysconfig import get_platform - - plat = get_platform() - if sys.platform == "darwin" and not plat.startswith('macosx-'): - try: - version = _macosx_vers() - machine = os.uname()[4].replace(" ", "_") - return "macosx-%d.%d-%s" % (int(version[0]), int(version[1]), - _macosx_arch(machine)) - except ValueError: - # if someone is running a non-Mac darwin system, this will fall - # through to the default implementation - pass - return plat - -macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") -darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") -get_platform = get_build_platform # XXX backward compat - -def compatible_platforms(provided,required): - """Can code for the `provided` platform run on the `required` platform? - - Returns true if either platform is ``None``, or the platforms are equal. - - XXX Needs compatibility checks for Linux and other unixy OSes. - """ - if provided is None or required is None or provided==required: - return True # easy case - - # Mac OS X special cases - reqMac = macosVersionString.match(required) - if reqMac: - provMac = macosVersionString.match(provided) - - # is this a Mac package? - if not provMac: - # this is backwards compatibility for packages built before - # setuptools 0.6. All packages built after this point will - # use the new macosx designation. - provDarwin = darwinVersionString.match(provided) - if provDarwin: - dversion = int(provDarwin.group(1)) - macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) - if dversion == 7 and macosversion >= "10.3" or \ - dversion == 8 and macosversion >= "10.4": - - #import warnings - #warnings.warn("Mac eggs should be rebuilt to " - # "use the macosx designation instead of darwin.", - # category=DeprecationWarning) - return True - return False # egg isn't macosx or legacy darwin - - # are they the same major version and machine type? - if provMac.group(1) != reqMac.group(1) or \ - provMac.group(3) != reqMac.group(3): - return False - - - - # is the required OS major update >= the provided one? - if int(provMac.group(2)) > int(reqMac.group(2)): - return False - - return True - - # XXX Linux and other platforms' special cases should go here - return False - - -def run_script(dist_spec, script_name): - """Locate distribution `dist_spec` and run its `script_name` script""" - ns = sys._getframe(1).f_globals - name = ns['__name__'] - ns.clear() - ns['__name__'] = name - require(dist_spec)[0].run_script(script_name, ns) - -run_main = run_script # backward compatibility - -def get_distribution(dist): - """Return a current distribution object for a Requirement or string""" - if isinstance(dist,basestring): dist = Requirement.parse(dist) - if isinstance(dist,Requirement): dist = get_provider(dist) - if not isinstance(dist,Distribution): - raise TypeError("Expected string, Requirement, or Distribution", dist) - return dist - -def load_entry_point(dist, group, name): - """Return `name` entry point of `group` for `dist` or raise ImportError""" - return get_distribution(dist).load_entry_point(group, name) - -def get_entry_map(dist, group=None): - """Return the entry point map for `group`, or the full entry map""" - return get_distribution(dist).get_entry_map(group) - -def get_entry_info(dist, group, name): - """Return the EntryPoint object for `group`+`name`, or ``None``""" - return get_distribution(dist).get_entry_info(group, name) - - -class IMetadataProvider: - - def has_metadata(name): - """Does the package's distribution contain the named metadata?""" - - def get_metadata(name): - """The named metadata resource as a string""" - - def get_metadata_lines(name): - """Yield named metadata resource as list of non-blank non-comment lines - - Leading and trailing whitespace is stripped from each line, and lines - with ``#`` as the first non-blank character are omitted.""" - - def metadata_isdir(name): - """Is the named metadata a directory? (like ``os.path.isdir()``)""" - - def metadata_listdir(name): - """List of metadata names in the directory (like ``os.listdir()``)""" - - def run_script(script_name, namespace): - """Execute the named script in the supplied namespace dictionary""" - - - - - - - - - - -class IResourceProvider(IMetadataProvider): - """An object that provides access to package resources""" - - def get_resource_filename(manager, resource_name): - """Return a true filesystem path for `resource_name` - - `manager` must be an ``IResourceManager``""" - - def get_resource_stream(manager, resource_name): - """Return a readable file-like object for `resource_name` - - `manager` must be an ``IResourceManager``""" - - def get_resource_string(manager, resource_name): - """Return a string containing the contents of `resource_name` - - `manager` must be an ``IResourceManager``""" - - def has_resource(resource_name): - """Does the package contain the named resource?""" - - def resource_isdir(resource_name): - """Is the named resource a directory? (like ``os.path.isdir()``)""" - - def resource_listdir(resource_name): - """List of resource names in the directory (like ``os.listdir()``)""" - - - - - - - - - - - - - - - -class WorkingSet(object): - """A collection of active distributions on sys.path (or a similar list)""" - - def __init__(self, entries=None): - """Create working set from list of path entries (default=sys.path)""" - self.entries = [] - self.entry_keys = {} - self.by_key = {} - self.callbacks = [] - - if entries is None: - entries = sys.path - - for entry in entries: - self.add_entry(entry) - - - def add_entry(self, entry): - """Add a path item to ``.entries``, finding any distributions on it - - ``find_distributions(entry,True)`` is used to find distributions - corresponding to the path entry, and they are added. `entry` is - always appended to ``.entries``, even if it is already present. - (This is because ``sys.path`` can contain the same value more than - once, and the ``.entries`` of the ``sys.path`` WorkingSet should always - equal ``sys.path``.) - """ - self.entry_keys.setdefault(entry, []) - self.entries.append(entry) - for dist in find_distributions(entry, True): - self.add(dist, entry, False) - - - def __contains__(self,dist): - """True if `dist` is the active distribution for its project""" - return self.by_key.get(dist.key) == dist - - - - - - def find(self, req): - """Find a distribution matching requirement `req` - - If there is an active distribution for the requested project, this - returns it as long as it meets the version requirement specified by - `req`. But, if there is an active distribution for the project and it - does *not* meet the `req` requirement, ``VersionConflict`` is raised. - If there is no active distribution for the requested project, ``None`` - is returned. - """ - dist = self.by_key.get(req.key) - if dist is not None and dist not in req: - raise VersionConflict(dist,req) # XXX add more info - else: - return dist - - def iter_entry_points(self, group, name=None): - """Yield entry point objects from `group` matching `name` - - If `name` is None, yields all entry points in `group` from all - distributions in the working set, otherwise only ones matching - both `group` and `name` are yielded (in distribution order). - """ - for dist in self: - entries = dist.get_entry_map(group) - if name is None: - for ep in entries.values(): - yield ep - elif name in entries: - yield entries[name] - - def run_script(self, requires, script_name): - """Locate distribution for `requires` and run `script_name` script""" - ns = sys._getframe(1).f_globals - name = ns['__name__'] - ns.clear() - ns['__name__'] = name - self.require(requires)[0].run_script(script_name, ns) - - - - def __iter__(self): - """Yield distributions for non-duplicate projects in the working set - - The yield order is the order in which the items' path entries were - added to the working set. - """ - seen = {} - for item in self.entries: - for key in self.entry_keys[item]: - if key not in seen: - seen[key]=1 - yield self.by_key[key] - - def add(self, dist, entry=None, insert=True): - """Add `dist` to working set, associated with `entry` - - If `entry` is unspecified, it defaults to the ``.location`` of `dist`. - On exit from this routine, `entry` is added to the end of the working - set's ``.entries`` (if it wasn't already present). - - `dist` is only added to the working set if it's for a project that - doesn't already have a distribution in the set. If it's added, any - callbacks registered with the ``subscribe()`` method will be called. - """ - if insert: - dist.insert_on(self.entries, entry) - - if entry is None: - entry = dist.location - keys = self.entry_keys.setdefault(entry,[]) - keys2 = self.entry_keys.setdefault(dist.location,[]) - if dist.key in self.by_key: - return # ignore hidden distros - - self.by_key[dist.key] = dist - if dist.key not in keys: - keys.append(dist.key) - if dist.key not in keys2: - keys2.append(dist.key) - self._added_new(dist) - - def resolve(self, requirements, env=None, installer=None, replacement=True): - """List all distributions needed to (recursively) meet `requirements` - - `requirements` must be a sequence of ``Requirement`` objects. `env`, - if supplied, should be an ``Environment`` instance. If - not supplied, it defaults to all distributions available within any - entry or distribution in the working set. `installer`, if supplied, - will be invoked with each requirement that cannot be met by an - already-installed distribution; it should return a ``Distribution`` or - ``None``. - """ - - requirements = list(requirements)[::-1] # set up the stack - processed = {} # set of processed requirements - best = {} # key -> dist - to_activate = [] - - while requirements: - req = requirements.pop(0) # process dependencies breadth-first - if _override_setuptools(req) and replacement: - req = Requirement.parse('distribute') - - if req in processed: - # Ignore cyclic or redundant dependencies - continue - dist = best.get(req.key) - if dist is None: - # Find the best distribution and add it to the map - dist = self.by_key.get(req.key) - if dist is None: - if env is None: - env = Environment(self.entries) - dist = best[req.key] = env.best_match(req, self, installer) - if dist is None: - #msg = ("The '%s' distribution was not found on this " - # "system, and is required by this application.") - #raise DistributionNotFound(msg % req) - - # unfortunately, zc.buildout uses a str(err) - # to get the name of the distribution here.. - raise DistributionNotFound(req) - to_activate.append(dist) - if dist not in req: - # Oops, the "best" so far conflicts with a dependency - raise VersionConflict(dist,req) # XXX put more info here - requirements.extend(dist.requires(req.extras)[::-1]) - processed[req] = True - - return to_activate # return list of distros to activate - - def find_plugins(self, - plugin_env, full_env=None, installer=None, fallback=True - ): - """Find all activatable distributions in `plugin_env` - - Example usage:: - - distributions, errors = working_set.find_plugins( - Environment(plugin_dirlist) - ) - map(working_set.add, distributions) # add plugins+libs to sys.path - print 'Could not load', errors # display errors - - The `plugin_env` should be an ``Environment`` instance that contains - only distributions that are in the project's "plugin directory" or - directories. The `full_env`, if supplied, should be an ``Environment`` - contains all currently-available distributions. If `full_env` is not - supplied, one is created automatically from the ``WorkingSet`` this - method is called on, which will typically mean that every directory on - ``sys.path`` will be scanned for distributions. - - `installer` is a standard installer callback as used by the - ``resolve()`` method. The `fallback` flag indicates whether we should - attempt to resolve older versions of a plugin if the newest version - cannot be resolved. - - This method returns a 2-tuple: (`distributions`, `error_info`), where - `distributions` is a list of the distributions found in `plugin_env` - that were loadable, along with any other distributions that are needed - to resolve their dependencies. `error_info` is a dictionary mapping - unloadable plugin distributions to an exception instance describing the - error that occurred. Usually this will be a ``DistributionNotFound`` or - ``VersionConflict`` instance. - """ - - plugin_projects = list(plugin_env) - plugin_projects.sort() # scan project names in alphabetic order - - error_info = {} - distributions = {} - - if full_env is None: - env = Environment(self.entries) - env += plugin_env - else: - env = full_env + plugin_env - - shadow_set = self.__class__([]) - map(shadow_set.add, self) # put all our entries in shadow_set - - for project_name in plugin_projects: - - for dist in plugin_env[project_name]: - - req = [dist.as_requirement()] - - try: - resolvees = shadow_set.resolve(req, env, installer) - - except ResolutionError,v: - error_info[dist] = v # save error info - if fallback: - continue # try the next older version of project - else: - break # give up on this project, keep going - - else: - map(shadow_set.add, resolvees) - distributions.update(dict.fromkeys(resolvees)) - - # success, no need to try any more versions of this project - break - - distributions = list(distributions) - distributions.sort() - - return distributions, error_info - - - - - - def require(self, *requirements): - """Ensure that distributions matching `requirements` are activated - - `requirements` must be a string or a (possibly-nested) sequence - thereof, specifying the distributions and versions required. The - return value is a sequence of the distributions that needed to be - activated to fulfill the requirements; all relevant distributions are - included, even if they were already activated in this working set. - """ - - needed = self.resolve(parse_requirements(requirements)) - - for dist in needed: - self.add(dist) - - return needed - - - def subscribe(self, callback): - """Invoke `callback` for all distributions (including existing ones)""" - if callback in self.callbacks: - return - self.callbacks.append(callback) - for dist in self: - callback(dist) - - - def _added_new(self, dist): - for callback in self.callbacks: - callback(dist) - - - - - - - - - - - -class Environment(object): - """Searchable snapshot of distributions on a search path""" - - def __init__(self, search_path=None, platform=get_supported_platform(), python=PY_MAJOR): - """Snapshot distributions available on a search path - - Any distributions found on `search_path` are added to the environment. - `search_path` should be a sequence of ``sys.path`` items. If not - supplied, ``sys.path`` is used. - - `platform` is an optional string specifying the name of the platform - that platform-specific distributions must be compatible with. If - unspecified, it defaults to the current platform. `python` is an - optional string naming the desired version of Python (e.g. ``'2.4'``); - it defaults to the current version. - - You may explicitly set `platform` (and/or `python`) to ``None`` if you - wish to map *all* distributions, not just those compatible with the - running platform or Python version. - """ - self._distmap = {} - self._cache = {} - self.platform = platform - self.python = python - self.scan(search_path) - - def can_add(self, dist): - """Is distribution `dist` acceptable for this environment? - - The distribution must match the platform and python version - requirements specified when this environment was created, or False - is returned. - """ - return (self.python is None or dist.py_version is None - or dist.py_version==self.python) \ - and compatible_platforms(dist.platform,self.platform) - - def remove(self, dist): - """Remove `dist` from the environment""" - self._distmap[dist.key].remove(dist) - - def scan(self, search_path=None): - """Scan `search_path` for distributions usable in this environment - - Any distributions found are added to the environment. - `search_path` should be a sequence of ``sys.path`` items. If not - supplied, ``sys.path`` is used. Only distributions conforming to - the platform/python version defined at initialization are added. - """ - if search_path is None: - search_path = sys.path - - for item in search_path: - for dist in find_distributions(item): - self.add(dist) - - def __getitem__(self,project_name): - """Return a newest-to-oldest list of distributions for `project_name` - """ - try: - return self._cache[project_name] - except KeyError: - project_name = project_name.lower() - if project_name not in self._distmap: - return [] - - if project_name not in self._cache: - dists = self._cache[project_name] = self._distmap[project_name] - _sort_dists(dists) - - return self._cache[project_name] - - def add(self,dist): - """Add `dist` if we ``can_add()`` it and it isn't already added""" - if self.can_add(dist) and dist.has_version(): - dists = self._distmap.setdefault(dist.key,[]) - if dist not in dists: - dists.append(dist) - if dist.key in self._cache: - _sort_dists(self._cache[dist.key]) - - - def best_match(self, req, working_set, installer=None): - """Find distribution best matching `req` and usable on `working_set` - - This calls the ``find(req)`` method of the `working_set` to see if a - suitable distribution is already active. (This may raise - ``VersionConflict`` if an unsuitable version of the project is already - active in the specified `working_set`.) If a suitable distribution - isn't active, this method returns the newest distribution in the - environment that meets the ``Requirement`` in `req`. If no suitable - distribution is found, and `installer` is supplied, then the result of - calling the environment's ``obtain(req, installer)`` method will be - returned. - """ - dist = working_set.find(req) - if dist is not None: - return dist - for dist in self[req.key]: - if dist in req: - return dist - return self.obtain(req, installer) # try and download/install - - def obtain(self, requirement, installer=None): - """Obtain a distribution matching `requirement` (e.g. via download) - - Obtain a distro that matches requirement (e.g. via download). In the - base ``Environment`` class, this routine just returns - ``installer(requirement)``, unless `installer` is None, in which case - None is returned instead. This method is a hook that allows subclasses - to attempt other ways of obtaining a distribution before falling back - to the `installer` argument.""" - if installer is not None: - return installer(requirement) - - def __iter__(self): - """Yield the unique project names of the available distributions""" - for key in self._distmap.keys(): - if self[key]: yield key - - - - - def __iadd__(self, other): - """In-place addition of a distribution or environment""" - if isinstance(other,Distribution): - self.add(other) - elif isinstance(other,Environment): - for project in other: - for dist in other[project]: - self.add(dist) - else: - raise TypeError("Can't add %r to environment" % (other,)) - return self - - def __add__(self, other): - """Add an environment or distribution to an environment""" - new = self.__class__([], platform=None, python=None) - for env in self, other: - new += env - return new - - -AvailableDistributions = Environment # XXX backward compatibility - - -class ExtractionError(RuntimeError): - """An error occurred extracting a resource - - The following attributes are available from instances of this exception: - - manager - The resource manager that raised this exception - - cache_path - The base directory for resource extraction - - original_error - The exception instance that caused extraction to fail - """ - - - - -class ResourceManager: - """Manage resource extraction and packages""" - extraction_path = None - - def __init__(self): - self.cached_files = {} - - def resource_exists(self, package_or_requirement, resource_name): - """Does the named resource exist?""" - return get_provider(package_or_requirement).has_resource(resource_name) - - def resource_isdir(self, package_or_requirement, resource_name): - """Is the named resource an existing directory?""" - return get_provider(package_or_requirement).resource_isdir( - resource_name - ) - - def resource_filename(self, package_or_requirement, resource_name): - """Return a true filesystem path for specified resource""" - return get_provider(package_or_requirement).get_resource_filename( - self, resource_name - ) - - def resource_stream(self, package_or_requirement, resource_name): - """Return a readable file-like object for specified resource""" - return get_provider(package_or_requirement).get_resource_stream( - self, resource_name - ) - - def resource_string(self, package_or_requirement, resource_name): - """Return specified resource as a string""" - return get_provider(package_or_requirement).get_resource_string( - self, resource_name - ) - - def resource_listdir(self, package_or_requirement, resource_name): - """List the contents of the named resource directory""" - return get_provider(package_or_requirement).resource_listdir( - resource_name - ) - - def extraction_error(self): - """Give an error message for problems extracting file(s)""" - - old_exc = sys.exc_info()[1] - cache_path = self.extraction_path or get_default_cache() - - err = ExtractionError("""Can't extract file(s) to egg cache - -The following error occurred while trying to extract file(s) to the Python egg -cache: - - %s - -The Python egg cache directory is currently set to: - - %s - -Perhaps your account does not have write access to this directory? You can -change the cache directory by setting the PYTHON_EGG_CACHE environment -variable to point to an accessible directory. -""" % (old_exc, cache_path) - ) - err.manager = self - err.cache_path = cache_path - err.original_error = old_exc - raise err - - - - - - - - - - - - - - - - def get_cache_path(self, archive_name, names=()): - """Return absolute location in cache for `archive_name` and `names` - - The parent directory of the resulting path will be created if it does - not already exist. `archive_name` should be the base filename of the - enclosing egg (which may not be the name of the enclosing zipfile!), - including its ".egg" extension. `names`, if provided, should be a - sequence of path name parts "under" the egg's extraction location. - - This method should only be called by resource providers that need to - obtain an extraction location, and only for names they intend to - extract, as it tracks the generated names for possible cleanup later. - """ - extract_path = self.extraction_path or get_default_cache() - target_path = os.path.join(extract_path, archive_name+'-tmp', *names) - try: - _bypass_ensure_directory(target_path) - except: - self.extraction_error() - - self.cached_files[target_path] = 1 - return target_path - - - - - - - - - - - - - - - - - - - - def postprocess(self, tempname, filename): - """Perform any platform-specific postprocessing of `tempname` - - This is where Mac header rewrites should be done; other platforms don't - have anything special they should do. - - Resource providers should call this method ONLY after successfully - extracting a compressed resource. They must NOT call it on resources - that are already in the filesystem. - - `tempname` is the current (temporary) name of the file, and `filename` - is the name it will be renamed to by the caller after this routine - returns. - """ - - if os.name == 'posix': - # Make the resource executable - mode = ((os.stat(tempname).st_mode) | 0555) & 07777 - os.chmod(tempname, mode) - - - - - - - - - - - - - - - - - - - - - - - def set_extraction_path(self, path): - """Set the base path where resources will be extracted to, if needed. - - If you do not call this routine before any extractions take place, the - path defaults to the return value of ``get_default_cache()``. (Which - is based on the ``PYTHON_EGG_CACHE`` environment variable, with various - platform-specific fallbacks. See that routine's documentation for more - details.) - - Resources are extracted to subdirectories of this path based upon - information given by the ``IResourceProvider``. You may set this to a - temporary directory, but then you must call ``cleanup_resources()`` to - delete the extracted files when done. There is no guarantee that - ``cleanup_resources()`` will be able to remove all extracted files. - - (Note: you may not change the extraction path for a given resource - manager once resources have been extracted, unless you first call - ``cleanup_resources()``.) - """ - if self.cached_files: - raise ValueError( - "Can't change extraction path, files already extracted" - ) - - self.extraction_path = path - - def cleanup_resources(self, force=False): - """ - Delete all extracted resource files and directories, returning a list - of the file and directory names that could not be successfully removed. - This function does not have any concurrency protection, so it should - generally only be called when the extraction path is a temporary - directory exclusive to a single process. This method is not - automatically called; you must call it explicitly or register it as an - ``atexit`` function if you wish to ensure cleanup of a temporary - directory used for extractions. - """ - # XXX - - - -def get_default_cache(): - """Determine the default cache location - - This returns the ``PYTHON_EGG_CACHE`` environment variable, if set. - Otherwise, on Windows, it returns a "Python-Eggs" subdirectory of the - "Application Data" directory. On all other systems, it's "~/.python-eggs". - """ - try: - return os.environ['PYTHON_EGG_CACHE'] - except KeyError: - pass - - if os.name!='nt': - return os.path.expanduser('~/.python-eggs') - - app_data = 'Application Data' # XXX this may be locale-specific! - app_homes = [ - (('APPDATA',), None), # best option, should be locale-safe - (('USERPROFILE',), app_data), - (('HOMEDRIVE','HOMEPATH'), app_data), - (('HOMEPATH',), app_data), - (('HOME',), None), - (('WINDIR',), app_data), # 95/98/ME - ] - - for keys, subdir in app_homes: - dirname = '' - for key in keys: - if key in os.environ: - dirname = os.path.join(dirname, os.environ[key]) - else: - break - else: - if subdir: - dirname = os.path.join(dirname,subdir) - return os.path.join(dirname, 'Python-Eggs') - else: - raise RuntimeError( - "Please set the PYTHON_EGG_CACHE enviroment variable" - ) - -def safe_name(name): - """Convert an arbitrary string to a standard distribution name - - Any runs of non-alphanumeric/. characters are replaced with a single '-'. - """ - return re.sub('[^A-Za-z0-9.]+', '-', name) - - -def safe_version(version): - """Convert an arbitrary string to a standard version string - - Spaces become dots, and all other non-alphanumeric characters become - dashes, with runs of multiple dashes condensed to a single dash. - """ - version = version.replace(' ','.') - return re.sub('[^A-Za-z0-9.]+', '-', version) - - -def safe_extra(extra): - """Convert an arbitrary string to a standard 'extra' name - - Any runs of non-alphanumeric characters are replaced with a single '_', - and the result is always lowercased. - """ - return re.sub('[^A-Za-z0-9.]+', '_', extra).lower() - - -def to_filename(name): - """Convert a project or version name to its filename-escaped form - - Any '-' characters are currently replaced with '_'. - """ - return name.replace('-','_') - - - - - - - - -class NullProvider: - """Try to implement resources and metadata for arbitrary PEP 302 loaders""" - - egg_name = None - egg_info = None - loader = None - - def __init__(self, module): - self.loader = getattr(module, '__loader__', None) - self.module_path = os.path.dirname(getattr(module, '__file__', '')) - - def get_resource_filename(self, manager, resource_name): - return self._fn(self.module_path, resource_name) - - def get_resource_stream(self, manager, resource_name): - return StringIO(self.get_resource_string(manager, resource_name)) - - def get_resource_string(self, manager, resource_name): - return self._get(self._fn(self.module_path, resource_name)) - - def has_resource(self, resource_name): - return self._has(self._fn(self.module_path, resource_name)) - - def has_metadata(self, name): - return self.egg_info and self._has(self._fn(self.egg_info,name)) - - if sys.version_info <= (3,): - def get_metadata(self, name): - if not self.egg_info: - return "" - return self._get(self._fn(self.egg_info,name)) - else: - def get_metadata(self, name): - if not self.egg_info: - return "" - return self._get(self._fn(self.egg_info,name)).decode("utf-8") - - def get_metadata_lines(self, name): - return yield_lines(self.get_metadata(name)) - - def resource_isdir(self,resource_name): - return self._isdir(self._fn(self.module_path, resource_name)) - - def metadata_isdir(self,name): - return self.egg_info and self._isdir(self._fn(self.egg_info,name)) - - - def resource_listdir(self,resource_name): - return self._listdir(self._fn(self.module_path,resource_name)) - - def metadata_listdir(self,name): - if self.egg_info: - return self._listdir(self._fn(self.egg_info,name)) - return [] - - def run_script(self,script_name,namespace): - script = 'scripts/'+script_name - if not self.has_metadata(script): - raise ResolutionError("No script named %r" % script_name) - script_text = self.get_metadata(script).replace('\r\n','\n') - script_text = script_text.replace('\r','\n') - script_filename = self._fn(self.egg_info,script) - namespace['__file__'] = script_filename - if os.path.exists(script_filename): - execfile(script_filename, namespace, namespace) - else: - from linecache import cache - cache[script_filename] = ( - len(script_text), 0, script_text.split('\n'), script_filename - ) - script_code = compile(script_text,script_filename,'exec') - exec script_code in namespace, namespace - - def _has(self, path): - raise NotImplementedError( - "Can't perform this operation for unregistered loader type" - ) - - def _isdir(self, path): - raise NotImplementedError( - "Can't perform this operation for unregistered loader type" - ) - - def _listdir(self, path): - raise NotImplementedError( - "Can't perform this operation for unregistered loader type" - ) - - def _fn(self, base, resource_name): - if resource_name: - return os.path.join(base, *resource_name.split('/')) - return base - - def _get(self, path): - if hasattr(self.loader, 'get_data'): - return self.loader.get_data(path) - raise NotImplementedError( - "Can't perform this operation for loaders without 'get_data()'" - ) - -register_loader_type(object, NullProvider) - - -class EggProvider(NullProvider): - """Provider based on a virtual filesystem""" - - def __init__(self,module): - NullProvider.__init__(self,module) - self._setup_prefix() - - def _setup_prefix(self): - # we assume here that our metadata may be nested inside a "basket" - # of multiple eggs; that's why we use module_path instead of .archive - path = self.module_path - old = None - while path!=old: - if path.lower().endswith('.egg'): - self.egg_name = os.path.basename(path) - self.egg_info = os.path.join(path, 'EGG-INFO') - self.egg_root = path - break - old = path - path, base = os.path.split(path) - - - - - - -class DefaultProvider(EggProvider): - """Provides access to package resources in the filesystem""" - - def _has(self, path): - return os.path.exists(path) - - def _isdir(self,path): - return os.path.isdir(path) - - def _listdir(self,path): - return os.listdir(path) - - def get_resource_stream(self, manager, resource_name): - return open(self._fn(self.module_path, resource_name), 'rb') - - def _get(self, path): - stream = open(path, 'rb') - try: - return stream.read() - finally: - stream.close() - -register_loader_type(type(None), DefaultProvider) - - -class EmptyProvider(NullProvider): - """Provider that returns nothing for all requests""" - - _isdir = _has = lambda self,path: False - _get = lambda self,path: '' - _listdir = lambda self,path: [] - module_path = None - - def __init__(self): - pass - -empty_provider = EmptyProvider() - - - - -class ZipProvider(EggProvider): - """Resource support for zips and eggs""" - - eagers = None - - def __init__(self, module): - EggProvider.__init__(self,module) - self.zipinfo = zipimport._zip_directory_cache[self.loader.archive] - self.zip_pre = self.loader.archive+os.sep - - def _zipinfo_name(self, fspath): - # Convert a virtual filename (full path to file) into a zipfile subpath - # usable with the zipimport directory cache for our target archive - if fspath.startswith(self.zip_pre): - return fspath[len(self.zip_pre):] - raise AssertionError( - "%s is not a subpath of %s" % (fspath,self.zip_pre) - ) - - def _parts(self,zip_path): - # Convert a zipfile subpath into an egg-relative path part list - fspath = self.zip_pre+zip_path # pseudo-fs path - if fspath.startswith(self.egg_root+os.sep): - return fspath[len(self.egg_root)+1:].split(os.sep) - raise AssertionError( - "%s is not a subpath of %s" % (fspath,self.egg_root) - ) - - def get_resource_filename(self, manager, resource_name): - if not self.egg_name: - raise NotImplementedError( - "resource_filename() only supported for .egg, not .zip" - ) - # no need to lock for extraction, since we use temp names - zip_path = self._resource_to_zip(resource_name) - eagers = self._get_eager_resources() - if '/'.join(self._parts(zip_path)) in eagers: - for name in eagers: - self._extract_resource(manager, self._eager_to_zip(name)) - return self._extract_resource(manager, zip_path) - - def _extract_resource(self, manager, zip_path): - - if zip_path in self._index(): - for name in self._index()[zip_path]: - last = self._extract_resource( - manager, os.path.join(zip_path, name) - ) - return os.path.dirname(last) # return the extracted directory name - - zip_stat = self.zipinfo[zip_path] - t,d,size = zip_stat[5], zip_stat[6], zip_stat[3] - date_time = ( - (d>>9)+1980, (d>>5)&0xF, d&0x1F, # ymd - (t&0xFFFF)>>11, (t>>5)&0x3F, (t&0x1F) * 2, 0, 0, -1 # hms, etc. - ) - timestamp = time.mktime(date_time) - - try: - if not WRITE_SUPPORT: - raise IOError('"os.rename" and "os.unlink" are not supported ' - 'on this platform') - - real_path = manager.get_cache_path( - self.egg_name, self._parts(zip_path) - ) - - if os.path.isfile(real_path): - stat = os.stat(real_path) - if stat.st_size==size and stat.st_mtime==timestamp: - # size and stamp match, don't bother extracting - return real_path - - outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path)) - os.write(outf, self.loader.get_data(zip_path)) - os.close(outf) - utime(tmpnam, (timestamp,timestamp)) - manager.postprocess(tmpnam, real_path) - - try: - rename(tmpnam, real_path) - - except os.error: - if os.path.isfile(real_path): - stat = os.stat(real_path) - - if stat.st_size==size and stat.st_mtime==timestamp: - # size and stamp match, somebody did it just ahead of - # us, so we're done - return real_path - elif os.name=='nt': # Windows, del old file and retry - unlink(real_path) - rename(tmpnam, real_path) - return real_path - raise - - except os.error: - manager.extraction_error() # report a user-friendly error - - return real_path - - def _get_eager_resources(self): - if self.eagers is None: - eagers = [] - for name in ('native_libs.txt', 'eager_resources.txt'): - if self.has_metadata(name): - eagers.extend(self.get_metadata_lines(name)) - self.eagers = eagers - return self.eagers - - def _index(self): - try: - return self._dirindex - except AttributeError: - ind = {} - for path in self.zipinfo: - parts = path.split(os.sep) - while parts: - parent = os.sep.join(parts[:-1]) - if parent in ind: - ind[parent].append(parts[-1]) - break - else: - ind[parent] = [parts.pop()] - self._dirindex = ind - return ind - - def _has(self, fspath): - zip_path = self._zipinfo_name(fspath) - return zip_path in self.zipinfo or zip_path in self._index() - - def _isdir(self,fspath): - return self._zipinfo_name(fspath) in self._index() - - def _listdir(self,fspath): - return list(self._index().get(self._zipinfo_name(fspath), ())) - - def _eager_to_zip(self,resource_name): - return self._zipinfo_name(self._fn(self.egg_root,resource_name)) - - def _resource_to_zip(self,resource_name): - return self._zipinfo_name(self._fn(self.module_path,resource_name)) - -register_loader_type(zipimport.zipimporter, ZipProvider) - - - - - - - - - - - - - - - - - - - - - - - - -class FileMetadata(EmptyProvider): - """Metadata handler for standalone PKG-INFO files - - Usage:: - - metadata = FileMetadata("/path/to/PKG-INFO") - - This provider rejects all data and metadata requests except for PKG-INFO, - which is treated as existing, and will be the contents of the file at - the provided location. - """ - - def __init__(self,path): - self.path = path - - def has_metadata(self,name): - return name=='PKG-INFO' - - def get_metadata(self,name): - if name=='PKG-INFO': - f = open(self.path,'rU') - metadata = f.read() - f.close() - return metadata - raise KeyError("No metadata except PKG-INFO is available") - - def get_metadata_lines(self,name): - return yield_lines(self.get_metadata(name)) - - - - - - - - - - - - - - - - -class PathMetadata(DefaultProvider): - """Metadata provider for egg directories - - Usage:: - - # Development eggs: - - egg_info = "/path/to/PackageName.egg-info" - base_dir = os.path.dirname(egg_info) - metadata = PathMetadata(base_dir, egg_info) - dist_name = os.path.splitext(os.path.basename(egg_info))[0] - dist = Distribution(basedir,project_name=dist_name,metadata=metadata) - - # Unpacked egg directories: - - egg_path = "/path/to/PackageName-ver-pyver-etc.egg" - metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO')) - dist = Distribution.from_filename(egg_path, metadata=metadata) - """ - - def __init__(self, path, egg_info): - self.module_path = path - self.egg_info = egg_info - - -class EggMetadata(ZipProvider): - """Metadata provider for .egg files""" - - def __init__(self, importer): - """Create a metadata provider from a zipimporter""" - - self.zipinfo = zipimport._zip_directory_cache[importer.archive] - self.zip_pre = importer.archive+os.sep - self.loader = importer - if importer.prefix: - self.module_path = os.path.join(importer.archive, importer.prefix) - else: - self.module_path = importer.archive - self._setup_prefix() - - -class ImpWrapper: - """PEP 302 Importer that wraps Python's "normal" import algorithm""" - - def __init__(self, path=None): - self.path = path - - def find_module(self, fullname, path=None): - subname = fullname.split(".")[-1] - if subname != fullname and self.path is None: - return None - if self.path is None: - path = None - else: - path = [self.path] - try: - file, filename, etc = imp.find_module(subname, path) - except ImportError: - return None - return ImpLoader(file, filename, etc) - - -class ImpLoader: - """PEP 302 Loader that wraps Python's "normal" import algorithm""" - - def __init__(self, file, filename, etc): - self.file = file - self.filename = filename - self.etc = etc - - def load_module(self, fullname): - try: - mod = imp.load_module(fullname, self.file, self.filename, self.etc) - finally: - if self.file: self.file.close() - # Note: we don't set __loader__ because we want the module to look - # normal; i.e. this is just a wrapper for standard import machinery - return mod - - - - -def get_importer(path_item): - """Retrieve a PEP 302 "importer" for the given path item - - If there is no importer, this returns a wrapper around the builtin import - machinery. The returned importer is only cached if it was created by a - path hook. - """ - try: - importer = sys.path_importer_cache[path_item] - except KeyError: - for hook in sys.path_hooks: - try: - importer = hook(path_item) - except ImportError: - pass - else: - break - else: - importer = None - - sys.path_importer_cache.setdefault(path_item,importer) - if importer is None: - try: - importer = ImpWrapper(path_item) - except ImportError: - pass - return importer - -try: - from pkgutil import get_importer, ImpImporter -except ImportError: - pass # Python 2.3 or 2.4, use our own implementation -else: - ImpWrapper = ImpImporter # Python 2.5, use pkgutil's implementation - del ImpLoader, ImpImporter - - - - - - -_distribution_finders = {} - -def register_finder(importer_type, distribution_finder): - """Register `distribution_finder` to find distributions in sys.path items - - `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item - handler), and `distribution_finder` is a callable that, passed a path - item and the importer instance, yields ``Distribution`` instances found on - that path item. See ``pkg_resources.find_on_path`` for an example.""" - _distribution_finders[importer_type] = distribution_finder - - -def find_distributions(path_item, only=False): - """Yield distributions accessible via `path_item`""" - importer = get_importer(path_item) - finder = _find_adapter(_distribution_finders, importer) - return finder(importer, path_item, only) - -def find_in_zip(importer, path_item, only=False): - metadata = EggMetadata(importer) - if metadata.has_metadata('PKG-INFO'): - yield Distribution.from_filename(path_item, metadata=metadata) - if only: - return # don't yield nested distros - for subitem in metadata.resource_listdir('/'): - if subitem.endswith('.egg'): - subpath = os.path.join(path_item, subitem) - for dist in find_in_zip(zipimport.zipimporter(subpath), subpath): - yield dist - -register_finder(zipimport.zipimporter, find_in_zip) - -def StringIO(*args, **kw): - """Thunk to load the real StringIO on demand""" - global StringIO - try: - from cStringIO import StringIO - except ImportError: - from StringIO import StringIO - return StringIO(*args,**kw) - -def find_nothing(importer, path_item, only=False): - return () -register_finder(object,find_nothing) - -def find_on_path(importer, path_item, only=False): - """Yield distributions accessible on a sys.path directory""" - path_item = _normalize_cached(path_item) - - if os.path.isdir(path_item) and os.access(path_item, os.R_OK): - if path_item.lower().endswith('.egg'): - # unpacked egg - yield Distribution.from_filename( - path_item, metadata=PathMetadata( - path_item, os.path.join(path_item,'EGG-INFO') - ) - ) - else: - # scan for .egg and .egg-info in directory - for entry in os.listdir(path_item): - lower = entry.lower() - if lower.endswith('.egg-info'): - fullpath = os.path.join(path_item, entry) - if os.path.isdir(fullpath): - # egg-info directory, allow getting metadata - metadata = PathMetadata(path_item, fullpath) - else: - metadata = FileMetadata(fullpath) - yield Distribution.from_location( - path_item,entry,metadata,precedence=DEVELOP_DIST - ) - elif not only and lower.endswith('.egg'): - for dist in find_distributions(os.path.join(path_item, entry)): - yield dist - elif not only and lower.endswith('.egg-link'): - for line in open(os.path.join(path_item, entry)): - if not line.strip(): continue - for item in find_distributions(os.path.join(path_item,line.rstrip())): - yield item - break -register_finder(ImpWrapper,find_on_path) - -_namespace_handlers = {} -_namespace_packages = {} - -def register_namespace_handler(importer_type, namespace_handler): - """Register `namespace_handler` to declare namespace packages - - `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item - handler), and `namespace_handler` is a callable like this:: - - def namespace_handler(importer,path_entry,moduleName,module): - # return a path_entry to use for child packages - - Namespace handlers are only called if the importer object has already - agreed that it can handle the relevant path item, and they should only - return a subpath if the module __path__ does not already contain an - equivalent subpath. For an example namespace handler, see - ``pkg_resources.file_ns_handler``. - """ - _namespace_handlers[importer_type] = namespace_handler - -def _handle_ns(packageName, path_item): - """Ensure that named package includes a subpath of path_item (if needed)""" - importer = get_importer(path_item) - if importer is None: - return None - loader = importer.find_module(packageName) - if loader is None: - return None - module = sys.modules.get(packageName) - if module is None: - module = sys.modules[packageName] = types.ModuleType(packageName) - module.__path__ = []; _set_parent_ns(packageName) - elif not hasattr(module,'__path__'): - raise TypeError("Not a package:", packageName) - handler = _find_adapter(_namespace_handlers, importer) - subpath = handler(importer,path_item,packageName,module) - if subpath is not None: - path = module.__path__; path.append(subpath) - loader.load_module(packageName); module.__path__ = path - return subpath - -def declare_namespace(packageName): - """Declare that package 'packageName' is a namespace package""" - - imp.acquire_lock() - try: - if packageName in _namespace_packages: - return - - path, parent = sys.path, None - if '.' in packageName: - parent = '.'.join(packageName.split('.')[:-1]) - declare_namespace(parent) - __import__(parent) - try: - path = sys.modules[parent].__path__ - except AttributeError: - raise TypeError("Not a package:", parent) - - # Track what packages are namespaces, so when new path items are added, - # they can be updated - _namespace_packages.setdefault(parent,[]).append(packageName) - _namespace_packages.setdefault(packageName,[]) - - for path_item in path: - # Ensure all the parent's path items are reflected in the child, - # if they apply - _handle_ns(packageName, path_item) - - finally: - imp.release_lock() - -def fixup_namespace_packages(path_item, parent=None): - """Ensure that previously-declared namespace packages include path_item""" - imp.acquire_lock() - try: - for package in _namespace_packages.get(parent,()): - subpath = _handle_ns(package, path_item) - if subpath: fixup_namespace_packages(subpath,package) - finally: - imp.release_lock() - -def file_ns_handler(importer, path_item, packageName, module): - """Compute an ns-package subpath for a filesystem or zipfile importer""" - - subpath = os.path.join(path_item, packageName.split('.')[-1]) - normalized = _normalize_cached(subpath) - for item in module.__path__: - if _normalize_cached(item)==normalized: - break - else: - # Only return the path if it's not already there - return subpath - -register_namespace_handler(ImpWrapper,file_ns_handler) -register_namespace_handler(zipimport.zipimporter,file_ns_handler) - - -def null_ns_handler(importer, path_item, packageName, module): - return None - -register_namespace_handler(object,null_ns_handler) - - -def normalize_path(filename): - """Normalize a file/dir name for comparison purposes""" - return os.path.normcase(os.path.realpath(filename)) - -def _normalize_cached(filename,_cache={}): - try: - return _cache[filename] - except KeyError: - _cache[filename] = result = normalize_path(filename) - return result - -def _set_parent_ns(packageName): - parts = packageName.split('.') - name = parts.pop() - if parts: - parent = '.'.join(parts) - setattr(sys.modules[parent], name, sys.modules[packageName]) - - -def yield_lines(strs): - """Yield non-empty/non-comment lines of a ``basestring`` or sequence""" - if isinstance(strs,basestring): - for s in strs.splitlines(): - s = s.strip() - if s and not s.startswith('#'): # skip blank lines/comments - yield s - else: - for ss in strs: - for s in yield_lines(ss): - yield s - -LINE_END = re.compile(r"\s*(#.*)?$").match # whitespace and comment -CONTINUE = re.compile(r"\s*\\\s*(#.*)?$").match # line continuation -DISTRO = re.compile(r"\s*((\w|[-.])+)").match # Distribution or extra -VERSION = re.compile(r"\s*(<=?|>=?|==|!=)\s*((\w|[-.])+)").match # ver. info -COMMA = re.compile(r"\s*,").match # comma between items -OBRACKET = re.compile(r"\s*\[").match -CBRACKET = re.compile(r"\s*\]").match -MODULE = re.compile(r"\w+(\.\w+)*$").match -EGG_NAME = re.compile( - r"(?P[^-]+)" - r"( -(?P[^-]+) (-py(?P[^-]+) (-(?P.+))? )? )?", - re.VERBOSE | re.IGNORECASE -).match - -component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE) -replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c','dev':'@'}.get - -def _parse_version_parts(s): - for part in component_re.split(s): - part = replace(part,part) - if not part or part=='.': - continue - if part[:1] in '0123456789': - yield part.zfill(8) # pad for numeric comparison - else: - yield '*'+part - - yield '*final' # ensure that alpha/beta/candidate are before final - -def parse_version(s): - """Convert a version string to a chronologically-sortable key - - This is a rough cross between distutils' StrictVersion and LooseVersion; - if you give it versions that would work with StrictVersion, then it behaves - the same; otherwise it acts like a slightly-smarter LooseVersion. It is - *possible* to create pathological version coding schemes that will fool - this parser, but they should be very rare in practice. - - The returned value will be a tuple of strings. Numeric portions of the - version are padded to 8 digits so they will compare numerically, but - without relying on how numbers compare relative to strings. Dots are - dropped, but dashes are retained. Trailing zeros between alpha segments - or dashes are suppressed, so that e.g. "2.4.0" is considered the same as - "2.4". Alphanumeric parts are lower-cased. - - The algorithm assumes that strings like "-" and any alpha string that - alphabetically follows "final" represents a "patch level". So, "2.4-1" - is assumed to be a branch or patch of "2.4", and therefore "2.4.1" is - considered newer than "2.4-1", which in turn is newer than "2.4". - - Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that - come before "final" alphabetically) are assumed to be pre-release versions, - so that the version "2.4" is considered newer than "2.4a1". - - Finally, to handle miscellaneous cases, the strings "pre", "preview", and - "rc" are treated as if they were "c", i.e. as though they were release - candidates, and therefore are not as new as a version string that does not - contain them, and "dev" is replaced with an '@' so that it sorts lower than - than any other pre-release tag. - """ - parts = [] - for part in _parse_version_parts(s.lower()): - if part.startswith('*'): - if part<'*final': # remove '-' before a prerelease tag - while parts and parts[-1]=='*final-': parts.pop() - # remove trailing zeros from each series of numeric parts - while parts and parts[-1]=='00000000': - parts.pop() - parts.append(part) - return tuple(parts) - -class EntryPoint(object): - """Object representing an advertised importable object""" - - def __init__(self, name, module_name, attrs=(), extras=(), dist=None): - if not MODULE(module_name): - raise ValueError("Invalid module name", module_name) - self.name = name - self.module_name = module_name - self.attrs = tuple(attrs) - self.extras = Requirement.parse(("x[%s]" % ','.join(extras))).extras - self.dist = dist - - def __str__(self): - s = "%s = %s" % (self.name, self.module_name) - if self.attrs: - s += ':' + '.'.join(self.attrs) - if self.extras: - s += ' [%s]' % ','.join(self.extras) - return s - - def __repr__(self): - return "EntryPoint.parse(%r)" % str(self) - - def load(self, require=True, env=None, installer=None): - if require: self.require(env, installer) - entry = __import__(self.module_name, globals(),globals(), ['__name__']) - for attr in self.attrs: - try: - entry = getattr(entry,attr) - except AttributeError: - raise ImportError("%r has no %r attribute" % (entry,attr)) - return entry - - def require(self, env=None, installer=None): - if self.extras and not self.dist: - raise UnknownExtra("Can't require() without a distribution", self) - map(working_set.add, - working_set.resolve(self.dist.requires(self.extras),env,installer)) - - - - #@classmethod - def parse(cls, src, dist=None): - """Parse a single entry point from string `src` - - Entry point syntax follows the form:: - - name = some.module:some.attr [extra1,extra2] - - The entry name and module name are required, but the ``:attrs`` and - ``[extras]`` parts are optional - """ - try: - attrs = extras = () - name,value = src.split('=',1) - if '[' in value: - value,extras = value.split('[',1) - req = Requirement.parse("x["+extras) - if req.specs: raise ValueError - extras = req.extras - if ':' in value: - value,attrs = value.split(':',1) - if not MODULE(attrs.rstrip()): - raise ValueError - attrs = attrs.rstrip().split('.') - except ValueError: - raise ValueError( - "EntryPoint must be in 'name=module:attrs [extras]' format", - src - ) - else: - return cls(name.strip(), value.strip(), attrs, extras, dist) - - parse = classmethod(parse) - - - - - - - - - #@classmethod - def parse_group(cls, group, lines, dist=None): - """Parse an entry point group""" - if not MODULE(group): - raise ValueError("Invalid group name", group) - this = {} - for line in yield_lines(lines): - ep = cls.parse(line, dist) - if ep.name in this: - raise ValueError("Duplicate entry point", group, ep.name) - this[ep.name]=ep - return this - - parse_group = classmethod(parse_group) - - #@classmethod - def parse_map(cls, data, dist=None): - """Parse a map of entry point groups""" - if isinstance(data,dict): - data = data.items() - else: - data = split_sections(data) - maps = {} - for group, lines in data: - if group is None: - if not lines: - continue - raise ValueError("Entry points must be listed in groups") - group = group.strip() - if group in maps: - raise ValueError("Duplicate group name", group) - maps[group] = cls.parse_group(group, lines, dist) - return maps - - parse_map = classmethod(parse_map) - - -def _remove_md5_fragment(location): - if not location: - return '' - parsed = urlparse(location) - if parsed[-1].startswith('md5='): - return urlunparse(parsed[:-1] + ('',)) - return location - - -class Distribution(object): - """Wrap an actual or potential sys.path entry w/metadata""" - def __init__(self, - location=None, metadata=None, project_name=None, version=None, - py_version=PY_MAJOR, platform=None, precedence = EGG_DIST - ): - self.project_name = safe_name(project_name or 'Unknown') - if version is not None: - self._version = safe_version(version) - self.py_version = py_version - self.platform = platform - self.location = location - self.precedence = precedence - self._provider = metadata or empty_provider - - #@classmethod - def from_location(cls,location,basename,metadata=None,**kw): - project_name, version, py_version, platform = [None]*4 - basename, ext = os.path.splitext(basename) - if ext.lower() in (".egg",".egg-info"): - match = EGG_NAME(basename) - if match: - project_name, version, py_version, platform = match.group( - 'name','ver','pyver','plat' - ) - return cls( - location, metadata, project_name=project_name, version=version, - py_version=py_version, platform=platform, **kw - ) - from_location = classmethod(from_location) - - - hashcmp = property( - lambda self: ( - getattr(self,'parsed_version',()), - self.precedence, - self.key, - _remove_md5_fragment(self.location), - self.py_version, - self.platform - ) - ) - def __hash__(self): return hash(self.hashcmp) - def __lt__(self, other): - return self.hashcmp < other.hashcmp - def __le__(self, other): - return self.hashcmp <= other.hashcmp - def __gt__(self, other): - return self.hashcmp > other.hashcmp - def __ge__(self, other): - return self.hashcmp >= other.hashcmp - def __eq__(self, other): - if not isinstance(other, self.__class__): - # It's not a Distribution, so they are not equal - return False - return self.hashcmp == other.hashcmp - def __ne__(self, other): - return not self == other - - # These properties have to be lazy so that we don't have to load any - # metadata until/unless it's actually needed. (i.e., some distributions - # may not know their name or version without loading PKG-INFO) - - #@property - def key(self): - try: - return self._key - except AttributeError: - self._key = key = self.project_name.lower() - return key - key = property(key) - - #@property - def parsed_version(self): - try: - return self._parsed_version - except AttributeError: - self._parsed_version = pv = parse_version(self.version) - return pv - - parsed_version = property(parsed_version) - - #@property - def version(self): - try: - return self._version - except AttributeError: - for line in self._get_metadata('PKG-INFO'): - if line.lower().startswith('version:'): - self._version = safe_version(line.split(':',1)[1].strip()) - return self._version - else: - raise ValueError( - "Missing 'Version:' header and/or PKG-INFO file", self - ) - version = property(version) - - - - - #@property - def _dep_map(self): - try: - return self.__dep_map - except AttributeError: - dm = self.__dep_map = {None: []} - for name in 'requires.txt', 'depends.txt': - for extra,reqs in split_sections(self._get_metadata(name)): - if extra: extra = safe_extra(extra) - dm.setdefault(extra,[]).extend(parse_requirements(reqs)) - return dm - _dep_map = property(_dep_map) - - def requires(self,extras=()): - """List of Requirements needed for this distro if `extras` are used""" - dm = self._dep_map - deps = [] - deps.extend(dm.get(None,())) - for ext in extras: - try: - deps.extend(dm[safe_extra(ext)]) - except KeyError: - raise UnknownExtra( - "%s has no such extra feature %r" % (self, ext) - ) - return deps - - def _get_metadata(self,name): - if self.has_metadata(name): - for line in self.get_metadata_lines(name): - yield line - - def activate(self,path=None): - """Ensure distribution is importable on `path` (default=sys.path)""" - if path is None: path = sys.path - self.insert_on(path) - if path is sys.path: - fixup_namespace_packages(self.location) - map(declare_namespace, self._get_metadata('namespace_packages.txt')) - - - def egg_name(self): - """Return what this distribution's standard .egg filename should be""" - filename = "%s-%s-py%s" % ( - to_filename(self.project_name), to_filename(self.version), - self.py_version or PY_MAJOR - ) - - if self.platform: - filename += '-'+self.platform - return filename - - def __repr__(self): - if self.location: - return "%s (%s)" % (self,self.location) - else: - return str(self) - - def __str__(self): - try: version = getattr(self,'version',None) - except ValueError: version = None - version = version or "[unknown version]" - return "%s %s" % (self.project_name,version) - - def __getattr__(self,attr): - """Delegate all unrecognized public attributes to .metadata provider""" - if attr.startswith('_'): - raise AttributeError,attr - return getattr(self._provider, attr) - - #@classmethod - def from_filename(cls,filename,metadata=None, **kw): - return cls.from_location( - _normalize_cached(filename), os.path.basename(filename), metadata, - **kw - ) - from_filename = classmethod(from_filename) - - def as_requirement(self): - """Return a ``Requirement`` that matches this distribution exactly""" - return Requirement.parse('%s==%s' % (self.project_name, self.version)) - - def load_entry_point(self, group, name): - """Return the `name` entry point of `group` or raise ImportError""" - ep = self.get_entry_info(group,name) - if ep is None: - raise ImportError("Entry point %r not found" % ((group,name),)) - return ep.load() - - def get_entry_map(self, group=None): - """Return the entry point map for `group`, or the full entry map""" - try: - ep_map = self._ep_map - except AttributeError: - ep_map = self._ep_map = EntryPoint.parse_map( - self._get_metadata('entry_points.txt'), self - ) - if group is not None: - return ep_map.get(group,{}) - return ep_map - - def get_entry_info(self, group, name): - """Return the EntryPoint object for `group`+`name`, or ``None``""" - return self.get_entry_map(group).get(name) - - - - - - - - - - - - - - - - - - - - def insert_on(self, path, loc = None): - """Insert self.location in path before its nearest parent directory""" - - loc = loc or self.location - - if self.project_name == 'setuptools': - try: - version = self.version - except ValueError: - version = '' - if '0.7' in version: - raise ValueError( - "A 0.7-series setuptools cannot be installed " - "with distribute. Found one at %s" % str(self.location)) - - if not loc: - return - - if path is sys.path: - self.check_version_conflict() - - nloc = _normalize_cached(loc) - bdir = os.path.dirname(nloc) - npath= map(_normalize_cached, path) - - bp = None - for p, item in enumerate(npath): - if item==nloc: - break - elif item==bdir and self.precedence==EGG_DIST: - # if it's an .egg, give it precedence over its directory - path.insert(p, loc) - npath.insert(p, nloc) - break - else: - path.append(loc) - return - - # p is the spot where we found or inserted loc; now remove duplicates - while 1: - try: - np = npath.index(nloc, p+1) - except ValueError: - break - else: - del npath[np], path[np] - p = np # ha! - - return - - - - def check_version_conflict(self): - if self.key=='distribute': - return # ignore the inevitable setuptools self-conflicts :( - - nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) - loc = normalize_path(self.location) - for modname in self._get_metadata('top_level.txt'): - if (modname not in sys.modules or modname in nsp - or modname in _namespace_packages - ): - continue - if modname in ('pkg_resources', 'setuptools', 'site'): - continue - fn = getattr(sys.modules[modname], '__file__', None) - if fn and (normalize_path(fn).startswith(loc) or - fn.startswith(self.location)): - continue - issue_warning( - "Module %s was already imported from %s, but %s is being added" - " to sys.path" % (modname, fn, self.location), - ) - - def has_version(self): - try: - self.version - except ValueError: - issue_warning("Unbuilt egg for "+repr(self)) - return False - return True - - def clone(self,**kw): - """Copy this distribution, substituting in any changed keyword args""" - for attr in ( - 'project_name', 'version', 'py_version', 'platform', 'location', - 'precedence' - ): - kw.setdefault(attr, getattr(self,attr,None)) - kw.setdefault('metadata', self._provider) - return self.__class__(**kw) - - - - - #@property - def extras(self): - return [dep for dep in self._dep_map if dep] - extras = property(extras) - - -def issue_warning(*args,**kw): - level = 1 - g = globals() - try: - # find the first stack frame that is *not* code in - # the pkg_resources module, to use for the warning - while sys._getframe(level).f_globals is g: - level += 1 - except ValueError: - pass - from warnings import warn - warn(stacklevel = level+1, *args, **kw) - - - - - - - - - - - - - - - - - - - - - - - -def parse_requirements(strs): - """Yield ``Requirement`` objects for each specification in `strs` - - `strs` must be an instance of ``basestring``, or a (possibly-nested) - iterable thereof. - """ - # create a steppable iterator, so we can handle \-continuations - lines = iter(yield_lines(strs)) - - def scan_list(ITEM,TERMINATOR,line,p,groups,item_name): - - items = [] - - while not TERMINATOR(line,p): - if CONTINUE(line,p): - try: - line = lines.next(); p = 0 - except StopIteration: - raise ValueError( - "\\ must not appear on the last nonblank line" - ) - - match = ITEM(line,p) - if not match: - raise ValueError("Expected "+item_name+" in",line,"at",line[p:]) - - items.append(match.group(*groups)) - p = match.end() - - match = COMMA(line,p) - if match: - p = match.end() # skip the comma - elif not TERMINATOR(line,p): - raise ValueError( - "Expected ',' or end-of-list in",line,"at",line[p:] - ) - - match = TERMINATOR(line,p) - if match: p = match.end() # skip the terminator, if any - return line, p, items - - for line in lines: - match = DISTRO(line) - if not match: - raise ValueError("Missing distribution spec", line) - project_name = match.group(1) - p = match.end() - extras = [] - - match = OBRACKET(line,p) - if match: - p = match.end() - line, p, extras = scan_list( - DISTRO, CBRACKET, line, p, (1,), "'extra' name" - ) - - line, p, specs = scan_list(VERSION,LINE_END,line,p,(1,2),"version spec") - specs = [(op,safe_version(val)) for op,val in specs] - yield Requirement(project_name, specs, extras) - - -def _sort_dists(dists): - tmp = [(dist.hashcmp,dist) for dist in dists] - tmp.sort() - dists[::-1] = [d for hc,d in tmp] - - - - - - - - - - - - - - - - - -class Requirement: - def __init__(self, project_name, specs, extras): - """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" - self.unsafe_name, project_name = project_name, safe_name(project_name) - self.project_name, self.key = project_name, project_name.lower() - index = [(parse_version(v),state_machine[op],op,v) for op,v in specs] - index.sort() - self.specs = [(op,ver) for parsed,trans,op,ver in index] - self.index, self.extras = index, tuple(map(safe_extra,extras)) - self.hashCmp = ( - self.key, tuple([(op,parsed) for parsed,trans,op,ver in index]), - frozenset(self.extras) - ) - self.__hash = hash(self.hashCmp) - - def __str__(self): - specs = ','.join([''.join(s) for s in self.specs]) - extras = ','.join(self.extras) - if extras: extras = '[%s]' % extras - return '%s%s%s' % (self.project_name, extras, specs) - - def __eq__(self,other): - return isinstance(other,Requirement) and self.hashCmp==other.hashCmp - - def __contains__(self,item): - if isinstance(item,Distribution): - if item.key <> self.key: return False - if self.index: item = item.parsed_version # only get if we need it - elif isinstance(item,basestring): - item = parse_version(item) - last = None - compare = lambda a, b: (a > b) - (a < b) # -1, 0, 1 - for parsed,trans,op,ver in self.index: - action = trans[compare(item,parsed)] # Indexing: 0, 1, -1 - if action=='F': return False - elif action=='T': return True - elif action=='+': last = True - elif action=='-' or last is None: last = False - if last is None: last = True # no rules encountered - return last - - - def __hash__(self): - return self.__hash - - def __repr__(self): return "Requirement.parse(%r)" % str(self) - - #@staticmethod - def parse(s, replacement=True): - reqs = list(parse_requirements(s)) - if reqs: - if len(reqs) == 1: - founded_req = reqs[0] - # if asked for setuptools distribution - # and if distribute is installed, we want to give - # distribute instead - if _override_setuptools(founded_req) and replacement: - distribute = list(parse_requirements('distribute')) - if len(distribute) == 1: - return distribute[0] - return founded_req - else: - return founded_req - - raise ValueError("Expected only one requirement", s) - raise ValueError("No requirements found", s) - - parse = staticmethod(parse) - -state_machine = { - # =>< - '<' : '--T', - '<=': 'T-T', - '>' : 'F+F', - '>=': 'T+F', - '==': 'T..', - '!=': 'F++', -} - - -def _override_setuptools(req): - """Return True when distribute wants to override a setuptools dependency. - - We want to override when the requirement is setuptools and the version is - a variant of 0.6. - - """ - if req.project_name == 'setuptools': - if not len(req.specs): - # Just setuptools: ok - return True - for comparator, version in req.specs: - if comparator in ['==', '>=', '>']: - if '0.7' in version: - # We want some setuptools not from the 0.6 series. - return False - return True - return False - - -def _get_mro(cls): - """Get an mro for a type or classic class""" - if not isinstance(cls,type): - class cls(cls,object): pass - return cls.__mro__[1:] - return cls.__mro__ - -def _find_adapter(registry, ob): - """Return an adapter factory for `ob` from `registry`""" - for t in _get_mro(getattr(ob, '__class__', type(ob))): - if t in registry: - return registry[t] - - -def ensure_directory(path): - """Ensure that the parent directory of `path` exists""" - dirname = os.path.dirname(path) - if not os.path.isdir(dirname): - os.makedirs(dirname) - -def split_sections(s): - """Split a string or iterable thereof into (section,content) pairs - - Each ``section`` is a stripped version of the section header ("[section]") - and each ``content`` is a list of stripped lines excluding blank lines and - comment-only lines. If there are any such lines before the first section - header, they're returned in a first ``section`` of ``None``. - """ - section = None - content = [] - for line in yield_lines(s): - if line.startswith("["): - if line.endswith("]"): - if section or content: - yield section, content - section = line[1:-1].strip() - content = [] - else: - raise ValueError("Invalid section heading", line) - else: - content.append(line) - - # wrap up last segment - yield section, content - -def _mkstemp(*args,**kw): - from tempfile import mkstemp - old_open = os.open - try: - os.open = os_open # temporarily bypass sandboxing - return mkstemp(*args,**kw) - finally: - os.open = old_open # and then put it back - - -# Set up global resource manager -_manager = ResourceManager() -def _initialize(g): - for name in dir(_manager): - if not name.startswith('_'): - g[name] = getattr(_manager, name) -_initialize(globals()) - -# Prepare the master working set and make the ``require()`` API available -working_set = WorkingSet() -try: - # Does the main program list any requirements? - from __main__ import __requires__ -except ImportError: - pass # No: just use the default working set based on sys.path -else: - # Yes: ensure the requirements are met, by prefixing sys.path if necessary - try: - working_set.require(__requires__) - except VersionConflict: # try it without defaults already on sys.path - working_set = WorkingSet([]) # by starting with an empty path - for dist in working_set.resolve( - parse_requirements(__requires__), Environment() - ): - working_set.add(dist) - for entry in sys.path: # add any missing entries from sys.path - if entry not in working_set.entries: - working_set.add_entry(entry) - sys.path[:] = working_set.entries # then copy back to sys.path - -require = working_set.require -iter_entry_points = working_set.iter_entry_points -add_activation_listener = working_set.subscribe -run_script = working_set.run_script -run_main = run_script # backward compatibility -# Activate all distributions already on sys.path, and ensure that -# all distributions added to the working set in the future (e.g. by -# calling ``require()``) will get activated as well. -add_activation_listener(lambda dist: dist.activate()) -working_set.entries=[]; map(working_set.add_entry,sys.path) # match order - diff --git a/src/distutils2/pypi/simple.py b/src/distutils2/pypi/simple.py --- a/src/distutils2/pypi/simple.py +++ b/src/distutils2/pypi/simple.py @@ -1,797 +1,393 @@ -"""distutils2.pypi.package_index +"""pypi.simple -Implement a simple way to use the PyPI API's, by using known URIs from -PyPI. Works with the API defined on -http://peak.telecommunity.com/DevCenter/EasyInstall#package-index-api , -and available at http://pypi.python.org/simple/ . +Contains the class "SimpleIndex", a simple spider to find and retrieve +distributions on the Python Package Index, using it's "simple" API, +avalaible at http://pypi.python.org/simple/ +""" +from fnmatch import translate +import httplib +import re +import socket +import sys +import urllib2 +import urlparse -""" -# XXX Make a explaination document on how this API is made. - -import sys -import os.path -import re -import urlparse -import urllib2 -import shutil -import socket -import cStringIO -import httplib -import logging - -from distutils2.errors import DistutilsError +from distutils2.version import VersionPredicate +from distutils2.pypi.dist import (PyPIDistribution, PyPIDistributions, + EXTENSIONS) +from distutils2.pypi.errors import (PyPIError, DistributionNotFound, + DownloadError, UnableToDownload) from distutils2 import __version__ as __distutils2_version__ -try: - from hashlib import md5 -except ImportError: - from md5 import md5 -from fnmatch import translate +# -- Constants ----------------------------------------------- +PYPI_DEFAULT_INDEX_URL = "http://pypi.python.org/simple/" +PYPI_DEFAULT_MIRROR_URL = "mirrors.pypi.python.org" +DEFAULT_HOSTS = ("*",) +SOCKET_TIMEOUT = 15 +USER_AGENT = "Python-urllib/%s distutils2/%s" % ( + sys.version[:3], __distutils2_version__) -# -- Constants ---------------------------------------------------------------- - -__all__ = ['PackageIndex', - 'distros_for_url', - 'parse_bdist_wininst', - 'interpret_distro_name', -] - -_SOCKET_TIMEOUT = 15 -SOURCE_DIST = 1 +# -- Regexps ------------------------------------------------- EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$') HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) -# this is here to fix emacs' cruddy broken syntax highlighting PYPI_MD5 = re.compile( '([^<]+)\n\s+\\(md5\\)' -) -URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match -EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split() - -# -- Dependencies from distribute --------------------------------------------- - -# XXX Remove these dependencies, refactor the package_index codebase to not -# depends anymore on distribute classes and internal workings. -from pkg_resources import * - -# -- Base functions ----------------------------------------------------------- - -def parse_bdist_wininst(name): - """Parse a bdist_win package name, and return it's package base name - and version number if prossible. - - Return a tuple of (base,pyversion) for possible .exe names""" - - lower = name.lower() - base, py_ver = None, None - - if lower.endswith('.exe'): - if lower.endswith('.win32.exe'): - base = name[:-10] - elif lower.startswith('.win32-py',-16): - py_ver = name[-7:-4] - base = name[:-16] - - return base,py_ver - -def egg_info_for_url(url): - """ - """ - scheme, server, path, parameters, query, fragment = urlparse.urlparse(url) - base = urllib2.unquote(path.split('/')[-1]) - if '#' in base: - base, fragment = base.split('#',1) - return base,fragment - -def distros_for_url(url, metadata=None): - """Yield egg or source distribution objects that might be found at a URL""" - base, fragment = egg_info_for_url(url) - for dist in distros_for_location(url, base, metadata): - yield dist - if fragment: - match = EGG_FRAGMENT.match(fragment) - if match: - for dist in interpret_distro_name(url, match.group(1), metadata, - precedence = CHECKOUT_DIST): - yield dist - -def distros_for_location(location, basename, metadata=None): - """Yield egg or source distribution objects based on basename""" - if basename.endswith('.egg.zip'): - basename = basename[:-4] # strip the .zip - if basename.endswith('.egg') and '-' in basename: - # only one, unambiguous interpretation - return [Distribution.from_location(location, basename, metadata)] - - if basename.endswith('.exe'): - win_base, py_ver = parse_bdist_wininst(basename) - if win_base is not None: - return interpret_distro_name(location, win_base, metadata, py_ver, - BINARY_DIST, "win32") - - # Try source distro extensions (.zip, .tgz, etc.) - for ext in EXTENSIONS: - if basename.endswith(ext): - basename = basename[:-len(ext)] - return interpret_distro_name(location, basename, metadata) - return [] # no extension matched - -def distros_for_filename(filename, metadata=None): - """Yield possible egg or source distribution objects based on a - filename. - """ - return distros_for_location(normalize_path(filename), - os.path.basename(filename), metadata) - -def interpret_distro_name(location, basename, metadata, - py_version=None, precedence=SOURCE_DIST, platform=None): - """Generate alternative interpretations of a source distro name - - Note: if `location` is a filesystem filename, you should call - ``pkg_resources.normalize_path()`` on it before passing it to this - routine! - """ - # Generate alternative interpretations of a source distro name - # Because some packages are ambiguous as to name/versions split - # e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc. - # So, we generate each possible interepretation (e.g. "adns, python-1.1.0" - # "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice, - # the spurious interpretations should be ignored, because in the event - # there's also an "adns" package, the spurious "python-1.1.0" version will - # compare lower than any numeric version number, and is therefore unlikely - # to match a request for it. It's still a potential problem, though, and - # in the long run PyPI and the distutils should go for "safe" names and - # versions in distribution archive names (sdist and bdist). - - parts = basename.split('-') - if not py_version: - for i,p in enumerate(parts[2:]): - if len(p)==5 and p.startswith('py2.'): - return # It's a bdist_dumb, not an sdist -- bail out - - for p in range(1,len(parts)+1): - yield Distribution( - location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]), - py_version=py_version, precedence = precedence, - platform = platform) - -REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I) - -def find_external_links(url, page): - """Find rel="homepage" and rel="download" links in `page`, yielding URLs""" - - for match in REL.finditer(page): - tag, rel = match.groups() - rels = map(str.strip, rel.lower().split(',')) - if 'homepage' in rels or 'download' in rels: - for match in HREF.finditer(tag): - yield urlparse.urljoin(url, htmldecode(match.group(1))) - - for tag in ("

Home Page", "Download URL"): - pos = page.find(tag) - if pos!=-1: - match = HREF.search(page,pos) - if match: - yield urlparse.urljoin(url, htmldecode(match.group(1))) - -user_agent = "Python-urllib/%s distutils2/%s" % ( - sys.version[:3], __distutils2_version__ -) - -class PackageIndex(Environment): - """A distribution index that scans web pages for download URLs""" - - def __init__(self, index_url="http://pypi.python.org/simple", hosts=('*',), - *args, **kw): - self._init_logging() - Environment.__init__(self,*args,**kw) - self.index_url = index_url + "/"[:not index_url.endswith('/')] - self.scanned_urls = {} - self.fetched_urls = {} - self.package_pages = {} - self.allows = re.compile('|'.join(map(translate,hosts))).match - self.to_scan = [] - - def _init_logging(self): - import sys -# logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) - - def process_url(self, url, retrieve=False): - """Evaluate a URL as a possible download, and maybe retrieve it""" - if url in self.scanned_urls and not retrieve: - return - self.scanned_urls[url] = True - if not URL_SCHEME(url): - self.process_filename(url) - return - else: - dists = list(distros_for_url(url)) - if dists: - if not self.url_ok(url): - return - self.debug("Found link: %s", url) - - if dists or not retrieve or url in self.fetched_urls: - map(self.add, dists) - return # don't need the actual page - - if not self.url_ok(url): - self.fetched_urls[url] = True - return - - self.info("Reading %s", url) - f = self.open_url(url, "Download error: %s -- Some packages may not be found!") - if f is None: return - self.fetched_urls[url] = self.fetched_urls[f.url] = True - - if 'html' not in f.headers.get('content-type', '').lower(): - f.close() # not html, we can't process it - return - - base = f.url # handle redirects - page = f.read() - if sys.version_info >= (3,): - charset = f.headers.get_param('charset') or 'latin-1' - page = page.decode(charset, "ignore") - f.close() - for match in HREF.finditer(page): - link = urlparse.urljoin(base, htmldecode(match.group(1))) - self.process_url(link) - if url.startswith(self.index_url) and getattr(f,'code',None)!=404: - page = self.process_index(url, page) - - def process_filename(self, fn, nested=False): - # process filenames or directories - if not os.path.exists(fn): - self.warn("Not found: %s", fn) - return - - if os.path.isdir(fn) and not nested: - path = os.path.realpath(fn) - for item in os.listdir(path): - self.process_filename(os.path.join(path,item), True) - - dists = distros_for_filename(fn) - if dists: - self.debug("Found: %s", fn) - map(self.add, dists) - - def url_ok(self, url, fatal=False): - s = URL_SCHEME(url) - if (s and s.group(1).lower()=='file') or self.allows(urlparse.urlparse(url)[1]): - return True - msg = "\nLink to % s ***BLOCKED*** by --allow-hosts\n" - if fatal: - raise DistutilsError(msg % url) - else: - self.warn(msg, url) - return False - - def scan_egg_links(self, search_path): - for item in search_path: - if os.path.isdir(item): - for entry in os.listdir(item): - if entry.endswith('.egg-link'): - self.scan_egg_link(item, entry) - - def scan_egg_link(self, path, entry): - lines = filter(None, map(str.strip, open(os.path.join(path, entry)))) - if len(lines)==2: - for dist in find_distributions(os.path.join(path, lines[0])): - dist.location = os.path.join(path, *lines) - dist.precedence = SOURCE_DIST - self.add(dist) - - def process_index(self,url,page): - """Process the contents of a PyPI page""" - def scan(link): - # Process a URL to see if it's for a package page - if link.startswith(self.index_url): - parts = map( - urllib2.unquote, link[len(self.index_url):].split('/') - ) - if len(parts)==2 and '#' not in parts[1]: - # it's a package page, sanitize and index it - pkg = safe_name(parts[0]) - ver = safe_version(parts[1]) - self.package_pages.setdefault(pkg.lower(),{})[link] = True - return to_filename(pkg), to_filename(ver) - return None, None - - # process an index page into the package-page index - for match in HREF.finditer(page): - try: - scan( urlparse.urljoin(url, htmldecode(match.group(1))) ) - except ValueError: - pass - - pkg, ver = scan(url) # ensure this page is in the page index - if pkg: - # process individual package page - for new_url in find_external_links(url, page): - # Process the found URL - base, frag = egg_info_for_url(new_url) - if base.endswith('.py') and not frag: - if ver: - new_url+='#egg=%s-%s' % (pkg,ver) - else: - self.need_version_info(url) - self.scan_url(new_url) - - return PYPI_MD5.sub( - lambda m: '%s' % m.group(1,3,2), page - ) - else: - return "" # no sense double-scanning non-package pages - - def need_version_info(self, url): - self.scan_all( - "Page at %s links to .py file(s) without version info; an index " - "scan is required.", url - ) - - def scan_all(self, msg=None, *args): - if self.index_url not in self.fetched_urls: - if msg: self.warn(msg,*args) - self.info( - "Scanning index of all packages (this may take a while)" - ) - self.scan_url(self.index_url) - - def find_packages(self, requirement): - self.scan_url(self.index_url + requirement.unsafe_name+'/') - - if not self.package_pages.get(requirement.key): - # Fall back to safe version of the name - self.scan_url(self.index_url + requirement.project_name+'/') - - if not self.package_pages.get(requirement.key): - # We couldn't find the target package, so search the index page too - self.not_found_in_index(requirement) - - for url in list(self.package_pages.get(requirement.key,())): - # scan each page that might be related to the desired package - self.scan_url(url) - - def obtain(self, requirement, installer=None): - self.prescan(); self.find_packages(requirement) - for dist in self[requirement.key]: - if dist in requirement: - return dist - self.debug("%s does not match %s", requirement, dist) - return super(PackageIndex, self).obtain(requirement,installer) - - def check_md5(self, cs, info, filename, tfp): - if re.match('md5=[0-9a-f]{32}$', info): - self.debug("Validating md5 checksum for %s", filename) - if cs.hexdigest()<>info[4:]: - tfp.close() - os.unlink(filename) - raise DistutilsError( - "MD5 validation failed for "+os.path.basename(filename)+ - "; possible download problem?") - - def add_find_links(self, urls): - """Add `urls` to the list that will be prescanned for searches""" - for url in urls: - if ( - self.to_scan is None # if we have already "gone online" - or not URL_SCHEME(url) # or it's a local file/directory - or url.startswith('file:') - or list(distros_for_url(url)) # or a direct package link - ): - # then go ahead and process it now - self.scan_url(url) - else: - # otherwise, defer retrieval till later - self.to_scan.append(url) - - def prescan(self): - """Scan urls scheduled for prescanning (e.g. --find-links)""" - if self.to_scan: - map(self.scan_url, self.to_scan) - self.to_scan = None # from now on, go ahead and process immediately - - def not_found_in_index(self, requirement): - if self[requirement.key]: # we've seen at least one distro - meth, msg = self.info, "Couldn't retrieve index page for %r" - else: # no distros seen for this name, might be misspelled - meth, msg = (self.warn, - "Couldn't find index page for %r (maybe misspelled?)") - meth(msg, requirement.unsafe_name) - self.scan_all() - - def download(self, spec, tmpdir): - """Locate and/or download `spec` to `tmpdir`, returning a local path - - `spec` may be a ``Requirement`` object, or a string containing a URL, - an existing local filename, or a project/version requirement spec - (i.e. the string form of a ``Requirement`` object). If it is the URL - of a .py file with an unambiguous ``#egg=name-version`` tag (i.e., one - that escapes ``-`` as ``_`` throughout), a trivial ``setup.py`` is - automatically created alongside the downloaded file. - - If `spec` is a ``Requirement`` object or a string containing a - project/version requirement spec, this method returns the location of - a matching distribution (possibly after downloading it to `tmpdir`). - If `spec` is a locally existing file or directory name, it is simply - returned unchanged. If `spec` is a URL, it is downloaded to a subpath - of `tmpdir`, and the local filename is returned. Various errors may be - raised if a problem occurs during downloading. - """ - if not isinstance(spec,Requirement): - scheme = URL_SCHEME(spec) - if scheme: - # It's a url, download it to tmpdir - found = self._download_url(scheme.group(1), spec, tmpdir) - base, fragment = egg_info_for_url(spec) - if base.endswith('.py'): - found = self.gen_setup(found,fragment,tmpdir) - return found - elif os.path.exists(spec): - # Existing file or directory, just return it - return spec - else: - try: - spec = Requirement.parse(spec) - except ValueError: - raise DistutilsError( - "Not a URL, existing file, or requirement spec: %r" % - (spec,) - ) - return getattr(self.fetch_distribution(spec, tmpdir),'location',None) - - def fetch_distribution(self, - requirement, tmpdir, force_scan=False, source=False, develop_ok=False - ): - """Obtain a distribution suitable for fulfilling `requirement` - - `requirement` must be a ``pkg_resources.Requirement`` instance. - If necessary, or if the `force_scan` flag is set, the requirement is - searched for in the (online) package index as well as the locally - installed packages. If a distribution matching `requirement` is found, - the returned distribution's ``location`` is the value you would have - gotten from calling the ``download()`` method with the matching - distribution's URL or filename. If no matching distribution is found, - ``None`` is returned. - - If the `source` flag is set, only source distributions and source - checkout links will be considered. Unless the `develop_ok` flag is - set, development and system eggs (i.e., those using the ``.egg-info`` - format) will be ignored. - """ - - # process a Requirement - self.info("Searching for %s", requirement) - skipped = {} - - def find(req): - # Find a matching distribution; may be called more than once - - for dist in self[req.key]: - - if dist.precedence==DEVELOP_DIST and not develop_ok: - if dist not in skipped: - self.warn("Skipping development or system egg: %s",dist) - skipped[dist] = 1 - continue - - if dist in req and (dist.precedence<=SOURCE_DIST or not source): - self.info("Best match: %s", dist) - return dist.clone( - location=self.download(dist.location, tmpdir) - ) - - if force_scan: - self.prescan() - self.find_packages(requirement) - - dist = find(requirement) - if dist is None and self.to_scan is not None: - self.prescan() - dist = find(requirement) - - if dist is None and not force_scan: - self.find_packages(requirement) - dist = find(requirement) - - if dist is None: - self.warn( - "No local packages or download links found for %s%s", - (source and "a source distribution of " or ""), - requirement, - ) - return dist - - def fetch(self, requirement, tmpdir, force_scan=False, source=False): - """Obtain a file suitable for fulfilling `requirement` - - DEPRECATED; use the ``fetch_distribution()`` method now instead. For - backward compatibility, this routine is identical but returns the - ``location`` of the downloaded distribution instead of a distribution - object. - """ - dist = self.fetch_distribution(requirement,tmpdir,force_scan,source) - if dist is not None: - return dist.location - return None - - def gen_setup(self, filename, fragment, tmpdir): - match = EGG_FRAGMENT.match(fragment) - dists = match and [d for d in - interpret_distro_name(filename, match.group(1), None) if d.version - ] or [] - - if len(dists)==1: # unambiguous ``#egg`` fragment - basename = os.path.basename(filename) - - # Make sure the file has been downloaded to the temp dir. - if os.path.dirname(filename) != tmpdir: - dst = os.path.join(tmpdir, basename) - from setuptools.command.easy_install import samefile - if not samefile(filename, dst): - shutil.copy2(filename, dst) - filename=dst - - file = open(os.path.join(tmpdir, 'setup.py'), 'w') - file.write( - "from setuptools import setup\n" - "setup(name=%r, version=%r, py_modules=[%r])\n" - % ( - dists[0].project_name, dists[0].version, - os.path.splitext(basename)[0] - ) - ) - file.close() - return filename - - elif match: - raise DistutilsError( - "Can't unambiguously interpret project/version identifier %r; " - "any dashes in the name or version should be escaped using " - "underscores. %r" % (fragment,dists) - ) - else: - raise DistutilsError( - "Can't process plain .py without an '#egg=name-version'" - " suffix to enable automatic setup script generation." - ) - - dl_blocksize = 8192 - def _download_to(self, url, filename): - self.info("Downloading %s", url) - # Download the file - fp, tfp, info = None, None, None - try: - if '#' in url: - url, info = url.split('#', 1) - fp = self.open_url(url) - if isinstance(fp, urllib2.HTTPError): - raise DistutilsError( - "Can't download %s: %s %s" % (url, fp.code,fp.msg) - ) - cs = md5() - headers = fp.info() - blocknum = 0 - bs = self.dl_blocksize - size = -1 - if "content-length" in headers: - size = int(headers["Content-Length"]) - self.reporthook(url, filename, blocknum, bs, size) - tfp = open(filename,'wb') - while True: - block = fp.read(bs) - if block: - cs.update(block) - tfp.write(block) - blocknum += 1 - self.reporthook(url, filename, blocknum, bs, size) - else: - break - if info: self.check_md5(cs, info, filename, tfp) - return headers - finally: - if fp: fp.close() - if tfp: tfp.close() - - def reporthook(self, url, filename, blocknum, blksize, size): - pass # no-op - - def open_url(self, url, warning=None): - if url.startswith('file:'): - return local_open(url) - try: - return open_with_auth(url) - except (ValueError, httplib.InvalidURL), v: - msg = ' '.join([str(arg) for arg in v.args]) - if warning: - self.warn(warning, msg) - else: - raise DistutilsError('%s %s' % (url, msg)) - except urllib2.HTTPError, v: - return v - except urllib2.URLError, v: - if warning: - self.warn(warning, v.reason) - else: - raise DistutilsError("Download error for %s: %s" - % (url, v.reason)) - except httplib.BadStatusLine, v: - if warning: - self.warn(warning, v.line) - else: - raise DistutilsError('%s returned a bad status line. ' - 'The server might be down, %s' % \ - (url, v.line)) - except httplib.HTTPException, v: - if warning: - self.warn(warning, v) - else: - raise DistutilsError("Download error for %s: %s" - % (url, v)) - - def _download_url(self, scheme, url, tmpdir): - # Determine download filename - name = filter(None,urlparse.urlparse(url)[2].split('/')) - if name: - name = name[-1] - while '..' in name: - name = name.replace('..','.').replace('\\','_') - else: - name = "__downloaded__" # default if URL has no path contents - - if name.endswith('.egg.zip'): - name = name[:-4] # strip the extra .zip before download - - filename = os.path.join(tmpdir,name) - - # Download the file - if scheme=='svn' or scheme.startswith('svn+'): - return self._download_svn(url, filename) - elif scheme=='file': - return urllib2.url2pathname(urlparse.urlparse(url)[2]) - else: - self.url_ok(url, True) # raises error if not allowed - return self._attempt_download(url, filename) - - def scan_url(self, url): - self.process_url(url, True) - - def _attempt_download(self, url, filename): - headers = self._download_to(url, filename) - if 'html' in headers.get('content-type','').lower(): - return self._download_html(url, headers, filename) - else: - return filename - - def _download_html(self, url, headers, filename): - file = open(filename) - for line in file: - if line.strip(): - # Check for a subversion index page - if re.search(r'([^- ]+ - )?Revision \d+:', line): - # it's a subversion index page: - file.close() - os.unlink(filename) - return self._download_svn(url, filename) - break # not an index page - file.close() - os.unlink(filename) - raise DistutilsError("Unexpected HTML page found at "+url) - - def _download_svn(self, url, filename): - url = url.split('#',1)[0] # remove any fragment for svn's sake - self.info("Doing subversion checkout from %s to %s", url, filename) - os.system("svn checkout -q %s %s" % (url, filename)) - return filename - - def debug(self, msg, *args): - logging.debug(msg, *args) - - def info(self, msg, *args): - logging.info(msg, *args) - - def warn(self, msg, *args): - logging.warn(msg, *args) + 'href="[^?]+\?:action=show_md5&digest=([0-9a-f]{32})">md5</a>\\)') +URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match # This pattern matches a character entity reference (a decimal numeric # references, a hexadecimal numeric reference, or a named reference). -entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub +ENTITY_SUB = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub +REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I) -def uchr(c): - if not isinstance(c, int): - return c - if c>255: - return unichr(c) - return chr(c) -def decode_entity(match): - what = match.group(1) - if what.startswith('#x'): - what = int(what[2:], 16) - elif what.startswith('#'): - what = int(what[1:]) - else: - from htmlentitydefs import name2codepoint - what = name2codepoint.get(what, match.group(0)) - return uchr(what) - -def htmldecode(text): - """Decode HTML entities in the given text.""" - return entity_sub(decode_entity, text) - -def socket_timeout(timeout=15): +def socket_timeout(timeout=SOCKET_TIMEOUT): + """Decorator to add a socket timeout when requesting pages on PyPI. + """ def _socket_timeout(func): - def _socket_timeout(*args, **kwargs): + def _socket_timeout(self, *args, **kwargs): old_timeout = socket.getdefaulttimeout() + if hasattr(self, "_timeout"): + timeout = self._timeout socket.setdefaulttimeout(timeout) try: - return func(*args, **kwargs) + return func(self, *args, **kwargs) finally: socket.setdefaulttimeout(old_timeout) return _socket_timeout return _socket_timeout -def open_with_auth(url): - """Open a urllib2 request, handling HTTP authentication""" - scheme, netloc, path, params, query, frag = urlparse.urlparse(url) +class SimpleIndex(object): + """Provides useful tools to request the Python Package Index simple API - if scheme in ('http', 'https'): - auth, host = urllib2.splituser(netloc) - else: - auth = None + :param index_url: the url of the simple index to search on. + :param follow_externals: tell if following external links is needed or + not. Default is False. + :param hosts: a list of hosts allowed to be processed while using + follow_externals=True. Default behavior is to follow all + hosts. + :param follow_externals: tell if following external links is needed or + not. Default is False. + :param prefer_source: if there is binary and source distributions, the + source prevails. + :param prefer_final: if the version is not mentioned, and the last + version is not a "final" one (alpha, beta, etc.), + pick up the last final version. + :param mirrors_url: the url to look on for DNS records giving mirror + adresses. + :param mirrors: a list of mirrors to check out if problems + occurs while working with the one given in "url" + :param timeout: time in seconds to consider a url has timeouted. + """ - if auth: - auth = "Basic " + urllib2.unquote(auth).encode('base64').strip() - new_url = urlparse.urlunparse((scheme,host,path,params,query,frag)) - request = urllib2.Request(new_url) - request.add_header("Authorization", auth) - else: - request = urllib2.Request(url) + def __init__(self, index_url=PYPI_DEFAULT_INDEX_URL, hosts=DEFAULT_HOSTS, + follow_externals=False, prefer_source=True, + prefer_final=False, mirrors_url=PYPI_DEFAULT_MIRROR_URL, + mirrors=None, timeout=SOCKET_TIMEOUT): + self.follow_externals = follow_externals - request.add_header('User-Agent', user_agent) - fp = urllib2.urlopen(request) + if not index_url.endswith("/"): + index_url += "/" + self._index_urls = [index_url] + # if no mirrors are defined, use the method described in PEP 381. + if mirrors is None: + try: + mirrors = socket.gethostbyname_ex(mirrors_url)[-1] + except socket.gaierror: + mirrors = [] + self._index_urls.extend(mirrors) + self._current_index_url = 0 + self._timeout = timeout + self._prefer_source = prefer_source + self._prefer_final = prefer_final - if auth: - # Put authentication info back into request URL if same host, - # so that links found on the page will work - s2, h2, path2, param2, query2, frag2 = urlparse.urlparse(fp.url) - if s2==scheme and h2==host: - fp.url = urlparse.urlunparse((s2,netloc,path2,param2,query2,frag2)) + # create a regexp to match all given hosts + self._allowed_hosts = re.compile('|'.join(map(translate, hosts))).match - return fp + # we keep an index of pages we have processed, in order to avoid + # scanning them multple time (eg. if there is multiple pages pointing + # on one) + self._processed_urls = [] + self._distributions = {} + def find(self, requirements, prefer_source=None, prefer_final=None): + """Browse the PyPI to find distributions that fullfil the given + requirements. -# adding a timeout to avoid freezing package_index -open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth) + :param requirements: A project name and it's distribution, using + version specifiers, as described in PEP345. + :type requirements: You can pass either a version.VersionPredicate + or a string. + :param prefer_source: if there is binary and source distributions, the + source prevails. + :param prefer_final: if the version is not mentioned, and the last + version is not a "final" one (alpha, beta, etc.), + pick up the last final version. + """ + requirements = self._get_version_predicate(requirements) + if prefer_source is None: + prefer_source = self._prefer_source + if prefer_final is None: + prefer_final = self._prefer_final -def fix_sf_url(url): - return url # backward compatibility + # process the index for this project + self._process_pypi_page(requirements.name) -def local_open(url): - """Read a local path, with special support for directories""" - scheme, server, path, param, query, frag = urlparse.urlparse(url) - filename = urllib2.url2pathname(path) - if os.path.isfile(filename): - return urllib2.urlopen(url) - elif path.endswith('/') and os.path.isdir(filename): - files = [] - for f in os.listdir(filename): - if f=='index.html': - fp = open(os.path.join(filename,f),'rb') - body = fp.read() - fp.close() - break - elif os.path.isdir(os.path.join(filename,f)): - f+='/' - files.append("<a href=%r>%s</a>" % (f,f)) + # filter with requirements and return the results + if requirements.name in self._distributions: + dists = self._distributions[requirements.name].filter(requirements) + dists.sort_distributions(prefer_source=prefer_source, + prefer_final=prefer_final) else: - body = ("<html><head><title>%s" % url) + \ - "%s" % '\n'.join(files) - status, message = 200, "OK" - else: - status, message, body = 404, "Path not found", "Not found" + dists = [] - return urllib2.HTTPError(url, status, message, - {'content-type':'text/html'}, cStringIO.StringIO(body)) + return dists + + def get(self, requirements, *args, **kwargs): + """Browse the PyPI index to find distributions that fullfil the + given requirements, and return the most recent one. + + You can specify prefer_final and prefer_source arguments here. + If not, the default one will be used. + """ + predicate = self._get_version_predicate(requirements) + dists = self.find(predicate, *args, **kwargs) + + if len(dists) == 0: + raise DistributionNotFound(requirements) + + return dists.get_last(predicate) + + def download(self, requirements, temp_path=None, *args, **kwargs): + """Download the distribution, using the requirements. + + If more than one distribution match the requirements, use the last + version. + Download the distribution, and put it in the temp_path. If no temp_path + is given, creates and return one. + + Returns the complete absolute path to the downloaded archive. + + :param requirements: The same as the find attribute of `find`. + + You can specify prefer_final and prefer_source arguments here. + If not, the default one will be used. + """ + return self.get(requirements, *args, **kwargs)\ + .download(path=temp_path) + + def _get_version_predicate(self, requirements): + """Return a VersionPredicate object, from a string or an already + existing object. + """ + if isinstance(requirements, str): + requirements = VersionPredicate(requirements) + return requirements + + @property + def index_url(self): + return self._index_urls[self._current_index_url] + + def _switch_to_next_mirror(self): + """Switch to the next mirror (eg. point self.index_url to the next + url. + """ + # Internally, iter over the _index_url iterable, if we have read all + # of the available indexes, raise an exception. + if self._current_index_url < len(self._index_urls): + self._current_index_url = self._current_index_url + 1 + else: + raise UnableToDownload("All mirrors fails") + + def _is_browsable(self, url): + """Tell if the given URL can be browsed or not. + + It uses the follow_externals and the hosts list to tell if the given + url is browsable or not. + """ + # if _index_url is contained in the given URL, we are browsing the + # index, and it's always "browsable". + # local files are always considered browable resources + if self.index_url in url or urlparse.urlparse(url)[0] == "file": + return True + elif self.follow_externals: + if self._allowed_hosts(urlparse.urlparse(url)[1]): # 1 is netloc + return True + else: + return False + return False + + def _is_distribution(self, link): + """Tell if the given URL matches to a distribution name or not. + """ + #XXX find a better way to check that links are distributions + # Using a regexp ? + for ext in EXTENSIONS: + if ext in link: + return True + return False + + def _register_dist(self, dist): + """Register a distribution as a part of fetched distributions for + SimpleIndex. + + Return the PyPIDistributions object for the specified project name + """ + # Internally, check if a entry exists with the project name, if not, + # create a new one, and if exists, add the dist to the pool. + if not dist.name in self._distributions: + self._distributions[dist.name] = PyPIDistributions() + self._distributions[dist.name].append(dist) + return self._distributions[dist.name] + + def _process_url(self, url, project_name=None, follow_links=True): + """Process an url and search for distributions packages. + + For each URL found, if it's a download, creates a PyPIdistribution + object. If it's a homepage and we can follow links, process it too. + + :param url: the url to process + :param project_name: the project name we are searching for. + :param follow_links: Do not want to follow links more than from one + level. This parameter tells if we want to follow + the links we find (eg. run recursively this + method on it) + """ + f = self._open_url(url) + base_url = f.url + if url not in self._processed_urls: + self._processed_urls.append(url) + link_matcher = self._get_link_matcher(url) + for link, is_download in link_matcher(f.read(), base_url): + if link not in self._processed_urls: + if self._is_distribution(link) or is_download: + self._processed_urls.append(link) + # it's a distribution, so create a dist object + dist = PyPIDistribution.from_url(link, project_name, + is_external=not self.index_url in url) + self._register_dist(dist) + else: + if self._is_browsable(link) and follow_links: + self._process_url(link, project_name, + follow_links=False) + + def _get_link_matcher(self, url): + """Returns the right link matcher function of the given url + """ + if self.index_url in url: + return self._simple_link_matcher + else: + return self._default_link_matcher + + def _simple_link_matcher(self, content, base_url): + """Yield all links with a rel="download" or rel="homepage". + + This matches the simple index requirements for matching links. + If follow_externals is set to False, dont yeld the external + urls. + """ + for match in REL.finditer(content): + tag, rel = match.groups() + rels = map(str.strip, rel.lower().split(',')) + if 'homepage' in rels or 'download' in rels: + for match in HREF.finditer(tag): + url = urlparse.urljoin(base_url, + self._htmldecode(match.group(1))) + if 'download' in rels or self._is_browsable(url): + # yield a list of (url, is_download) + yield (urlparse.urljoin(base_url, url), + 'download' in rels) + + def _default_link_matcher(self, content, base_url): + """Yield all links found on the page. + """ + for match in HREF.finditer(content): + url = urlparse.urljoin(base_url, self._htmldecode(match.group(1))) + if self._is_browsable(url): + yield (url, False) + + def _process_pypi_page(self, name): + """Find and process a PyPI page for the given project name. + + :param name: the name of the project to find the page + """ + try: + # Browse and index the content of the given PyPI page. + url = self.index_url + name + "/" + self._process_url(url, name) + except DownloadError: + # if an error occurs, try with the next index_url + # (provided by the mirrors) + self._switch_to_next_mirror() + self._distributions.clear() + self._process_pypi_page(name) + + @socket_timeout() + def _open_url(self, url): + """Open a urllib2 request, handling HTTP authentication, and local + files support. + + """ + try: + scheme, netloc, path, params, query, frag = urlparse.urlparse(url) + + if scheme in ('http', 'https'): + auth, host = urllib2.splituser(netloc) + else: + auth = None + + # add index.html automatically for filesystem paths + if scheme == 'file': + if url.endswith('/'): + url += "index.html" + + if auth: + auth = "Basic " + \ + urllib2.unquote(auth).encode('base64').strip() + new_url = urlparse.urlunparse(( + scheme, host, path, params, query, frag)) + request = urllib2.Request(new_url) + request.add_header("Authorization", auth) + else: + request = urllib2.Request(url) + request.add_header('User-Agent', USER_AGENT) + fp = urllib2.urlopen(request) + + if auth: + # Put authentication info back into request URL if same host, + # so that links found on the page will work + s2, h2, path2, param2, query2, frag2 = \ + urlparse.urlparse(fp.url) + if s2 == scheme and h2 == host: + fp.url = urlparse.urlunparse( + (s2, netloc, path2, param2, query2, frag2)) + + return fp + except (ValueError, httplib.InvalidURL), v: + msg = ' '.join([str(arg) for arg in v.args]) + raise PyPIError('%s %s' % (url, msg)) + except urllib2.HTTPError, v: + return v + except urllib2.URLError, v: + raise DownloadError("Download error for %s: %s" % (url, v.reason)) + except httplib.BadStatusLine, v: + raise DownloadError('%s returned a bad status line. ' + 'The server might be down, %s' % (url, v.line)) + except httplib.HTTPException, v: + raise DownloadError("Download error for %s: %s" % (url, v)) + + def _decode_entity(self, match): + what = match.group(1) + if what.startswith('#x'): + what = int(what[2:], 16) + elif what.startswith('#'): + what = int(what[1:]) + else: + from htmlentitydefs import name2codepoint + what = name2codepoint.get(what, match.group(0)) + return unichr(what) + + def _htmldecode(self, text): + """Decode HTML entities in the given text.""" + return ENTITY_SUB(self._decode_entity, text) diff --git a/src/distutils2/spawn.py b/src/distutils2/spawn.py deleted file mode 100644 --- a/src/distutils2/spawn.py +++ /dev/null @@ -1,173 +0,0 @@ -"""distutils.spawn - -Provides the 'spawn()' function, a front-end to various platform- -specific functions for launching another program in a sub-process. -Also provides the 'find_executable()' to search the path for a given -executable name. -""" - -__revision__ = "$Id: spawn.py 73147 2009-06-02 15:58:43Z tarek.ziade $" - -import sys -import os - -from distutils2.errors import DistutilsPlatformError, DistutilsExecError -from distutils2 import log - -def spawn(cmd, search_path=1, verbose=0, dry_run=0): - """Run another program, specified as a command list 'cmd', in a new process. - - 'cmd' is just the argument list for the new process, ie. - cmd[0] is the program to run and cmd[1:] are the rest of its arguments. - There is no way to run a program with a name different from that of its - executable. - - If 'search_path' is true (the default), the system's executable - search path will be used to find the program; otherwise, cmd[0] - must be the exact path to the executable. If 'dry_run' is true, - the command will not actually be run. - - Raise DistutilsExecError if running the program fails in any way; just - return on success. - """ - if os.name == 'posix': - _spawn_posix(cmd, search_path, dry_run=dry_run) - elif os.name == 'nt': - _spawn_nt(cmd, search_path, dry_run=dry_run) - elif os.name == 'os2': - _spawn_os2(cmd, search_path, dry_run=dry_run) - else: - raise DistutilsPlatformError, \ - "don't know how to spawn programs on platform '%s'" % os.name - -def _nt_quote_args(args): - """Quote command-line arguments for DOS/Windows conventions. - - Just wraps every argument which contains blanks in double quotes, and - returns a new argument list. - """ - # XXX this doesn't seem very robust to me -- but if the Windows guys - # say it'll work, I guess I'll have to accept it. (What if an arg - # contains quotes? What other magic characters, other than spaces, - # have to be escaped? Is there an escaping mechanism other than - # quoting?) - for i, arg in enumerate(args): - if ' ' in arg: - args[i] = '"%s"' % arg - return args - -def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): - executable = cmd[0] - cmd = _nt_quote_args(cmd) - if search_path: - # either we find one or it stays the same - executable = find_executable(executable) or executable - log.info(' '.join([executable] + cmd[1:])) - if not dry_run: - # spawn for NT requires a full path to the .exe - try: - rc = os.spawnv(os.P_WAIT, executable, cmd) - except OSError, exc: - # this seems to happen when the command isn't found - raise DistutilsExecError, \ - "command '%s' failed: %s" % (cmd[0], exc[-1]) - if rc != 0: - # and this reflects the command running but failing - raise DistutilsExecError, \ - "command '%s' failed with exit status %d" % (cmd[0], rc) - -def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): - executable = cmd[0] - if search_path: - # either we find one or it stays the same - executable = find_executable(executable) or executable - log.info(' '.join([executable] + cmd[1:])) - if not dry_run: - # spawnv for OS/2 EMX requires a full path to the .exe - try: - rc = os.spawnv(os.P_WAIT, executable, cmd) - except OSError, exc: - # this seems to happen when the command isn't found - raise DistutilsExecError, \ - "command '%s' failed: %s" % (cmd[0], exc[-1]) - if rc != 0: - # and this reflects the command running but failing - log.debug("command '%s' failed with exit status %d" % (cmd[0], rc)) - raise DistutilsExecError, \ - "command '%s' failed with exit status %d" % (cmd[0], rc) - - -def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): - log.info(' '.join(cmd)) - if dry_run: - return - exec_fn = search_path and os.execvp or os.execv - pid = os.fork() - - if pid == 0: # in the child - try: - exec_fn(cmd[0], cmd) - except OSError, e: - sys.stderr.write("unable to execute %s: %s\n" % - (cmd[0], e.strerror)) - os._exit(1) - - sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0]) - os._exit(1) - else: # in the parent - # Loop until the child either exits or is terminated by a signal - # (ie. keep waiting if it's merely stopped) - while 1: - try: - pid, status = os.waitpid(pid, 0) - except OSError, exc: - import errno - if exc.errno == errno.EINTR: - continue - raise DistutilsExecError, \ - "command '%s' failed: %s" % (cmd[0], exc[-1]) - if os.WIFSIGNALED(status): - raise DistutilsExecError, \ - "command '%s' terminated by signal %d" % \ - (cmd[0], os.WTERMSIG(status)) - - elif os.WIFEXITED(status): - exit_status = os.WEXITSTATUS(status) - if exit_status == 0: - return # hey, it succeeded! - else: - raise DistutilsExecError, \ - "command '%s' failed with exit status %d" % \ - (cmd[0], exit_status) - - elif os.WIFSTOPPED(status): - continue - - else: - raise DistutilsExecError, \ - "unknown error executing '%s': termination status %d" % \ - (cmd[0], status) - -def find_executable(executable, path=None): - """Tries to find 'executable' in the directories listed in 'path'. - - A string listing directories separated by 'os.pathsep'; defaults to - os.environ['PATH']. Returns the complete filename or None if not found. - """ - if path is None: - path = os.environ['PATH'] - paths = path.split(os.pathsep) - base, ext = os.path.splitext(executable) - - if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): - executable = executable + '.exe' - - if not os.path.isfile(executable): - for p in paths: - f = os.path.join(p, executable) - if os.path.isfile(f): - # the file exists, we have a shot at spawn working - return f - return None - else: - return executable diff --git a/src/distutils2/tests/__init__.py b/src/distutils2/tests/__init__.py --- a/src/distutils2/tests/__init__.py +++ b/src/distutils2/tests/__init__.py @@ -45,7 +45,7 @@ """Test failed.""" -class BasicTestRunner: +class BasicTestRunner(object): def run(self, test): result = unittest.TestResult() test(result) diff --git a/src/distutils2/tests/conversions/05_after.py b/src/distutils2/tests/conversions/05_after.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/conversions/05_after.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2003-2009 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://trac.edgewall.org/wiki/TracLicense. +# +# This software consists of voluntary contributions made by many +# individuals. For the exact contribution history, see the revision +# history and logs, available at http://trac.edgewall.org/log/. + +from distutils2.core import setup, find_packages + +extra = {} + +try: + import babel + + extractors = [ + ('**.py', 'python', None), + ('**/templates/**.html', 'genshi', None), + ('**/templates/**.txt', 'genshi', + {'template_class': 'genshi.template:NewTextTemplate'}), + ] + extra['message_extractors'] = { + 'trac': extractors, + 'tracopt': extractors, + } + + from trac.util.dist import get_l10n_js_cmdclass + extra['cmdclass'] = get_l10n_js_cmdclass() + +except ImportError, e: + pass + +setup( + name = 'Trac', + version = '0.12.1', + summary = 'Integrated SCM, wiki, issue tracker and project environment', + description = """ +Trac is a minimalistic web-based software project management and bug/issue +tracking system. It provides an interface to the Subversion revision control +systems, an integrated wiki, flexible issue tracking and convenient report +facilities. +""", + author = 'Edgewall Software', + author_email = 'info at edgewall.com', + license = 'BSD', + home_page = 'http://trac.edgewall.org/', + download_url = 'http://trac.edgewall.org/wiki/TracDownload', + classifiers = [ + 'Environment :: Web Environment', + 'Framework :: Trac', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Software Development :: Bug Tracking', + 'Topic :: Software Development :: Version Control', + ], + + packages = find_packages(exclude=['*.tests']), + package_data = { + '': ['templates/*'], + 'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*', + 'htdocs/js/messages/*.*', 'htdocs/css/*.*', + 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'], + 'trac.wiki': ['default-pages/*'], + 'trac.ticket': ['workflows/*.ini'], + }, + + test_suite = 'trac.test.suite', + zip_safe = True, + + requires_dist = [ + 'setuptools>=0.6b1', + 'Genshi>=0.6', + ], + extras_require = { + 'Babel': ['Babel>=0.9.5'], + 'Pygments': ['Pygments>=0.6'], + 'reST': ['docutils>=0.3'], + 'SilverCity': ['SilverCity>=0.9.4'], + 'Textile': ['textile>=2.0'], + }, + + entry_points = """ + [console_scripts] + trac-admin = trac.admin.console:run + tracd = trac.web.standalone:main + + [trac.plugins] + trac.about = trac.about + trac.admin.console = trac.admin.console + trac.admin.web_ui = trac.admin.web_ui + trac.attachment = trac.attachment + trac.db.mysql = trac.db.mysql_backend + trac.db.postgres = trac.db.postgres_backend + trac.db.sqlite = trac.db.sqlite_backend + trac.mimeview.patch = trac.mimeview.patch + trac.mimeview.pygments = trac.mimeview.pygments[Pygments] + trac.mimeview.rst = trac.mimeview.rst[reST] + trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity] + trac.mimeview.txtl = trac.mimeview.txtl[Textile] + trac.prefs = trac.prefs.web_ui + trac.search = trac.search.web_ui + trac.ticket.admin = trac.ticket.admin + trac.ticket.query = trac.ticket.query + trac.ticket.report = trac.ticket.report + trac.ticket.roadmap = trac.ticket.roadmap + trac.ticket.web_ui = trac.ticket.web_ui + trac.timeline = trac.timeline.web_ui + trac.versioncontrol.admin = trac.versioncontrol.admin + trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz + trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs + trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop + trac.versioncontrol.web_ui = trac.versioncontrol.web_ui + trac.web.auth = trac.web.auth + trac.web.session = trac.web.session + trac.wiki.admin = trac.wiki.admin + trac.wiki.interwiki = trac.wiki.interwiki + trac.wiki.macros = trac.wiki.macros + trac.wiki.web_ui = trac.wiki.web_ui + trac.wiki.web_api = trac.wiki.web_api + tracopt.mimeview.enscript = tracopt.mimeview.enscript + tracopt.mimeview.php = tracopt.mimeview.php + tracopt.perm.authz_policy = tracopt.perm.authz_policy + tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider + tracopt.ticket.commit_updater = tracopt.ticket.commit_updater + tracopt.ticket.deleter = tracopt.ticket.deleter + """, + + **extra +) diff --git a/src/distutils2/tests/conversions/05_before.py b/src/distutils2/tests/conversions/05_before.py new file mode 100755 --- /dev/null +++ b/src/distutils2/tests/conversions/05_before.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2003-2009 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://trac.edgewall.org/wiki/TracLicense. +# +# This software consists of voluntary contributions made by many +# individuals. For the exact contribution history, see the revision +# history and logs, available at http://trac.edgewall.org/log/. + +from setuptools import setup, find_packages + +extra = {} + +try: + import babel + + extractors = [ + ('**.py', 'python', None), + ('**/templates/**.html', 'genshi', None), + ('**/templates/**.txt', 'genshi', + {'template_class': 'genshi.template:NewTextTemplate'}), + ] + extra['message_extractors'] = { + 'trac': extractors, + 'tracopt': extractors, + } + + from trac.util.dist import get_l10n_js_cmdclass + extra['cmdclass'] = get_l10n_js_cmdclass() + +except ImportError, e: + pass + +setup( + name = 'Trac', + version = '0.12.1', + description = 'Integrated SCM, wiki, issue tracker and project environment', + long_description = """ +Trac is a minimalistic web-based software project management and bug/issue +tracking system. It provides an interface to the Subversion revision control +systems, an integrated wiki, flexible issue tracking and convenient report +facilities. +""", + author = 'Edgewall Software', + author_email = 'info at edgewall.com', + license = 'BSD', + url = 'http://trac.edgewall.org/', + download_url = 'http://trac.edgewall.org/wiki/TracDownload', + classifiers = [ + 'Environment :: Web Environment', + 'Framework :: Trac', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Software Development :: Bug Tracking', + 'Topic :: Software Development :: Version Control', + ], + + packages = find_packages(exclude=['*.tests']), + package_data = { + '': ['templates/*'], + 'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*', + 'htdocs/js/messages/*.*', 'htdocs/css/*.*', + 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'], + 'trac.wiki': ['default-pages/*'], + 'trac.ticket': ['workflows/*.ini'], + }, + + test_suite = 'trac.test.suite', + zip_safe = True, + + install_requires = [ + 'setuptools>=0.6b1', + 'Genshi>=0.6', + ], + extras_require = { + 'Babel': ['Babel>=0.9.5'], + 'Pygments': ['Pygments>=0.6'], + 'reST': ['docutils>=0.3'], + 'SilverCity': ['SilverCity>=0.9.4'], + 'Textile': ['textile>=2.0'], + }, + + entry_points = """ + [console_scripts] + trac-admin = trac.admin.console:run + tracd = trac.web.standalone:main + + [trac.plugins] + trac.about = trac.about + trac.admin.console = trac.admin.console + trac.admin.web_ui = trac.admin.web_ui + trac.attachment = trac.attachment + trac.db.mysql = trac.db.mysql_backend + trac.db.postgres = trac.db.postgres_backend + trac.db.sqlite = trac.db.sqlite_backend + trac.mimeview.patch = trac.mimeview.patch + trac.mimeview.pygments = trac.mimeview.pygments[Pygments] + trac.mimeview.rst = trac.mimeview.rst[reST] + trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity] + trac.mimeview.txtl = trac.mimeview.txtl[Textile] + trac.prefs = trac.prefs.web_ui + trac.search = trac.search.web_ui + trac.ticket.admin = trac.ticket.admin + trac.ticket.query = trac.ticket.query + trac.ticket.report = trac.ticket.report + trac.ticket.roadmap = trac.ticket.roadmap + trac.ticket.web_ui = trac.ticket.web_ui + trac.timeline = trac.timeline.web_ui + trac.versioncontrol.admin = trac.versioncontrol.admin + trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz + trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs + trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop + trac.versioncontrol.web_ui = trac.versioncontrol.web_ui + trac.web.auth = trac.web.auth + trac.web.session = trac.web.session + trac.wiki.admin = trac.wiki.admin + trac.wiki.interwiki = trac.wiki.interwiki + trac.wiki.macros = trac.wiki.macros + trac.wiki.web_ui = trac.wiki.web_ui + trac.wiki.web_api = trac.wiki.web_api + tracopt.mimeview.enscript = tracopt.mimeview.enscript + tracopt.mimeview.php = tracopt.mimeview.php + tracopt.perm.authz_policy = tracopt.perm.authz_policy + tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider + tracopt.ticket.commit_updater = tracopt.ticket.commit_updater + tracopt.ticket.deleter = tracopt.ticket.deleter + """, + + **extra +) diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py --- a/src/distutils2/tests/pypi_server.py +++ b/src/distutils2/tests/pypi_server.py @@ -26,10 +26,12 @@ def wrapped(*args, **kwargs): server = PyPIServer(*server_args, **server_kwargs) server.start() - func(server=server, *args, **kwargs) - server.stop() + try: + func(server=server, *args, **kwargs) + finally: + server.stop() return wrapped - return wrapper + return wrapper class PyPIServerTestCase(unittest.TestCase): @@ -50,7 +52,7 @@ """ def __init__(self, test_static_path=None, - static_filesystem_paths=["default"], static_uri_paths=["simple"]): + static_filesystem_paths=["default"], static_uri_paths=["simple"]): """Initialize the server. static_uri_paths and static_base_path are parameters used to provides @@ -59,7 +61,7 @@ """ threading.Thread.__init__(self) self._run = True - self.httpd = HTTPServer(('', 0), PyPIRequestHandler) + self.httpd = HTTPServer(('', 0), PyPIRequestHandler) self.httpd.RequestHandlerClass.log_request = lambda *_: None self.httpd.RequestHandlerClass.pypi_server = self self.address = (self.httpd.server_name, self.httpd.server_port) @@ -131,7 +133,7 @@ """ # record the request. Read the input only on PUT or POST requests if self.command in ("PUT", "POST"): - if self.headers.dict.has_key("content-length"): + if 'content-length' in self.headers.dict: request_data = self.rfile.read( int(self.headers['content-length'])) else: @@ -158,7 +160,11 @@ relative_path += "index.html" file = open(fs_path + relative_path) data = file.read() - self.make_response(data) + if relative_path.endswith('.tar.gz'): + headers=[('Content-type', 'application/x-gtar')] + else: + headers=[('Content-type', 'text/html')] + self.make_response(data, headers=headers) except IOError: pass @@ -171,8 +177,8 @@ status, headers, data = self.pypi_server.get_next_response() self.make_response(data, status, headers) - def make_response(self, data, status=200, - headers=[('Content-type', 'text/html')]): + def make_response(self, data, status=200, + headers=[('Content-type', 'text/html')]): """Send the response to the HTTP client""" if not isinstance(status, int): try: @@ -180,7 +186,7 @@ except ValueError: # we probably got something like YYY Codename. # Just get the first 3 digits - status = int(status[:3]) + status = int(status[:3]) self.send_response(status) for header, value in headers: diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz new file mode 100644 diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html @@ -0,0 +1,3 @@ + +badmd5-0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/foobar-0.1.tar.gz b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/foobar-0.1.tar.gz new file mode 100644 diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html @@ -0,0 +1,3 @@ + +foobar-0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/index.html b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/index.html @@ -0,0 +1,2 @@ +foobar/ +badmd5/ diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/bar/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/bar/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/bar/index.html @@ -0,0 +1,6 @@ +Links for bar

Links for bar

+bar-1.0.tar.gz
+bar-1.0.1.tar.gz
+bar-2.0.tar.gz
+bar-2.0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/baz/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/baz/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/baz/index.html @@ -0,0 +1,6 @@ +Links for baz

Links for baz

+baz-1.0.tar.gz
+baz-1.0.1.tar.gz
+baz-2.0.tar.gz
+baz-2.0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/foo/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/foo/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/foo/index.html @@ -0,0 +1,6 @@ +Links for foo

Links for foo

+foo-1.0.tar.gz
+foo-1.0.1.tar.gz
+foo-2.0.tar.gz
+foo-2.0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/index.html @@ -0,0 +1,3 @@ +foo/ +bar/ +baz/ diff --git a/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html b/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html --- a/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html +++ b/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html @@ -1,6 +1,6 @@ Links for Foobar

Links for Foobar

-Foobar-1.0.tar.gz
-Foobar-1.0.1.tar.gz
-Foobar-2.0.tar.gz
-Foobar-2.0.1.tar.gz
+Foobar-1.0.tar.gz
+Foobar-1.0.1.tar.gz
+Foobar-2.0.tar.gz
+Foobar-2.0.1.tar.gz
diff --git a/src/distutils2/tests/pypiserver/test_link_priority/external/external.html b/src/distutils2/tests/pypiserver/with_externals/external/external.html rename from src/distutils2/tests/pypiserver/test_link_priority/external/external.html rename to src/distutils2/tests/pypiserver/with_externals/external/external.html diff --git a/src/distutils2/tests/pypiserver/test_link_priority/simple/foobar/index.html b/src/distutils2/tests/pypiserver/with_externals/simple/foobar/index.html rename from src/distutils2/tests/pypiserver/test_link_priority/simple/foobar/index.html rename to src/distutils2/tests/pypiserver/with_externals/simple/foobar/index.html --- a/src/distutils2/tests/pypiserver/test_link_priority/simple/foobar/index.html +++ b/src/distutils2/tests/pypiserver/with_externals/simple/foobar/index.html @@ -1,4 +1,4 @@ -foobar-0.1.tar.gz
+foobar-0.1.tar.gz
external homepage
diff --git a/src/distutils2/tests/pypiserver/test_link_priority/simple/index.html b/src/distutils2/tests/pypiserver/with_externals/simple/index.html rename from src/distutils2/tests/pypiserver/test_link_priority/simple/index.html rename to src/distutils2/tests/pypiserver/with_externals/simple/index.html diff --git a/src/distutils2/tests/pypiserver/with_norel_links/external/homepage.html b/src/distutils2/tests/pypiserver/with_norel_links/external/homepage.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_norel_links/external/homepage.html @@ -0,0 +1,7 @@ + + +

a rel=homepage HTML page

+foobar 2.0 + + + diff --git a/src/distutils2/tests/pypiserver/with_norel_links/external/nonrel.html b/src/distutils2/tests/pypiserver/with_norel_links/external/nonrel.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_norel_links/external/nonrel.html @@ -0,0 +1,1 @@ +A page linked without rel="download" or rel="homepage" link. diff --git a/src/distutils2/tests/pypiserver/with_norel_links/simple/foobar/index.html b/src/distutils2/tests/pypiserver/with_norel_links/simple/foobar/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_norel_links/simple/foobar/index.html @@ -0,0 +1,6 @@ + +foobar-0.1.tar.gz
+external homepage
+unrelated link
+unrelated download
+ diff --git a/src/distutils2/tests/pypiserver/with_norel_links/simple/index.html b/src/distutils2/tests/pypiserver/with_norel_links/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_norel_links/simple/index.html @@ -0,0 +1,1 @@ +foobar/ diff --git a/src/distutils2/tests/pypiserver/with_real_externals/simple/foobar/index.html b/src/distutils2/tests/pypiserver/with_real_externals/simple/foobar/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_real_externals/simple/foobar/index.html @@ -0,0 +1,4 @@ + +foobar-0.1.tar.gz
+external homepage
+ diff --git a/src/distutils2/tests/pypiserver/with_real_externals/simple/index.html b/src/distutils2/tests/pypiserver/with_real_externals/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_real_externals/simple/index.html @@ -0,0 +1,1 @@ +foobar/ diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -14,7 +14,6 @@ from distutils2 import log from distutils2.log import DEBUG, INFO, WARN, ERROR, FATAL -from distutils2.core import Distribution if sys.version_info >= (2, 7): # improved unittest package from 2.7's standard library @@ -42,7 +41,7 @@ def _log(self, level, msg, args): if level not in (DEBUG, INFO, WARN, ERROR, FATAL): - raise ValueError('%s wrong log level' % str(level)) + raise ValueError('%s wrong log level' % level) self.logs.append((level, msg, args)) def get_logs(self, *levels): @@ -65,12 +64,22 @@ def setUp(self): super(TempdirManager, self).setUp() self.tempdirs = [] + self.tempfiles = [] def tearDown(self): super(TempdirManager, self).tearDown() while self.tempdirs: d = self.tempdirs.pop() shutil.rmtree(d, os.name in ('nt', 'cygwin')) + for file_ in self.tempfiles: + if os.path.exists(file_): + os.remove(file_) + + def mktempfile(self): + """Create a temporary file that will be cleaned up.""" + tempfile_ = tempfile.NamedTemporaryFile() + self.tempfiles.append(tempfile_.name) + return tempfile_ def mkdtemp(self): """Create a temporary directory that will be cleaned up. @@ -105,6 +114,7 @@ It returns the package directory and the distribution instance. """ + from distutils2.dist import Distribution tmp_dir = self.mkdtemp() pkg_dir = os.path.join(tmp_dir, pkg_name) os.mkdir(pkg_dir) diff --git a/src/distutils2/tests/test_Mixin2to3.py b/src/distutils2/tests/test_Mixin2to3.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_Mixin2to3.py @@ -0,0 +1,54 @@ +"""Tests for distutils.command.build_py.""" +import sys +import tempfile + +import distutils2 +from distutils2.tests import support +from distutils2.tests.support import unittest +from distutils2.command.build_py import Mixin2to3 + + +class Mixin2to3TestCase(support.TempdirManager, unittest.TestCase): + + @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + def test_convert_code_only(self): + # used to check if code gets converted properly. + code_content = "print 'test'\n" + code_handle = self.mktempfile() + code_name = code_handle.name + + code_handle.write(code_content) + code_handle.flush() + + mixin2to3 = Mixin2to3() + mixin2to3._run_2to3([code_name]) + converted_code_content = "print('test')\n" + new_code_content = "".join(open(code_name).readlines()) + + self.assertEquals(new_code_content, converted_code_content) + + @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + def test_doctests_only(self): + # used to check if doctests gets converted properly. + doctest_content = '"""\n>>> print test\ntest\n"""\nprint test\n\n' + doctest_handle = self.mktempfile() + doctest_name = doctest_handle.name + + doctest_handle.write(doctest_content) + doctest_handle.flush() + + mixin2to3 = Mixin2to3() + mixin2to3._run_2to3([doctest_name]) + + converted_doctest_content = ['"""', '>>> print(test)', 'test', '"""', + 'print(test)', '', '', ''] + converted_doctest_content = '\n'.join(converted_doctest_content) + new_doctest_content = "".join(open(doctest_name).readlines()) + + self.assertEquals(new_doctest_content, converted_doctest_content) + +def test_suite(): + return unittest.makeSuite(Mixin2to3TestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_bdist.py b/src/distutils2/tests/test_bdist.py --- a/src/distutils2/tests/test_bdist.py +++ b/src/distutils2/tests/test_bdist.py @@ -1,8 +1,6 @@ """Tests for distutils.command.bdist.""" import sys import os -import tempfile -import shutil from distutils2.tests import run_unittest @@ -10,8 +8,7 @@ from distutils2.command.bdist import bdist from distutils2.tests import support from distutils2.tests.support import unittest -from distutils2.spawn import find_executable -from distutils2 import spawn +from distutils2.util import find_executable from distutils2.errors import DistutilsExecError class BuildTestCase(support.TempdirManager, @@ -25,7 +22,7 @@ cmd = bdist(dist) cmd.formats = ['msi'] cmd.ensure_finalized() - self.assertEquals(cmd.formats, ['msi']) + self.assertEqual(cmd.formats, ['msi']) # what format bdist offers ? # XXX an explicit list in bdist is @@ -33,9 +30,9 @@ # we should add a registry formats = ['zip', 'gztar', 'bztar', 'ztar', 'tar', 'wininst', 'msi'] formats.sort() - founded = cmd.format_command.keys() - founded.sort() - self.assertEquals(founded, formats) + found = cmd.format_command.keys() + found.sort() + self.assertEqual(found, formats) def test_suite(): return unittest.makeSuite(BuildTestCase) diff --git a/src/distutils2/tests/test_bdist_dumb.py b/src/distutils2/tests/test_bdist_dumb.py --- a/src/distutils2/tests/test_bdist_dumb.py +++ b/src/distutils2/tests/test_bdist_dumb.py @@ -78,7 +78,7 @@ base = base.replace(':', '-') wanted = ['%s.zip' % base] - self.assertEquals(dist_created, wanted) + self.assertEqual(dist_created, wanted) # now let's check what we have in the zip file # XXX to be done @@ -87,16 +87,16 @@ pkg_dir, dist = self.create_dist() os.chdir(pkg_dir) cmd = bdist_dumb(dist) - self.assertEquals(cmd.bdist_dir, None) + self.assertEqual(cmd.bdist_dir, None) cmd.finalize_options() # bdist_dir is initialized to bdist_base/dumb if not set base = cmd.get_finalized_command('bdist').bdist_base - self.assertEquals(cmd.bdist_dir, os.path.join(base, 'dumb')) + self.assertEqual(cmd.bdist_dir, os.path.join(base, 'dumb')) # the format is set to a default value depending on the os.name default = cmd.default_format[os.name] - self.assertEquals(cmd.format, default) + self.assertEqual(cmd.format, default) def test_suite(): return unittest.makeSuite(BuildDumbTestCase) diff --git a/src/distutils2/tests/test_build.py b/src/distutils2/tests/test_build.py --- a/src/distutils2/tests/test_build.py +++ b/src/distutils2/tests/test_build.py @@ -20,11 +20,11 @@ cmd.finalize_options() # if not specified, plat_name gets the current platform - self.assertEquals(cmd.plat_name, get_platform()) + self.assertEqual(cmd.plat_name, get_platform()) # build_purelib is build + lib wanted = os.path.join(cmd.build_base, 'lib') - self.assertEquals(cmd.build_purelib, wanted) + self.assertEqual(cmd.build_purelib, wanted) # build_platlib is 'build/lib.platform-x.x[-pydebug]' # examples: @@ -34,21 +34,21 @@ self.assertTrue(cmd.build_platlib.endswith('-pydebug')) plat_spec += '-pydebug' wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) - self.assertEquals(cmd.build_platlib, wanted) + self.assertEqual(cmd.build_platlib, wanted) # by default, build_lib = build_purelib - self.assertEquals(cmd.build_lib, cmd.build_purelib) + self.assertEqual(cmd.build_lib, cmd.build_purelib) # build_temp is build/temp. wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) - self.assertEquals(cmd.build_temp, wanted) + self.assertEqual(cmd.build_temp, wanted) # build_scripts is build/scripts-x.x wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3]) - self.assertEquals(cmd.build_scripts, wanted) + self.assertEqual(cmd.build_scripts, wanted) # executable is os.path.normpath(sys.executable) - self.assertEquals(cmd.executable, os.path.normpath(sys.executable)) + self.assertEqual(cmd.executable, os.path.normpath(sys.executable)) def test_suite(): return unittest.makeSuite(BuildTestCase) diff --git a/src/distutils2/tests/test_build_clib.py b/src/distutils2/tests/test_build_clib.py --- a/src/distutils2/tests/test_build_clib.py +++ b/src/distutils2/tests/test_build_clib.py @@ -5,7 +5,7 @@ from distutils2.command.build_clib import build_clib from distutils2.errors import DistutilsSetupError from distutils2.tests import support -from distutils2.spawn import find_executable +from distutils2.util import find_executable from distutils2.tests.support import unittest class BuildCLibTestCase(support.TempdirManager, @@ -55,14 +55,14 @@ self.assertRaises(DistutilsSetupError, cmd.get_source_files) cmd.libraries = [('name', {'sources': ['a', 'b']})] - self.assertEquals(cmd.get_source_files(), ['a', 'b']) + self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')})] - self.assertEquals(cmd.get_source_files(), ['a', 'b']) + self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')}), ('name2', {'sources': ['c', 'd']})] - self.assertEquals(cmd.get_source_files(), ['a', 'b', 'c', 'd']) + self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd']) def test_build_libraries(self): @@ -91,11 +91,11 @@ cmd.include_dirs = 'one-dir' cmd.finalize_options() - self.assertEquals(cmd.include_dirs, ['one-dir']) + self.assertEqual(cmd.include_dirs, ['one-dir']) cmd.include_dirs = None cmd.finalize_options() - self.assertEquals(cmd.include_dirs, []) + self.assertEqual(cmd.include_dirs, []) cmd.distribution.libraries = 'WONTWORK' self.assertRaises(DistutilsSetupError, cmd.finalize_options) diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py --- a/src/distutils2/tests/test_build_ext.py +++ b/src/distutils2/tests/test_build_ext.py @@ -1,6 +1,5 @@ import sys import os -import tempfile import shutil from StringIO import StringIO import warnings @@ -81,11 +80,11 @@ for attr in ('error', 'foo', 'new', 'roj'): self.assertTrue(hasattr(xx, attr)) - self.assertEquals(xx.foo(2, 5), 7) - self.assertEquals(xx.foo(13,15), 28) - self.assertEquals(xx.new().demo(), None) + self.assertEqual(xx.foo(2, 5), 7) + self.assertEqual(xx.foo(13,15), 28) + self.assertEqual(xx.new().demo(), None) doc = 'This is a template module just for instruction.' - self.assertEquals(xx.__doc__, doc) + self.assertEqual(xx.__doc__, doc) self.assertTrue(isinstance(xx.Null(), xx.Null)) self.assertTrue(isinstance(xx.Str(), xx.Str)) @@ -195,7 +194,7 @@ cmd = build_ext(dist) cmd.libraries = 'my_lib' cmd.finalize_options() - self.assertEquals(cmd.libraries, ['my_lib']) + self.assertEqual(cmd.libraries, ['my_lib']) # make sure cmd.library_dirs is turned into a list # if it's a string @@ -209,7 +208,7 @@ cmd = build_ext(dist) cmd.rpath = os.pathsep.join(['one', 'two']) cmd.finalize_options() - self.assertEquals(cmd.rpath, ['one', 'two']) + self.assertEqual(cmd.rpath, ['one', 'two']) # XXX more tests to perform for win32 @@ -218,79 +217,32 @@ cmd = build_ext(dist) cmd.define = 'one,two' cmd.finalize_options() - self.assertEquals(cmd.define, [('one', '1'), ('two', '1')]) + self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) # make sure undef is turned into a list of # strings if they are ','-separated strings cmd = build_ext(dist) cmd.undef = 'one,two' cmd.finalize_options() - self.assertEquals(cmd.undef, ['one', 'two']) + self.assertEqual(cmd.undef, ['one', 'two']) # make sure swig_opts is turned into a list cmd = build_ext(dist) cmd.swig_opts = None cmd.finalize_options() - self.assertEquals(cmd.swig_opts, []) + self.assertEqual(cmd.swig_opts, []) cmd = build_ext(dist) cmd.swig_opts = '1 2' cmd.finalize_options() - self.assertEquals(cmd.swig_opts, ['1', '2']) - - def test_check_extensions_list(self): - dist = Distribution() - cmd = build_ext(dist) - cmd.finalize_options() - - #'extensions' option must be a list of Extension instances - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo') - - # each element of 'ext_modules' option must be an - # Extension instance or 2-tuple - exts = [('bar', 'foo', 'bar'), 'foo'] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # first element of each tuple in 'ext_modules' - # must be the extension name (a string) and match - # a python dotted-separated name - exts = [('foo-bar', '')] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # second element of each tuple in 'ext_modules' - # must be a ary (build info) - exts = [('foo.bar', '')] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # ok this one should pass - exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', - 'some': 'bar'})] - cmd.check_extensions_list(exts) - ext = exts[0] - self.assertTrue(isinstance(ext, Extension)) - - # check_extensions_list adds in ext the values passed - # when they are in ('include_dirs', 'library_dirs', 'libraries' - # 'extra_objects', 'extra_compile_args', 'extra_link_args') - self.assertEquals(ext.libraries, 'foo') - self.assertTrue(not hasattr(ext, 'some')) - - # 'macros' element of build info dict must be 1- or 2-tuple - exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', - 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - exts[0][1]['macros'] = [('1', '2'), ('3',)] - cmd.check_extensions_list(exts) - self.assertEquals(exts[0].undef_macros, ['3']) - self.assertEquals(exts[0].define_macros, [('1', '2')]) + self.assertEqual(cmd.swig_opts, ['1', '2']) def test_get_source_files(self): modules = [Extension('foo', ['xxx'], optional=False)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = build_ext(dist) cmd.ensure_finalized() - self.assertEquals(cmd.get_source_files(), ['xxx']) + self.assertEqual(cmd.get_source_files(), ['xxx']) def test_compiler_option(self): # cmd.compiler is an option and @@ -301,7 +253,7 @@ cmd.compiler = 'unix' cmd.ensure_finalized() cmd.run() - self.assertEquals(cmd.compiler, 'unix') + self.assertEqual(cmd.compiler, 'unix') def test_get_outputs(self): tmp_dir = self.mkdtemp() @@ -312,7 +264,7 @@ 'ext_modules': [ext]}) cmd = build_ext(dist) cmd.ensure_finalized() - self.assertEquals(len(cmd.get_outputs()), 1) + self.assertEqual(len(cmd.get_outputs()), 1) if os.name == "nt": cmd.debug = sys.executable.endswith("_d.exe") @@ -332,19 +284,19 @@ finally: os.chdir(old_wd) self.assertTrue(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], + self.assertEqual(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, other_tmp_dir) + self.assertEqual(so_dir, other_tmp_dir) cmd.inplace = 0 cmd.run() so_file = cmd.get_outputs()[0] self.assertTrue(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], + self.assertEqual(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, cmd.build_lib) + self.assertEqual(so_dir, cmd.build_lib) # inplace = 0, cmd.package = 'bar' build_py = cmd.get_finalized_command('build_py') @@ -352,7 +304,7 @@ path = cmd.get_ext_fullpath('foo') # checking that the last directory is the build_dir path = os.path.split(path)[0] - self.assertEquals(path, cmd.build_lib) + self.assertEqual(path, cmd.build_lib) # inplace = 1, cmd.package = 'bar' cmd.inplace = 1 @@ -366,7 +318,7 @@ # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, 'bar') + self.assertEqual(lastdir, 'bar') def test_ext_fullpath(self): ext = sysconfig.get_config_vars()['SO'] @@ -382,14 +334,14 @@ curdir = os.getcwd() wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building lxml.etree not inplace cmd.inplace = 0 cmd.build_lib = os.path.join(curdir, 'tmpdir') wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building twisted.runner.portmap not inplace build_py = cmd.get_finalized_command('build_py') @@ -398,13 +350,13 @@ path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', 'portmap' + ext) - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building twisted.runner.portmap inplace cmd.inplace = 1 path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) def test_suite(): src = _get_source_filename() @@ -416,4 +368,4 @@ else: return unittest.makeSuite(BuildExtTestCase) if __name__ == '__main__': - distsutils2.tests.run_unittest(test_suite()) + distutils2.tests.run_unittest(test_suite()) diff --git a/src/distutils2/tests/test_build_py.py b/src/distutils2/tests/test_build_py.py --- a/src/distutils2/tests/test_build_py.py +++ b/src/distutils2/tests/test_build_py.py @@ -19,11 +19,15 @@ def test_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") - f.write("# Pretend this is a package.") - f.close() + try: + f.write("# Pretend this is a package.") + finally: + f.close() f = open(os.path.join(sources, "README.txt"), "w") - f.write("Info about this package") - f.close() + try: + f.write("Info about this package") + finally: + f.close() destination = self.mkdtemp() diff --git a/src/distutils2/tests/test_build_scripts.py b/src/distutils2/tests/test_build_scripts.py --- a/src/distutils2/tests/test_build_scripts.py +++ b/src/distutils2/tests/test_build_scripts.py @@ -74,8 +74,10 @@ def write_script(self, dir, name, text): f = open(os.path.join(dir, name), "w") - f.write(text) - f.close() + try: + f.write(text) + finally: + f.close() def test_version_int(self): source = self.mkdtemp() diff --git a/src/distutils2/tests/test_ccompiler.py b/src/distutils2/tests/test_ccompiler.py --- a/src/distutils2/tests/test_ccompiler.py +++ b/src/distutils2/tests/test_ccompiler.py @@ -31,7 +31,7 @@ opts = gen_lib_options(compiler, libdirs, runlibdirs, libs) wanted = ['-Llib1', '-Llib2', '-cool', '-Rrunlib1', 'found', '-lname2'] - self.assertEquals(opts, wanted) + self.assertEqual(opts, wanted) def test_customize_compiler(self): @@ -51,7 +51,7 @@ comp = compiler() customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') + self.assertEqual(comp.exes['archiver'], 'my_ar -arflags') def test_suite(): return unittest.makeSuite(CCompilerTestCase) diff --git a/src/distutils2/tests/test_check.py b/src/distutils2/tests/test_check.py --- a/src/distutils2/tests/test_check.py +++ b/src/distutils2/tests/test_check.py @@ -37,7 +37,7 @@ 'name': 'xxx', 'version': 'xxx' } cmd = self._run(metadata) - self.assertEquals(len(cmd._warnings), 0) + self.assertEqual(len(cmd._warnings), 0) # now with the strict mode, we should # get an error if there are missing metadata @@ -45,7 +45,7 @@ # and of course, no error when all metadata are present cmd = self._run(metadata, strict=1) - self.assertEquals(len(cmd._warnings), 0) + self.assertEqual(len(cmd._warnings), 0) def test_check_restructuredtext(self): if not _HAS_DOCUTILS: # won't test without docutils @@ -55,7 +55,7 @@ pkg_info, dist = self.create_dist(description=broken_rest) cmd = check(dist) cmd.check_restructuredtext() - self.assertEquals(len(cmd._warnings), 1) + self.assertEqual(len(cmd._warnings), 1) # let's see if we have an error with strict=1 metadata = {'home_page': 'xxx', 'author': 'xxx', @@ -69,7 +69,7 @@ # and non-broken rest metadata['description'] = 'title\n=====\n\ntest' cmd = self._run(metadata, strict=1, restructuredtext=1) - self.assertEquals(len(cmd._warnings), 0) + self.assertEqual(len(cmd._warnings), 0) def test_check_all(self): diff --git a/src/distutils2/tests/test_cmd.py b/src/distutils2/tests/test_cmd.py --- a/src/distutils2/tests/test_cmd.py +++ b/src/distutils2/tests/test_cmd.py @@ -43,7 +43,7 @@ # making sure execute gets called properly def _execute(func, args, exec_msg, level): - self.assertEquals(exec_msg, 'generating out from in') + self.assertEqual(exec_msg, 'generating out from in') cmd.force = True cmd.execute = _execute cmd.make_file(infiles='in', outfile='out', func='func', args=()) @@ -62,7 +62,7 @@ wanted = ["command options for 'MyCmd':", ' option1 = 1', ' option2 = 1'] - self.assertEquals(msgs, wanted) + self.assertEqual(msgs, wanted) def test_ensure_string(self): cmd = self.cmd @@ -80,7 +80,7 @@ cmd = self.cmd cmd.option1 = 'ok,dok' cmd.ensure_string_list('option1') - self.assertEquals(cmd.option1, ['ok', 'dok']) + self.assertEqual(cmd.option1, ['ok', 'dok']) cmd.option2 = ['xxx', 'www'] cmd.ensure_string_list('option2') diff --git a/src/distutils2/tests/test_config.py b/src/distutils2/tests/test_config.py --- a/src/distutils2/tests/test_config.py +++ b/src/distutils2/tests/test_config.py @@ -1,7 +1,6 @@ """Tests for distutils.pypirc.pypirc.""" import sys import os -import tempfile import shutil from distutils2.core import PyPIRCCommand @@ -87,20 +86,20 @@ config = config.items() config.sort() - waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi'), - ('server', 'server1'), ('username', 'me')] - self.assertEquals(config, waited) + expected = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server1'), ('username', 'me')] + self.assertEqual(config, expected) # old format self.write_file(self.rc, PYPIRC_OLD) config = cmd._read_pypirc() config = config.items() config.sort() - waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi'), - ('server', 'server-login'), ('username', 'tarek')] - self.assertEquals(config, waited) + expected = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server-login'), ('username', 'tarek')] + self.assertEqual(config, expected) def test_server_empty_registration(self): cmd = self._cmd(self.dist) @@ -109,7 +108,7 @@ cmd._store_pypirc('tarek', 'xxx') self.assertTrue(os.path.exists(rc)) content = open(rc).read() - self.assertEquals(content, WANTED) + self.assertEqual(content, WANTED) def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) diff --git a/src/distutils2/tests/test_config_cmd.py b/src/distutils2/tests/test_config_cmd.py --- a/src/distutils2/tests/test_config_cmd.py +++ b/src/distutils2/tests/test_config_cmd.py @@ -34,7 +34,7 @@ f.close() dump_file(this_file, 'I am the header') - self.assertEquals(len(self._logs), numlines+1) + self.assertEqual(len(self._logs), numlines+1) def test_search_cpp(self): if sys.platform == 'win32': @@ -44,10 +44,10 @@ # simple pattern searches match = cmd.search_cpp(pattern='xxx', body='// xxx') - self.assertEquals(match, 0) + self.assertEqual(match, 0) match = cmd.search_cpp(pattern='_configtest', body='// xxx') - self.assertEquals(match, 1) + self.assertEqual(match, 1) def test_finalize_options(self): # finalize_options does a bit of transformation @@ -59,9 +59,9 @@ cmd.library_dirs = 'three%sfour' % os.pathsep cmd.ensure_finalized() - self.assertEquals(cmd.include_dirs, ['one', 'two']) - self.assertEquals(cmd.libraries, ['one']) - self.assertEquals(cmd.library_dirs, ['three', 'four']) + self.assertEqual(cmd.include_dirs, ['one', 'two']) + self.assertEqual(cmd.libraries, ['one']) + self.assertEqual(cmd.library_dirs, ['three', 'four']) def test_clean(self): # _clean removes files diff --git a/src/distutils2/tests/test_converter.py b/src/distutils2/tests/test_converter.py --- a/src/distutils2/tests/test_converter.py +++ b/src/distutils2/tests/test_converter.py @@ -30,7 +30,7 @@ wanted = file_.replace('before', 'after') wanted = _read_file(os.path.join(convdir, wanted)) res = ref.refactor_string(original, 'setup.py') - self.assertEquals(str(res), wanted) + self.assertEqual(str(res), wanted) def test_suite(): return unittest.makeSuite(ConverterTestCase) diff --git a/src/distutils2/tests/test_cygwinccompiler.py b/src/distutils2/tests/test_cygwinccompiler.py --- a/src/distutils2/tests/test_cygwinccompiler.py +++ b/src/distutils2/tests/test_cygwinccompiler.py @@ -44,48 +44,48 @@ sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' '4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEquals(check_config_h()[0], CONFIG_H_OK) + self.assertEqual(check_config_h()[0], CONFIG_H_OK) # then it tries to see if it can find "__GNUC__" in pyconfig.h sys.version = 'something without the *CC word' # if the file doesn't exist it returns CONFIG_H_UNCERTAIN - self.assertEquals(check_config_h()[0], CONFIG_H_UNCERTAIN) + self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN) # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK self.write_file(self.python_h, 'xxx') - self.assertEquals(check_config_h()[0], CONFIG_H_NOTOK) + self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK) # and CONFIG_H_OK if __GNUC__ is found self.write_file(self.python_h, 'xxx __GNUC__ xxx') - self.assertEquals(check_config_h()[0], CONFIG_H_OK) + self.assertEqual(check_config_h()[0], CONFIG_H_OK) def test_get_msvcr(self): # none sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEquals(get_msvcr(), None) + self.assertEqual(get_msvcr(), None) # MSVC 7.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1300 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr70']) + self.assertEqual(get_msvcr(), ['msvcr70']) # MSVC 7.1 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1310 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr71']) + self.assertEqual(get_msvcr(), ['msvcr71']) # VS2005 / MSVC 8.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1400 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr80']) + self.assertEqual(get_msvcr(), ['msvcr80']) # VS2008 / MSVC 9.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1500 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr90']) + self.assertEqual(get_msvcr(), ['msvcr90']) # unknown sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' diff --git a/src/distutils2/tests/test_depgraph.py b/src/distutils2/tests/test_depgraph.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_depgraph.py @@ -0,0 +1,187 @@ +"""Tests for distutils.depgraph """ + +from distutils2.tests import support +from distutils2.tests.support import unittest +from distutils2 import depgraph +from distutils2._backport import pkgutil + +import os +import sys +import re +try: + import cStringIO as StringIO +except ImportError: + import StringIO + +class DepGraphTestCase(support.LoggingSilencer, + unittest.TestCase): + + DISTROS_DIST = ('choxie', 'grammar', 'towel-stuff') + DISTROS_EGG = ('bacon', 'banana', 'strawberry', 'cheese') + EDGE = re.compile( + r'"(?P.*)" -> "(?P.*)" \[label="(?P
+ tal:content="python:getattr(item,prop,None)">
+ tal:content="python:getattr(item,prop,None)">
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
File
u :up to issue
m :publish + mail comments
M :edit review message
j / k :jump to file after / before current file
J / K :jump to next file with a comment after / before current file
Side-by-side diff
i :toggle intra-line diffs
e :expand all comments
c :collapse all comments
s :toggle showing all comments
n / p :next / previous diff chunk or comment
N / P :next / previous comment
<Enter> :respond to / edit current comment
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Issue
u :up to list of issues
m :publish + mail comments
j / k :jump to patch after / before current patch
o / <Enter> :open current patch in side-by-side view
i :open current patch in unified diff view
 
Issue List
j / k :jump to issue after / before current issue
o / <Enter> :open current issue
# :close issue
 
Comment/message editing
<Ctrl> + s :save comment
<Esc> :cancel edit
+
+ + + + + +
({{counter}})
+ + + +
+{%block body%}BODY GOES HERE{%endblock%} +
+{%block popup%}{%endblock%} + +

+

+ +
+
+ RSS Feeds + Recent Issues + {%if user%} + | + My Issues + | + My Reviews + | + My Closed + {%endif%} + {%if issue%} + | + This issue + {%endif%} +
+
{%include "live_revision.html"%}
+
+ + + + + Added: tracker/instances/python-dev/lib/rietveld/templates/branch_edit.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/branch_edit.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/branch_edit.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/branch_new.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/branch_new.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/branch_new.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/diff.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/diff.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/diff.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/diff2.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/diff2.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/diff2.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/diff_navigation.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/diff_navigation.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/diff_navigation.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/draft_message.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/draft_message.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/draft_message.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/edit.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/edit.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/edit.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/feeds ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/feeds Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/feeds \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/file_navigation.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/file_navigation.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/file_navigation.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/inline_comment.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/inline_comment.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/inline_comment.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/issue.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/issue.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/issue.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/issue_base.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/issue_base.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1,127 @@ +{%extends "base.html"%} + +{%block mainmenu%} + Issues + Repositories +{%endblock%} + + +{%block mainmenu2%} + {%if user%} + {%if uploadpy_hint%} + Create Issue + {%else%} + Create Issue + {%endif%} + | + My Issues + | + Recent Issues + | + Starred Issues + {%else%} + Recent Issues + | + Sign in + with your traccer account to create issues and add comments + {%endif%} +{%endblock%} + + +{%block body%} +

+ {%include "issue_star.html"%} +{%if issue.edit_allowed and not issue.closed%} + + + + +{%endif%} + Issue + {{issue.key.id}}: + {{issue.subject}} {%if issue.closed %} (Closed) {%endif%} +

+ + + + + + + + +
+ {%block issue_actions%} +
+ {%if issue.edit_allowed%} + + Edit Issue + + {%else%} + Can't Edit + {%endif%} +
+ {%if user%} + + Publish+Mail Comments + ('m') + {%else%} + Can't Publish+Mail + {%endif%} + {%if last_patchset and first_patch%} +
+ + Start Review + + {%endif%} +
+ {%endblock%} +
+
Created:
+ {{issue.created|timesince}} ago by {{issue.owner|show_user}} +
+
Modified:
+ {{issue.modified|timesince}} ago +
+
Reviewers:
+ {{issue.reviewers|show_users}} +
+
CC:
+ {%nicknames issue.cc%} +
+
SVN Base:
+ {%firstof issue.base%} +
+
Visibility:
+ {%if issue.private%} + Private. Only viewable by reviewers and CCs. + {% else %} + Public. + {%endif%} +
+
+
+ {%block issue_body%}BODY GOES HERE{%endblock%} +
+{%endblock%} + + +{%block popup%} + {%if issue%} + + {%endif%} +{%endblock%} Added: tracker/instances/python-dev/lib/rietveld/templates/issue_heading.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/issue_heading.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/issue_heading.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/issue_row.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/issue_row.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/issue_row.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/issue_star.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/issue_star.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/issue_star.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/live_revision.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/live_revision.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/live_revision.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/mails ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/mails Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/mails \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/new.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/new.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/new.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/patch.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/patch.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/patch.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/patchset.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/patchset.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/patchset.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/publish.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/publish.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/publish.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/repo_new.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/repo_new.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/repo_new.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/repos.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/repos.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/repos.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/repos_base.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/repos_base.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1,15 @@ +{%extends "base.html"%} +{%block mainmenu%} +Issues +Repositories +{%endblock%} + +{%block mainmenu2%} + Repositories and Branches + {%if is_admin%} + | + Initialize Repositories + {%endif%} +{%endblock%} + +{%block body%}BODY GOES HERE{%endblock%} \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/settings.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/settings.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/settings.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/starred.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/starred.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/starred.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/use_uploadpy.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/use_uploadpy.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/use_uploadpy.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/user.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/user.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/user.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/user_popup.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/user_popup.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/user_popup.html \ No newline at end of file Added: tracker/instances/python-dev/lib/rietveld/templates/view_details_select.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/lib/rietveld/templates/view_details_select.html Thu Sep 30 23:07:30 2010 @@ -0,0 +1 @@ +link ../templates_svn/view_details_select.html \ No newline at end of file