first commit
This commit is contained in:
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
build
|
||||
dist
|
||||
tags
|
||||
|
||||
syntax: glob
|
||||
|
||||
*.py?
|
||||
*.so
|
||||
*.sw?
|
||||
*~
|
||||
.DS_Store
|
||||
|
||||
syntax: regexp
|
||||
5
ChangeLog
Normal file
5
ChangeLog
Normal file
@@ -0,0 +1,5 @@
|
||||
----------------
|
||||
2012-09-18 3.2.3
|
||||
----------------
|
||||
|
||||
This was the first release. Roughly equivalent to Python 3.2.3.
|
||||
289
LICENSE
Normal file
289
LICENSE
Normal file
@@ -0,0 +1,289 @@
|
||||
A. HISTORY OF THE SOFTWARE
|
||||
==========================
|
||||
|
||||
Python was created in the early 1990s by Guido van Rossum at Stichting
|
||||
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
|
||||
as a successor of a language called ABC. Guido remains Python's
|
||||
principal author, although it includes many contributions from others.
|
||||
|
||||
In 1995, Guido continued his work on Python at the Corporation for
|
||||
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
|
||||
in Reston, Virginia where he released several versions of the
|
||||
software.
|
||||
|
||||
In May 2000, Guido and the Python core development team moved to
|
||||
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
||||
year, the PythonLabs team moved to Digital Creations (now Zope
|
||||
Corporation, see http://www.zope.com). In 2001, the Python Software
|
||||
Foundation (PSF, see http://www.python.org/psf/) was formed, a
|
||||
non-profit organization created specifically to own Python-related
|
||||
Intellectual Property. Zope Corporation is a sponsoring member of
|
||||
the PSF.
|
||||
|
||||
All Python releases are Open Source (see http://www.opensource.org for
|
||||
the Open Source Definition). Historically, most, but not all, Python
|
||||
releases have also been GPL-compatible; the table below summarizes
|
||||
the various releases.
|
||||
|
||||
Release Derived Year Owner GPL-
|
||||
from compatible? (1)
|
||||
|
||||
0.9.0 thru 1.2 1991-1995 CWI yes
|
||||
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
||||
1.6 1.5.2 2000 CNRI no
|
||||
2.0 1.6 2000 BeOpen.com no
|
||||
1.6.1 1.6 2001 CNRI yes (2)
|
||||
2.1 2.0+1.6.1 2001 PSF no
|
||||
2.0.1 2.0+1.6.1 2001 PSF yes
|
||||
2.1.1 2.1+2.0.1 2001 PSF yes
|
||||
2.2 2.1.1 2001 PSF yes
|
||||
2.1.2 2.1.1 2002 PSF yes
|
||||
2.1.3 2.1.2 2002 PSF yes
|
||||
2.2.1 2.2 2002 PSF yes
|
||||
2.2.2 2.2.1 2002 PSF yes
|
||||
2.2.3 2.2.2 2003 PSF yes
|
||||
2.3 2.2.2 2002-2003 PSF yes
|
||||
2.3.1 2.3 2002-2003 PSF yes
|
||||
2.3.2 2.3.1 2002-2003 PSF yes
|
||||
2.3.3 2.3.2 2002-2003 PSF yes
|
||||
2.3.4 2.3.3 2004 PSF yes
|
||||
2.3.5 2.3.4 2005 PSF yes
|
||||
2.4 2.3 2004 PSF yes
|
||||
2.4.1 2.4 2005 PSF yes
|
||||
2.4.2 2.4.1 2005 PSF yes
|
||||
2.4.3 2.4.2 2006 PSF yes
|
||||
2.4.4 2.4.3 2006 PSF yes
|
||||
2.5 2.4 2006 PSF yes
|
||||
2.5.1 2.5 2007 PSF yes
|
||||
2.5.2 2.5.1 2008 PSF yes
|
||||
2.5.3 2.5.2 2008 PSF yes
|
||||
2.6 2.5 2008 PSF yes
|
||||
2.6.1 2.6 2008 PSF yes
|
||||
2.6.2 2.6.1 2009 PSF yes
|
||||
2.6.3 2.6.2 2009 PSF yes
|
||||
2.6.4 2.6.3 2009 PSF yes
|
||||
2.6.5 2.6.4 2010 PSF yes
|
||||
3.0 2.6 2008 PSF yes
|
||||
3.0.1 3.0 2009 PSF yes
|
||||
3.1 3.0.1 2009 PSF yes
|
||||
3.1.1 3.1 2009 PSF yes
|
||||
3.1.2 3.1.1 2010 PSF yes
|
||||
3.1.3 3.1.2 2010 PSF yes
|
||||
3.1.4 3.1.3 2011 PSF yes
|
||||
3.2 3.1 2011 PSF yes
|
||||
3.2.1 3.2 2011 PSF yes
|
||||
3.2.2 3.2.1 2011 PSF yes
|
||||
3.2.3 3.2.2 2012 PSF yes
|
||||
|
||||
Footnotes:
|
||||
|
||||
(1) GPL-compatible doesn't mean that we're distributing Python under
|
||||
the GPL. All Python licenses, unlike the GPL, let you distribute
|
||||
a modified version without making your changes open source. The
|
||||
GPL-compatible licenses make it possible to combine Python with
|
||||
other software that is released under the GPL; the others don't.
|
||||
|
||||
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
||||
because its license has a choice of law clause. According to
|
||||
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
||||
is "not incompatible" with the GPL.
|
||||
|
||||
Thanks to the many outside volunteers who have worked under Guido's
|
||||
direction to make these releases possible.
|
||||
|
||||
|
||||
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
||||
===============================================================
|
||||
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using this software ("Python") in source or binary form and
|
||||
its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
distribute, and otherwise use Python alone or in any derivative version,
|
||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012 Python Software Foundation; All Rights Reserved" are retained in Python
|
||||
alone or in any derivative version prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python.
|
||||
|
||||
4. PSF is making Python available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
||||
-------------------------------------------
|
||||
|
||||
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
||||
|
||||
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
||||
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
||||
Individual or Organization ("Licensee") accessing and otherwise using
|
||||
this software in source or binary form and its associated
|
||||
documentation ("the Software").
|
||||
|
||||
2. Subject to the terms and conditions of this BeOpen Python License
|
||||
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
||||
royalty-free, world-wide license to reproduce, analyze, test, perform
|
||||
and/or display publicly, prepare derivative works, distribute, and
|
||||
otherwise use the Software alone or in any derivative version,
|
||||
provided, however, that the BeOpen Python License is retained in the
|
||||
Software, alone or in any derivative version prepared by Licensee.
|
||||
|
||||
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
||||
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
||||
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
||||
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
||||
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
5. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
6. This License Agreement shall be governed by and interpreted in all
|
||||
respects by the law of the State of California, excluding conflict of
|
||||
law provisions. Nothing in this License Agreement shall be deemed to
|
||||
create any relationship of agency, partnership, or joint venture
|
||||
between BeOpen and Licensee. This License Agreement does not grant
|
||||
permission to use BeOpen trademarks or trade names in a trademark
|
||||
sense to endorse or promote products or services of Licensee, or any
|
||||
third party. As an exception, the "BeOpen Python" logos available at
|
||||
http://www.pythonlabs.com/logos.html may be used according to the
|
||||
permissions granted on that web page.
|
||||
|
||||
7. By copying, installing or otherwise using the software, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
||||
---------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Corporation for National
|
||||
Research Initiatives, having an office at 1895 Preston White Drive,
|
||||
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
||||
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
||||
source or binary form and its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, CNRI
|
||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||
license to reproduce, analyze, test, perform and/or display publicly,
|
||||
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
||||
alone or in any derivative version, provided, however, that CNRI's
|
||||
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
||||
1995-2001 Corporation for National Research Initiatives; All Rights
|
||||
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
||||
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
||||
Agreement, Licensee may substitute the following text (omitting the
|
||||
quotes): "Python 1.6.1 is made available subject to the terms and
|
||||
conditions in CNRI's License Agreement. This Agreement together with
|
||||
Python 1.6.1 may be located on the Internet using the following
|
||||
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
||||
Agreement may also be obtained from a proxy server on the Internet
|
||||
using the following URL: http://hdl.handle.net/1895.22/1013".
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python 1.6.1.
|
||||
|
||||
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
||||
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. This License Agreement shall be governed by the federal
|
||||
intellectual property law of the United States, including without
|
||||
limitation the federal copyright law, and, to the extent such
|
||||
U.S. federal law does not apply, by the law of the Commonwealth of
|
||||
Virginia, excluding Virginia's conflict of law provisions.
|
||||
Notwithstanding the foregoing, with regard to derivative works based
|
||||
on Python 1.6.1 that incorporate non-separable material that was
|
||||
previously distributed under the GNU General Public License (GPL), the
|
||||
law of the Commonwealth of Virginia shall govern this License
|
||||
Agreement only as to issues arising under or with respect to
|
||||
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
||||
License Agreement shall be deemed to create any relationship of
|
||||
agency, partnership, or joint venture between CNRI and Licensee. This
|
||||
License Agreement does not grant permission to use CNRI trademarks or
|
||||
trade name in a trademark sense to endorse or promote products or
|
||||
services of Licensee, or any third party.
|
||||
|
||||
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
||||
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
||||
bound by the terms and conditions of this License Agreement.
|
||||
|
||||
ACCEPT
|
||||
|
||||
|
||||
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
||||
--------------------------------------------------
|
||||
|
||||
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
||||
The Netherlands. All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose and without fee is hereby granted,
|
||||
provided that the above copyright notice appear in all copies and that
|
||||
both that copyright notice and this permission notice appear in
|
||||
supporting documentation, and that the name of Stichting Mathematisch
|
||||
Centrum or CWI not be used in advertising or publicity pertaining to
|
||||
distribution of the software without specific, written prior
|
||||
permission.
|
||||
|
||||
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
||||
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
12
MANIFEST.in
Normal file
12
MANIFEST.in
Normal file
@@ -0,0 +1,12 @@
|
||||
include *.txt
|
||||
include LICENSE*
|
||||
include setup.py
|
||||
include setup.cfg
|
||||
include ChangeLog
|
||||
include MANIFEST.in
|
||||
|
||||
include *.py
|
||||
|
||||
prune build
|
||||
prune dist
|
||||
prune .git*
|
||||
10
README.txt
Normal file
10
README.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
This is a backport of the Python 3.2 functools module for use on
|
||||
Python versions 2.4 through 2.7.
|
||||
|
||||
Refer to the Python 3.2 documentation for usage information:
|
||||
http://docs.python.org/3.2/library/functools.html
|
||||
|
||||
Bugs? Try to reproduce them on the latest Python 3.2.x itself and file bug
|
||||
reports on http://bugs.python.org/.
|
||||
|
||||
-- ENDOH takanao djmchl@gmail.com
|
||||
208
functools32.py
Normal file
208
functools32.py
Normal file
@@ -0,0 +1,208 @@
|
||||
"""functools.py - Tools for working with functions and callable objects
|
||||
"""
|
||||
# Python module wrapper for _functools C module
|
||||
# to allow utilities written in Python to be added
|
||||
# to the functools module.
|
||||
# Written by Nick Coghlan <ncoghlan at gmail.com>
|
||||
# and Raymond Hettinger <python at rcn.com>
|
||||
# Copyright (C) 2006-2010 Python Software Foundation.
|
||||
# See C source code for _functools credits/copyright
|
||||
|
||||
__all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES',
|
||||
'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial']
|
||||
|
||||
from _functools import partial, reduce
|
||||
from collections import OrderedDict, namedtuple
|
||||
try:
|
||||
from _thread import allocate_lock as Lock
|
||||
except:
|
||||
from _dummy_thread import allocate_lock as Lock
|
||||
|
||||
# update_wrapper() and wraps() are tools to help write
|
||||
# wrapper functions that can handle naive introspection
|
||||
|
||||
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__', '__annotations__')
|
||||
WRAPPER_UPDATES = ('__dict__',)
|
||||
def update_wrapper(wrapper,
|
||||
wrapped,
|
||||
assigned = WRAPPER_ASSIGNMENTS,
|
||||
updated = WRAPPER_UPDATES):
|
||||
"""Update a wrapper function to look like the wrapped function
|
||||
|
||||
wrapper is the function to be updated
|
||||
wrapped is the original function
|
||||
assigned is a tuple naming the attributes assigned directly
|
||||
from the wrapped function to the wrapper function (defaults to
|
||||
functools.WRAPPER_ASSIGNMENTS)
|
||||
updated is a tuple naming the attributes of the wrapper that
|
||||
are updated with the corresponding attribute from the wrapped
|
||||
function (defaults to functools.WRAPPER_UPDATES)
|
||||
"""
|
||||
wrapper.__wrapped__ = wrapped
|
||||
for attr in assigned:
|
||||
try:
|
||||
value = getattr(wrapped, attr)
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
setattr(wrapper, attr, value)
|
||||
for attr in updated:
|
||||
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
|
||||
# Return the wrapper so this can be used as a decorator via partial()
|
||||
return wrapper
|
||||
|
||||
def wraps(wrapped,
|
||||
assigned = WRAPPER_ASSIGNMENTS,
|
||||
updated = WRAPPER_UPDATES):
|
||||
"""Decorator factory to apply update_wrapper() to a wrapper function
|
||||
|
||||
Returns a decorator that invokes update_wrapper() with the decorated
|
||||
function as the wrapper argument and the arguments to wraps() as the
|
||||
remaining arguments. Default arguments are as for update_wrapper().
|
||||
This is a convenience function to simplify applying partial() to
|
||||
update_wrapper().
|
||||
"""
|
||||
return partial(update_wrapper, wrapped=wrapped,
|
||||
assigned=assigned, updated=updated)
|
||||
|
||||
def total_ordering(cls):
|
||||
"""Class decorator that fills in missing ordering methods"""
|
||||
convert = {
|
||||
'__lt__': [('__gt__', lambda self, other: not (self < other or self == other)),
|
||||
('__le__', lambda self, other: self < other or self == other),
|
||||
('__ge__', lambda self, other: not self < other)],
|
||||
'__le__': [('__ge__', lambda self, other: not self <= other or self == other),
|
||||
('__lt__', lambda self, other: self <= other and not self == other),
|
||||
('__gt__', lambda self, other: not self <= other)],
|
||||
'__gt__': [('__lt__', lambda self, other: not (self > other or self == other)),
|
||||
('__ge__', lambda self, other: self > other or self == other),
|
||||
('__le__', lambda self, other: not self > other)],
|
||||
'__ge__': [('__le__', lambda self, other: (not self >= other) or self == other),
|
||||
('__gt__', lambda self, other: self >= other and not self == other),
|
||||
('__lt__', lambda self, other: not self >= other)]
|
||||
}
|
||||
# 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: < > <= >=')
|
||||
root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
|
||||
for opname, opfunc in convert[root]:
|
||||
if opname not in roots:
|
||||
opfunc.__name__ = opname
|
||||
opfunc.__doc__ = getattr(int, opname).__doc__
|
||||
setattr(cls, opname, opfunc)
|
||||
return cls
|
||||
|
||||
def cmp_to_key(mycmp):
|
||||
"""Convert a cmp= function into a key= function"""
|
||||
class K(object):
|
||||
__slots__ = ['obj']
|
||||
def __init__(self, obj):
|
||||
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
|
||||
__hash__ = None
|
||||
return K
|
||||
|
||||
_CacheInfo = namedtuple("CacheInfo", "hits misses maxsize currsize")
|
||||
|
||||
def lru_cache(maxsize=100):
|
||||
"""Least-recently-used cache decorator.
|
||||
|
||||
If *maxsize* is set to None, the LRU features are disabled and the cache
|
||||
can grow without bound.
|
||||
|
||||
Arguments to the cached function must be hashable.
|
||||
|
||||
View the cache statistics named tuple (hits, misses, maxsize, currsize) with
|
||||
f.cache_info(). Clear the cache and statistics with f.cache_clear().
|
||||
Access the underlying function with f.__wrapped__.
|
||||
|
||||
See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
|
||||
|
||||
"""
|
||||
# Users should only access the lru_cache through its public API:
|
||||
# cache_info, cache_clear, and f.__wrapped__
|
||||
# The internals of the lru_cache are encapsulated for thread safety and
|
||||
# to allow the implementation to change (including a possible C version).
|
||||
|
||||
def decorating_function(user_function,
|
||||
tuple=tuple, sorted=sorted, len=len, KeyError=KeyError):
|
||||
|
||||
hits = misses = 0
|
||||
kwd_mark = (object(),) # separates positional and keyword args
|
||||
lock = Lock() # needed because OrderedDict isn't threadsafe
|
||||
|
||||
if maxsize is None:
|
||||
cache = dict() # simple cache without ordering or size limit
|
||||
|
||||
@wraps(user_function)
|
||||
def wrapper(*args, **kwds):
|
||||
nonlocal hits, misses
|
||||
key = args
|
||||
if kwds:
|
||||
key += kwd_mark + tuple(sorted(kwds.items()))
|
||||
try:
|
||||
result = cache[key]
|
||||
hits += 1
|
||||
return result
|
||||
except KeyError:
|
||||
pass
|
||||
result = user_function(*args, **kwds)
|
||||
cache[key] = result
|
||||
misses += 1
|
||||
return result
|
||||
else:
|
||||
cache = OrderedDict() # ordered least recent to most recent
|
||||
cache_popitem = cache.popitem
|
||||
cache_renew = cache.move_to_end
|
||||
|
||||
@wraps(user_function)
|
||||
def wrapper(*args, **kwds):
|
||||
nonlocal hits, misses
|
||||
key = args
|
||||
if kwds:
|
||||
key += kwd_mark + tuple(sorted(kwds.items()))
|
||||
with lock:
|
||||
try:
|
||||
result = cache[key]
|
||||
cache_renew(key) # record recent use of this key
|
||||
hits += 1
|
||||
return result
|
||||
except KeyError:
|
||||
pass
|
||||
result = user_function(*args, **kwds)
|
||||
with lock:
|
||||
cache[key] = result # record recent use of this key
|
||||
misses += 1
|
||||
if len(cache) > maxsize:
|
||||
cache_popitem(0) # purge least recently used cache entry
|
||||
return result
|
||||
|
||||
def cache_info():
|
||||
"""Report cache statistics"""
|
||||
with lock:
|
||||
return _CacheInfo(hits, misses, maxsize, len(cache))
|
||||
|
||||
def cache_clear():
|
||||
"""Clear the cache and cache statistics"""
|
||||
nonlocal hits, misses
|
||||
with lock:
|
||||
cache.clear()
|
||||
hits = misses = 0
|
||||
|
||||
wrapper.cache_info = cache_info
|
||||
wrapper.cache_clear = cache_clear
|
||||
return wrapper
|
||||
|
||||
return decorating_function
|
||||
6
setup.cfg
Normal file
6
setup.cfg
Normal file
@@ -0,0 +1,6 @@
|
||||
[sdist]
|
||||
formats=gztar,zip
|
||||
|
||||
[bdist_rpm]
|
||||
release = 1
|
||||
group = Development/Languages/Python
|
||||
31
setup.py
Executable file
31
setup.py
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
from distutils.core import setup
|
||||
|
||||
|
||||
def main():
|
||||
if not sys.version.startswith('2.'):
|
||||
sys.stderr.write('This backport is for Python 2.x only.\n')
|
||||
sys.exit(1)
|
||||
|
||||
setup(
|
||||
name='functools32',
|
||||
version='3.2.3',
|
||||
description='Backport of the functools module from Python 3.2.3 for use on 2.x.',
|
||||
long_description="""
|
||||
This is a backport of the functools standard library module from
|
||||
Python 3.2.3 for use on Python 2.4, 2.5, 2.6 and 2.7. It includes
|
||||
new features `lru_cache` (Least-recently-used cache decorator).""",
|
||||
license='PSF license',
|
||||
|
||||
maintainer='ENDOH takanao',
|
||||
maintainer_email='djmchl@gmail.com',
|
||||
url='https://github.com/MiCHiLU/python-functools32',
|
||||
|
||||
py_modules=['functools32'],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
9
test
Executable file
9
test
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This is for my own convenience, edit it for your own environment.
|
||||
|
||||
PYTHON=python
|
||||
$PYTHON setup.py build || exit 1
|
||||
|
||||
export PYTHONPATH=./build/lib.linux-x86_64-2.6
|
||||
exec $PYTHON test_functools32.py
|
||||
699
test_functools32.py
Normal file
699
test_functools32.py
Normal file
@@ -0,0 +1,699 @@
|
||||
import functools
|
||||
import collections
|
||||
import sys
|
||||
import unittest
|
||||
from test import support
|
||||
from weakref import proxy
|
||||
import pickle
|
||||
from random import choice
|
||||
|
||||
@staticmethod
|
||||
def PythonPartial(func, *args, **keywords):
|
||||
'Pure Python approximation of partial()'
|
||||
def newfunc(*fargs, **fkeywords):
|
||||
newkeywords = keywords.copy()
|
||||
newkeywords.update(fkeywords)
|
||||
return func(*(args + fargs), **newkeywords)
|
||||
newfunc.func = func
|
||||
newfunc.args = args
|
||||
newfunc.keywords = keywords
|
||||
return newfunc
|
||||
|
||||
def capture(*args, **kw):
|
||||
"""capture all positional and keyword arguments"""
|
||||
return args, kw
|
||||
|
||||
def signature(part):
|
||||
""" return the signature of a partial object """
|
||||
return (part.func, part.args, part.keywords, part.__dict__)
|
||||
|
||||
class TestPartial(unittest.TestCase):
|
||||
|
||||
thetype = functools.partial
|
||||
|
||||
def test_basic_examples(self):
|
||||
p = self.thetype(capture, 1, 2, a=10, b=20)
|
||||
self.assertEqual(p(3, 4, b=30, c=40),
|
||||
((1, 2, 3, 4), dict(a=10, b=30, c=40)))
|
||||
p = self.thetype(map, lambda x: x*10)
|
||||
self.assertEqual(list(p([1,2,3,4])), [10, 20, 30, 40])
|
||||
|
||||
def test_attributes(self):
|
||||
p = self.thetype(capture, 1, 2, a=10, b=20)
|
||||
# attributes should be readable
|
||||
self.assertEqual(p.func, capture)
|
||||
self.assertEqual(p.args, (1, 2))
|
||||
self.assertEqual(p.keywords, dict(a=10, b=20))
|
||||
# attributes should not be writable
|
||||
if not isinstance(self.thetype, type):
|
||||
return
|
||||
self.assertRaises(AttributeError, setattr, p, 'func', map)
|
||||
self.assertRaises(AttributeError, setattr, p, 'args', (1, 2))
|
||||
self.assertRaises(AttributeError, setattr, p, 'keywords', dict(a=1, b=2))
|
||||
|
||||
p = self.thetype(hex)
|
||||
try:
|
||||
del p.__dict__
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
self.fail('partial object allowed __dict__ to be deleted')
|
||||
|
||||
def test_argument_checking(self):
|
||||
self.assertRaises(TypeError, self.thetype) # need at least a func arg
|
||||
try:
|
||||
self.thetype(2)()
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
self.fail('First arg not checked for callability')
|
||||
|
||||
def test_protection_of_callers_dict_argument(self):
|
||||
# a caller's dictionary should not be altered by partial
|
||||
def func(a=10, b=20):
|
||||
return a
|
||||
d = {'a':3}
|
||||
p = self.thetype(func, a=5)
|
||||
self.assertEqual(p(**d), 3)
|
||||
self.assertEqual(d, {'a':3})
|
||||
p(b=7)
|
||||
self.assertEqual(d, {'a':3})
|
||||
|
||||
def test_arg_combinations(self):
|
||||
# exercise special code paths for zero args in either partial
|
||||
# object or the caller
|
||||
p = self.thetype(capture)
|
||||
self.assertEqual(p(), ((), {}))
|
||||
self.assertEqual(p(1,2), ((1,2), {}))
|
||||
p = self.thetype(capture, 1, 2)
|
||||
self.assertEqual(p(), ((1,2), {}))
|
||||
self.assertEqual(p(3,4), ((1,2,3,4), {}))
|
||||
|
||||
def test_kw_combinations(self):
|
||||
# exercise special code paths for no keyword args in
|
||||
# either the partial object or the caller
|
||||
p = self.thetype(capture)
|
||||
self.assertEqual(p(), ((), {}))
|
||||
self.assertEqual(p(a=1), ((), {'a':1}))
|
||||
p = self.thetype(capture, a=1)
|
||||
self.assertEqual(p(), ((), {'a':1}))
|
||||
self.assertEqual(p(b=2), ((), {'a':1, 'b':2}))
|
||||
# keyword args in the call override those in the partial object
|
||||
self.assertEqual(p(a=3, b=2), ((), {'a':3, 'b':2}))
|
||||
|
||||
def test_positional(self):
|
||||
# make sure positional arguments are captured correctly
|
||||
for args in [(), (0,), (0,1), (0,1,2), (0,1,2,3)]:
|
||||
p = self.thetype(capture, *args)
|
||||
expected = args + ('x',)
|
||||
got, empty = p('x')
|
||||
self.assertTrue(expected == got and empty == {})
|
||||
|
||||
def test_keyword(self):
|
||||
# make sure keyword arguments are captured correctly
|
||||
for a in ['a', 0, None, 3.5]:
|
||||
p = self.thetype(capture, a=a)
|
||||
expected = {'a':a,'x':None}
|
||||
empty, got = p(x=None)
|
||||
self.assertTrue(expected == got and empty == ())
|
||||
|
||||
def test_no_side_effects(self):
|
||||
# make sure there are no side effects that affect subsequent calls
|
||||
p = self.thetype(capture, 0, a=1)
|
||||
args1, kw1 = p(1, b=2)
|
||||
self.assertTrue(args1 == (0,1) and kw1 == {'a':1,'b':2})
|
||||
args2, kw2 = p()
|
||||
self.assertTrue(args2 == (0,) and kw2 == {'a':1})
|
||||
|
||||
def test_error_propagation(self):
|
||||
def f(x, y):
|
||||
x / y
|
||||
self.assertRaises(ZeroDivisionError, self.thetype(f, 1, 0))
|
||||
self.assertRaises(ZeroDivisionError, self.thetype(f, 1), 0)
|
||||
self.assertRaises(ZeroDivisionError, self.thetype(f), 1, 0)
|
||||
self.assertRaises(ZeroDivisionError, self.thetype(f, y=0), 1)
|
||||
|
||||
def test_weakref(self):
|
||||
f = self.thetype(int, base=16)
|
||||
p = proxy(f)
|
||||
self.assertEqual(f.func, p.func)
|
||||
f = None
|
||||
self.assertRaises(ReferenceError, getattr, p, 'func')
|
||||
|
||||
def test_with_bound_and_unbound_methods(self):
|
||||
data = list(map(str, range(10)))
|
||||
join = self.thetype(str.join, '')
|
||||
self.assertEqual(join(data), '0123456789')
|
||||
join = self.thetype(''.join)
|
||||
self.assertEqual(join(data), '0123456789')
|
||||
|
||||
def test_repr(self):
|
||||
args = (object(), object())
|
||||
args_repr = ', '.join(repr(a) for a in args)
|
||||
kwargs = {'a': object(), 'b': object()}
|
||||
kwargs_repr = ', '.join("%s=%r" % (k, v) for k, v in kwargs.items())
|
||||
if self.thetype is functools.partial:
|
||||
name = 'functools.partial'
|
||||
else:
|
||||
name = self.thetype.__name__
|
||||
|
||||
f = self.thetype(capture)
|
||||
self.assertEqual('{}({!r})'.format(name, capture),
|
||||
repr(f))
|
||||
|
||||
f = self.thetype(capture, *args)
|
||||
self.assertEqual('{}({!r}, {})'.format(name, capture, args_repr),
|
||||
repr(f))
|
||||
|
||||
f = self.thetype(capture, **kwargs)
|
||||
self.assertEqual('{}({!r}, {})'.format(name, capture, kwargs_repr),
|
||||
repr(f))
|
||||
|
||||
f = self.thetype(capture, *args, **kwargs)
|
||||
self.assertEqual('{}({!r}, {}, {})'.format(name, capture, args_repr, kwargs_repr),
|
||||
repr(f))
|
||||
|
||||
def test_pickle(self):
|
||||
f = self.thetype(signature, 'asdf', bar=True)
|
||||
f.add_something_to__dict__ = True
|
||||
f_copy = pickle.loads(pickle.dumps(f))
|
||||
self.assertEqual(signature(f), signature(f_copy))
|
||||
|
||||
class PartialSubclass(functools.partial):
|
||||
pass
|
||||
|
||||
class TestPartialSubclass(TestPartial):
|
||||
|
||||
thetype = PartialSubclass
|
||||
|
||||
class TestPythonPartial(TestPartial):
|
||||
|
||||
thetype = PythonPartial
|
||||
|
||||
# the python version hasn't a nice repr
|
||||
def test_repr(self): pass
|
||||
|
||||
# the python version isn't picklable
|
||||
def test_pickle(self): pass
|
||||
|
||||
class TestUpdateWrapper(unittest.TestCase):
|
||||
|
||||
def check_wrapper(self, wrapper, wrapped,
|
||||
assigned=functools.WRAPPER_ASSIGNMENTS,
|
||||
updated=functools.WRAPPER_UPDATES):
|
||||
# Check attributes were assigned
|
||||
for name in assigned:
|
||||
self.assertTrue(getattr(wrapper, name) is getattr(wrapped, name))
|
||||
# Check attributes were updated
|
||||
for name in updated:
|
||||
wrapper_attr = getattr(wrapper, name)
|
||||
wrapped_attr = getattr(wrapped, name)
|
||||
for key in wrapped_attr:
|
||||
self.assertTrue(wrapped_attr[key] is wrapper_attr[key])
|
||||
|
||||
def _default_update(self):
|
||||
def f(a:'This is a new annotation'):
|
||||
"""This is a test"""
|
||||
pass
|
||||
f.attr = 'This is also a test'
|
||||
def wrapper(b:'This is the prior annotation'):
|
||||
pass
|
||||
functools.update_wrapper(wrapper, f)
|
||||
return wrapper, f
|
||||
|
||||
def test_default_update(self):
|
||||
wrapper, f = self._default_update()
|
||||
self.check_wrapper(wrapper, f)
|
||||
self.assertIs(wrapper.__wrapped__, f)
|
||||
self.assertEqual(wrapper.__name__, 'f')
|
||||
self.assertEqual(wrapper.attr, 'This is also a test')
|
||||
self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation')
|
||||
self.assertNotIn('b', wrapper.__annotations__)
|
||||
|
||||
@unittest.skipIf(sys.flags.optimize >= 2,
|
||||
"Docstrings are omitted with -O2 and above")
|
||||
def test_default_update_doc(self):
|
||||
wrapper, f = self._default_update()
|
||||
self.assertEqual(wrapper.__doc__, 'This is a test')
|
||||
|
||||
def test_no_update(self):
|
||||
def f():
|
||||
"""This is a test"""
|
||||
pass
|
||||
f.attr = 'This is also a test'
|
||||
def wrapper():
|
||||
pass
|
||||
functools.update_wrapper(wrapper, f, (), ())
|
||||
self.check_wrapper(wrapper, f, (), ())
|
||||
self.assertEqual(wrapper.__name__, 'wrapper')
|
||||
self.assertEqual(wrapper.__doc__, None)
|
||||
self.assertEqual(wrapper.__annotations__, {})
|
||||
self.assertFalse(hasattr(wrapper, 'attr'))
|
||||
|
||||
def test_selective_update(self):
|
||||
def f():
|
||||
pass
|
||||
f.attr = 'This is a different test'
|
||||
f.dict_attr = dict(a=1, b=2, c=3)
|
||||
def wrapper():
|
||||
pass
|
||||
wrapper.dict_attr = {}
|
||||
assign = ('attr',)
|
||||
update = ('dict_attr',)
|
||||
functools.update_wrapper(wrapper, f, assign, update)
|
||||
self.check_wrapper(wrapper, f, assign, update)
|
||||
self.assertEqual(wrapper.__name__, 'wrapper')
|
||||
self.assertEqual(wrapper.__doc__, None)
|
||||
self.assertEqual(wrapper.attr, 'This is a different test')
|
||||
self.assertEqual(wrapper.dict_attr, f.dict_attr)
|
||||
|
||||
def test_missing_attributes(self):
|
||||
def f():
|
||||
pass
|
||||
def wrapper():
|
||||
pass
|
||||
wrapper.dict_attr = {}
|
||||
assign = ('attr',)
|
||||
update = ('dict_attr',)
|
||||
# Missing attributes on wrapped object are ignored
|
||||
functools.update_wrapper(wrapper, f, assign, update)
|
||||
self.assertNotIn('attr', wrapper.__dict__)
|
||||
self.assertEqual(wrapper.dict_attr, {})
|
||||
# Wrapper must have expected attributes for updating
|
||||
del wrapper.dict_attr
|
||||
with self.assertRaises(AttributeError):
|
||||
functools.update_wrapper(wrapper, f, assign, update)
|
||||
wrapper.dict_attr = 1
|
||||
with self.assertRaises(AttributeError):
|
||||
functools.update_wrapper(wrapper, f, assign, update)
|
||||
|
||||
@unittest.skipIf(sys.flags.optimize >= 2,
|
||||
"Docstrings are omitted with -O2 and above")
|
||||
def test_builtin_update(self):
|
||||
# Test for bug #1576241
|
||||
def wrapper():
|
||||
pass
|
||||
functools.update_wrapper(wrapper, max)
|
||||
self.assertEqual(wrapper.__name__, 'max')
|
||||
self.assertTrue(wrapper.__doc__.startswith('max('))
|
||||
self.assertEqual(wrapper.__annotations__, {})
|
||||
|
||||
class TestWraps(TestUpdateWrapper):
|
||||
|
||||
def _default_update(self):
|
||||
def f():
|
||||
"""This is a test"""
|
||||
pass
|
||||
f.attr = 'This is also a test'
|
||||
@functools.wraps(f)
|
||||
def wrapper():
|
||||
pass
|
||||
self.check_wrapper(wrapper, f)
|
||||
return wrapper
|
||||
|
||||
def test_default_update(self):
|
||||
wrapper = self._default_update()
|
||||
self.assertEqual(wrapper.__name__, 'f')
|
||||
self.assertEqual(wrapper.attr, 'This is also a test')
|
||||
|
||||
@unittest.skipIf(not sys.flags.optimize <= 1,
|
||||
"Docstrings are omitted with -O2 and above")
|
||||
def test_default_update_doc(self):
|
||||
wrapper = self._default_update()
|
||||
self.assertEqual(wrapper.__doc__, 'This is a test')
|
||||
|
||||
def test_no_update(self):
|
||||
def f():
|
||||
"""This is a test"""
|
||||
pass
|
||||
f.attr = 'This is also a test'
|
||||
@functools.wraps(f, (), ())
|
||||
def wrapper():
|
||||
pass
|
||||
self.check_wrapper(wrapper, f, (), ())
|
||||
self.assertEqual(wrapper.__name__, 'wrapper')
|
||||
self.assertEqual(wrapper.__doc__, None)
|
||||
self.assertFalse(hasattr(wrapper, 'attr'))
|
||||
|
||||
def test_selective_update(self):
|
||||
def f():
|
||||
pass
|
||||
f.attr = 'This is a different test'
|
||||
f.dict_attr = dict(a=1, b=2, c=3)
|
||||
def add_dict_attr(f):
|
||||
f.dict_attr = {}
|
||||
return f
|
||||
assign = ('attr',)
|
||||
update = ('dict_attr',)
|
||||
@functools.wraps(f, assign, update)
|
||||
@add_dict_attr
|
||||
def wrapper():
|
||||
pass
|
||||
self.check_wrapper(wrapper, f, assign, update)
|
||||
self.assertEqual(wrapper.__name__, 'wrapper')
|
||||
self.assertEqual(wrapper.__doc__, None)
|
||||
self.assertEqual(wrapper.attr, 'This is a different test')
|
||||
self.assertEqual(wrapper.dict_attr, f.dict_attr)
|
||||
|
||||
class TestReduce(unittest.TestCase):
|
||||
func = functools.reduce
|
||||
|
||||
def test_reduce(self):
|
||||
class Squares:
|
||||
def __init__(self, max):
|
||||
self.max = max
|
||||
self.sofar = []
|
||||
|
||||
def __len__(self):
|
||||
return len(self.sofar)
|
||||
|
||||
def __getitem__(self, i):
|
||||
if not 0 <= i < self.max: raise IndexError
|
||||
n = len(self.sofar)
|
||||
while n <= i:
|
||||
self.sofar.append(n*n)
|
||||
n += 1
|
||||
return self.sofar[i]
|
||||
def add(x, y):
|
||||
return x + y
|
||||
self.assertEqual(self.func(add, ['a', 'b', 'c'], ''), 'abc')
|
||||
self.assertEqual(
|
||||
self.func(add, [['a', 'c'], [], ['d', 'w']], []),
|
||||
['a','c','d','w']
|
||||
)
|
||||
self.assertEqual(self.func(lambda x, y: x*y, range(2,8), 1), 5040)
|
||||
self.assertEqual(
|
||||
self.func(lambda x, y: x*y, range(2,21), 1),
|
||||
2432902008176640000
|
||||
)
|
||||
self.assertEqual(self.func(add, Squares(10)), 285)
|
||||
self.assertEqual(self.func(add, Squares(10), 0), 285)
|
||||
self.assertEqual(self.func(add, Squares(0), 0), 0)
|
||||
self.assertRaises(TypeError, self.func)
|
||||
self.assertRaises(TypeError, self.func, 42, 42)
|
||||
self.assertRaises(TypeError, self.func, 42, 42, 42)
|
||||
self.assertEqual(self.func(42, "1"), "1") # func is never called with one item
|
||||
self.assertEqual(self.func(42, "", "1"), "1") # func is never called with one item
|
||||
self.assertRaises(TypeError, self.func, 42, (42, 42))
|
||||
self.assertRaises(TypeError, self.func, add, []) # arg 2 must not be empty sequence with no initial value
|
||||
self.assertRaises(TypeError, self.func, add, "")
|
||||
self.assertRaises(TypeError, self.func, add, ())
|
||||
self.assertRaises(TypeError, self.func, add, object())
|
||||
|
||||
class TestFailingIter:
|
||||
def __iter__(self):
|
||||
raise RuntimeError
|
||||
self.assertRaises(RuntimeError, self.func, add, TestFailingIter())
|
||||
|
||||
self.assertEqual(self.func(add, [], None), None)
|
||||
self.assertEqual(self.func(add, [], 42), 42)
|
||||
|
||||
class BadSeq:
|
||||
def __getitem__(self, index):
|
||||
raise ValueError
|
||||
self.assertRaises(ValueError, self.func, 42, BadSeq())
|
||||
|
||||
# Test reduce()'s use of iterators.
|
||||
def test_iterator_usage(self):
|
||||
class SequenceClass:
|
||||
def __init__(self, n):
|
||||
self.n = n
|
||||
def __getitem__(self, i):
|
||||
if 0 <= i < self.n:
|
||||
return i
|
||||
else:
|
||||
raise IndexError
|
||||
|
||||
from operator import add
|
||||
self.assertEqual(self.func(add, SequenceClass(5)), 10)
|
||||
self.assertEqual(self.func(add, SequenceClass(5), 42), 52)
|
||||
self.assertRaises(TypeError, self.func, add, SequenceClass(0))
|
||||
self.assertEqual(self.func(add, SequenceClass(0), 42), 42)
|
||||
self.assertEqual(self.func(add, SequenceClass(1)), 0)
|
||||
self.assertEqual(self.func(add, SequenceClass(1), 42), 42)
|
||||
|
||||
d = {"one": 1, "two": 2, "three": 3}
|
||||
self.assertEqual(self.func(add, d), "".join(d.keys()))
|
||||
|
||||
class TestCmpToKey(unittest.TestCase):
|
||||
def test_cmp_to_key(self):
|
||||
def mycmp(x, y):
|
||||
return y - x
|
||||
self.assertEqual(sorted(range(5), key=functools.cmp_to_key(mycmp)),
|
||||
[4, 3, 2, 1, 0])
|
||||
|
||||
def test_hash(self):
|
||||
def mycmp(x, y):
|
||||
return y - x
|
||||
key = functools.cmp_to_key(mycmp)
|
||||
k = key(10)
|
||||
self.assertRaises(TypeError, hash, k)
|
||||
self.assertFalse(isinstance(k, collections.Hashable))
|
||||
|
||||
class TestTotalOrdering(unittest.TestCase):
|
||||
|
||||
def test_total_ordering_lt(self):
|
||||
@functools.total_ordering
|
||||
class A:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __lt__(self, other):
|
||||
return self.value < other.value
|
||||
def __eq__(self, other):
|
||||
return self.value == other.value
|
||||
self.assertTrue(A(1) < A(2))
|
||||
self.assertTrue(A(2) > A(1))
|
||||
self.assertTrue(A(1) <= A(2))
|
||||
self.assertTrue(A(2) >= A(1))
|
||||
self.assertTrue(A(2) <= A(2))
|
||||
self.assertTrue(A(2) >= A(2))
|
||||
|
||||
def test_total_ordering_le(self):
|
||||
@functools.total_ordering
|
||||
class A:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __le__(self, other):
|
||||
return self.value <= other.value
|
||||
def __eq__(self, other):
|
||||
return self.value == other.value
|
||||
self.assertTrue(A(1) < A(2))
|
||||
self.assertTrue(A(2) > A(1))
|
||||
self.assertTrue(A(1) <= A(2))
|
||||
self.assertTrue(A(2) >= A(1))
|
||||
self.assertTrue(A(2) <= A(2))
|
||||
self.assertTrue(A(2) >= A(2))
|
||||
|
||||
def test_total_ordering_gt(self):
|
||||
@functools.total_ordering
|
||||
class A:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __gt__(self, other):
|
||||
return self.value > other.value
|
||||
def __eq__(self, other):
|
||||
return self.value == other.value
|
||||
self.assertTrue(A(1) < A(2))
|
||||
self.assertTrue(A(2) > A(1))
|
||||
self.assertTrue(A(1) <= A(2))
|
||||
self.assertTrue(A(2) >= A(1))
|
||||
self.assertTrue(A(2) <= A(2))
|
||||
self.assertTrue(A(2) >= A(2))
|
||||
|
||||
def test_total_ordering_ge(self):
|
||||
@functools.total_ordering
|
||||
class A:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __ge__(self, other):
|
||||
return self.value >= other.value
|
||||
def __eq__(self, other):
|
||||
return self.value == other.value
|
||||
self.assertTrue(A(1) < A(2))
|
||||
self.assertTrue(A(2) > A(1))
|
||||
self.assertTrue(A(1) <= A(2))
|
||||
self.assertTrue(A(2) >= A(1))
|
||||
self.assertTrue(A(2) <= A(2))
|
||||
self.assertTrue(A(2) >= A(2))
|
||||
|
||||
def test_total_ordering_no_overwrite(self):
|
||||
# new methods should not overwrite existing
|
||||
@functools.total_ordering
|
||||
class A(int):
|
||||
pass
|
||||
self.assertTrue(A(1) < A(2))
|
||||
self.assertTrue(A(2) > A(1))
|
||||
self.assertTrue(A(1) <= A(2))
|
||||
self.assertTrue(A(2) >= A(1))
|
||||
self.assertTrue(A(2) <= A(2))
|
||||
self.assertTrue(A(2) >= A(2))
|
||||
|
||||
def test_no_operations_defined(self):
|
||||
with self.assertRaises(ValueError):
|
||||
@functools.total_ordering
|
||||
class A:
|
||||
pass
|
||||
|
||||
def test_bug_10042(self):
|
||||
@functools.total_ordering
|
||||
class TestTO:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, TestTO):
|
||||
return self.value == other.value
|
||||
return False
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, TestTO):
|
||||
return self.value < other.value
|
||||
raise TypeError
|
||||
with self.assertRaises(TypeError):
|
||||
TestTO(8) <= ()
|
||||
|
||||
class TestLRU(unittest.TestCase):
|
||||
|
||||
def test_lru(self):
|
||||
def orig(x, y):
|
||||
return 3*x+y
|
||||
f = functools.lru_cache(maxsize=20)(orig)
|
||||
hits, misses, maxsize, currsize = f.cache_info()
|
||||
self.assertEqual(maxsize, 20)
|
||||
self.assertEqual(currsize, 0)
|
||||
self.assertEqual(hits, 0)
|
||||
self.assertEqual(misses, 0)
|
||||
|
||||
domain = range(5)
|
||||
for i in range(1000):
|
||||
x, y = choice(domain), choice(domain)
|
||||
actual = f(x, y)
|
||||
expected = orig(x, y)
|
||||
self.assertEqual(actual, expected)
|
||||
hits, misses, maxsize, currsize = f.cache_info()
|
||||
self.assertTrue(hits > misses)
|
||||
self.assertEqual(hits + misses, 1000)
|
||||
self.assertEqual(currsize, 20)
|
||||
|
||||
f.cache_clear() # test clearing
|
||||
hits, misses, maxsize, currsize = f.cache_info()
|
||||
self.assertEqual(hits, 0)
|
||||
self.assertEqual(misses, 0)
|
||||
self.assertEqual(currsize, 0)
|
||||
f(x, y)
|
||||
hits, misses, maxsize, currsize = f.cache_info()
|
||||
self.assertEqual(hits, 0)
|
||||
self.assertEqual(misses, 1)
|
||||
self.assertEqual(currsize, 1)
|
||||
|
||||
# Test bypassing the cache
|
||||
self.assertIs(f.__wrapped__, orig)
|
||||
f.__wrapped__(x, y)
|
||||
hits, misses, maxsize, currsize = f.cache_info()
|
||||
self.assertEqual(hits, 0)
|
||||
self.assertEqual(misses, 1)
|
||||
self.assertEqual(currsize, 1)
|
||||
|
||||
# test size zero (which means "never-cache")
|
||||
@functools.lru_cache(0)
|
||||
def f():
|
||||
nonlocal f_cnt
|
||||
f_cnt += 1
|
||||
return 20
|
||||
self.assertEqual(f.cache_info().maxsize, 0)
|
||||
f_cnt = 0
|
||||
for i in range(5):
|
||||
self.assertEqual(f(), 20)
|
||||
self.assertEqual(f_cnt, 5)
|
||||
hits, misses, maxsize, currsize = f.cache_info()
|
||||
self.assertEqual(hits, 0)
|
||||
self.assertEqual(misses, 5)
|
||||
self.assertEqual(currsize, 0)
|
||||
|
||||
# test size one
|
||||
@functools.lru_cache(1)
|
||||
def f():
|
||||
nonlocal f_cnt
|
||||
f_cnt += 1
|
||||
return 20
|
||||
self.assertEqual(f.cache_info().maxsize, 1)
|
||||
f_cnt = 0
|
||||
for i in range(5):
|
||||
self.assertEqual(f(), 20)
|
||||
self.assertEqual(f_cnt, 1)
|
||||
hits, misses, maxsize, currsize = f.cache_info()
|
||||
self.assertEqual(hits, 4)
|
||||
self.assertEqual(misses, 1)
|
||||
self.assertEqual(currsize, 1)
|
||||
|
||||
# test size two
|
||||
@functools.lru_cache(2)
|
||||
def f(x):
|
||||
nonlocal f_cnt
|
||||
f_cnt += 1
|
||||
return x*10
|
||||
self.assertEqual(f.cache_info().maxsize, 2)
|
||||
f_cnt = 0
|
||||
for x in 7, 9, 7, 9, 7, 9, 8, 8, 8, 9, 9, 9, 8, 8, 8, 7:
|
||||
# * * * *
|
||||
self.assertEqual(f(x), x*10)
|
||||
self.assertEqual(f_cnt, 4)
|
||||
hits, misses, maxsize, currsize = f.cache_info()
|
||||
self.assertEqual(hits, 12)
|
||||
self.assertEqual(misses, 4)
|
||||
self.assertEqual(currsize, 2)
|
||||
|
||||
def test_lru_with_maxsize_none(self):
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def fib(n):
|
||||
if n < 2:
|
||||
return n
|
||||
return fib(n-1) + fib(n-2)
|
||||
self.assertEqual([fib(n) for n in range(16)],
|
||||
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610])
|
||||
self.assertEqual(fib.cache_info(),
|
||||
functools._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16))
|
||||
fib.cache_clear()
|
||||
self.assertEqual(fib.cache_info(),
|
||||
functools._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
|
||||
|
||||
def test_lru_with_exceptions(self):
|
||||
# Verify that user_function exceptions get passed through without
|
||||
# creating a hard-to-read chained exception.
|
||||
# http://bugs.python.org/issue13177
|
||||
for maxsize in (None, 100):
|
||||
@functools.lru_cache(maxsize)
|
||||
def func(i):
|
||||
return 'abc'[i]
|
||||
self.assertEqual(func(0), 'a')
|
||||
with self.assertRaises(IndexError) as cm:
|
||||
func(15)
|
||||
self.assertIsNone(cm.exception.__context__)
|
||||
# Verify that the previous exception did not result in a cached entry
|
||||
with self.assertRaises(IndexError):
|
||||
func(15)
|
||||
|
||||
def test_main(verbose=None):
|
||||
test_classes = (
|
||||
TestPartial,
|
||||
TestPartialSubclass,
|
||||
TestPythonPartial,
|
||||
TestUpdateWrapper,
|
||||
TestTotalOrdering,
|
||||
TestCmpToKey,
|
||||
TestWraps,
|
||||
TestReduce,
|
||||
TestLRU,
|
||||
)
|
||||
support.run_unittest(*test_classes)
|
||||
|
||||
# verify reference counting
|
||||
if verbose and hasattr(sys, "gettotalrefcount"):
|
||||
import gc
|
||||
counts = [None] * 5
|
||||
for i in range(len(counts)):
|
||||
support.run_unittest(*test_classes)
|
||||
gc.collect()
|
||||
counts[i] = sys.gettotalrefcount()
|
||||
print(counts)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_main(verbose=True)
|
||||
Reference in New Issue
Block a user